Let's say we have a "measure" function with the signature:
def medida(x: A): Double
And we want to obtain the elements of a set that minimize the measure function. min
Applying the or methods minBy
I can obtain the first element that minimizes the function; but I'm interested in getting all the minimal elements.
One way to solve it would be to go through all the elements again to filter the ones with the minimum size, something like this:
val conjunto: Set[A] = ...
val minimo: Double = conjunto.map(medida).min
val resultado: Set[A] = conjunto.filter(medida(_) == minimo)
But you would be calculating the measure function twice for each element, which is something you want to avoid.
You could do a mapping to calculate the measure only once:
val conjunto: Set[A] = ...
val mapeo: Map[A, Double] = conjunto.map(x => x -> medida(x)).toMap
val minimo: Double = mapeo.values.min
val resultado: Set[A] = mapeo.keySet.filter(mapeo(_) == minimo)
Other than that it looks more complicated, I would loop through the entire array twice, which in my case is quite a large array.
I was thinking of solving the problem dynamically ( using state variables) or using some greedy algorithm . Without going so far, in the end I have come up with a solution that I will add as an answer.
The question: Is there a better way to get all the minimal elements of an array, that is both functional and efficient?
Edit 2016-11-29 (from @willena's answer
Application the idea of using foldLeft
:
val resultado: Set[A] = conjunto.foldLeft((Set[Double](), Double.PositiveInfinity) {
case ((conj, minimo), x) =>
val m = medida(x)
if (m < minimo) (Set(x), m)
else if (m == minimo) (conj + x, minimo)
else (conj, minimo)
}._1
You can loop through your set of values while accumulating the values you're interested in using the
foldLeft
. With this approach you get the following advantages:It would be something like this:
The result of
foldLeft
is(List(9, 3, 6, 6, 3, 9), 0)
.A possible improvement to the above code is to parameterize the compare function of the result and the initial result to compare with.
The solution I can think of is to group the elements of the array based on the measurement result:
I have not done tests to verify its efficiency.