Reputation: 23
Following question:
I have a mapMap<A, List<Integer> map
and I want to convert it into a Map<A, Double>
, using streams. To convert it, the following technique is used:
You subtract each element in that list with the next one (the last one with the first one), group the positive results and the negative (0's will be ignored), get the average of both groups (as double) and return the absolute sum of both averages as double (that will be the key of the resulting map).
So, for instance, you have the map:
A = 30, 40, 50; //here the result would be (A = 30) because 30 - 40 = -10; 40 - 50 = -10; 50 - 30 = 20;
//that means you calculate average of (-10, -10), which is -10 and average of (20), which is 20
//at last you get the absolute sum of those two averages (10 + 20 = 30)
A1 = 40, 70, 100, 30; //resulting in (A1 = 93.33333) because average of (-10, -30, -30) is -23.333 and average of (70) is 70
A2 = 100, 100, 110, 120; //resulting in (A2 = 30) because average of (-10, 10) = -10 and average of (20) = 20.
//The zero here (100-100 = 0) will not be regarded
So at the end you get the map {A = 30, A1 = 83.33333, A2 = 30).
My problem right now is I don't know how I can calculate the differences between each element in the list and group them.
Upvotes: 0
Views: 189
Reputation: 40034
In doing this I believe I found out that one of your answers is incorrect. The sum should be 93.333333.
And when you said 0's are to be ignored, that means that if the difference is 0 then the number of items to average is NOT increased.
Here is the completed stream answer. The big caveate is that it requires Java 12+ to make use of Collectors.teeing()
Given the following map of values.
Map<String, List<Integer>> map = Map.of("A",
List.of(30, 40, 50),
"A1",
List.of(40, 70, 100, 30),
"A2",
List.of(100, 100, 110, 120));
The resultant map is computed here.
Map<String, Double> result =
map.entrySet().stream().collect(Collectors.toMap(Entry::getKey,
Everything starting here computes the double value of the final map entry.
e -> IntStream.range(0,
e.getValue().size()).mapToDouble(
n -> e.getValue().get(n % e.getValue().size())- e.getValue().get((n + 1)
% e.getValue().size()))
.boxed().collect(Collectors.teeing(Collectors.filtering((n -> n < 0),
Collectors.averagingDouble(a -> a)),
Collectors.filtering((n -> n > 0),
Collectors.averagingDouble(a -> a)),
(a, b) -> Math.abs(a)+ Math.abs(b)))));
System.out.println(result);
entrySet().stream
is used to get the original data form the source map.e.getValue()
is the list and used to get the size
of the list and the individual values via get()
differences
are sent to two different collectors via the teeing()
method. The first collector filters and averages values less than 0. The second one does the same for values greater than 0.Upvotes: 1
Reputation: 102872
streams can't reference concepts such as 'the last element' or 'the first element' within it. It is even difficult to refer to 'the previous element' or 'the next element', and yet that is precisely what the job of calculating the double is all about.
Thus, you can use streams for the first stage of this two-dimensional construct, but not for the second stage. You're going to go with something like:
map.entrySet().stream().collect(Collectors.toMap(
e -> e.getKey(),
e -> calculateVoodooValue(e.getValue()));
public double calculateVoodooValue(List<Integer> list) {
// don't use stream() here. use for (int i = 0; i < idx -1; i++) and the like.
}
Upvotes: 1