use stream to sum all values from array stored in map - java

I have a map which looks like this:
Map<String,String[]> urlFormEncoded = new HashMap<String,String[]>();
and I would like to sum all values stored in String[] arrays as a double value
I tried something like this:
double requestAmount = urlFormEncoded.entrySet().stream().mapToDouble(k -> Arrays.stream(k.getValue()).).sum();
but unfortunately I don't know how to convert this String[] to value :(
I would like to do this using streams and lambda expressions

The first step would be to convert each String[] as a DoubleStream.
Arrays.stream(arr).mapToDouble(Double::valueOf)
Then you have to flatMap those streams to get a single DoubleStream will all the double values.
.flatMapToDouble(arr -> Arrays.stream(arr).mapToDouble(Double::valueOf))
So you end up with:
double requestAmount =
urlFormEncoded.values()
.stream()
.flatMapToDouble(arr -> Arrays.stream(arr).mapToDouble(Double::valueOf))
.sum();
Note that you don't need to use the entrySet() if you plan to work only on the values, you can directly use values().

Related

How to collect two string streams into a Map in Java? [duplicate]

This question already has answers here:
How to create a map out of two arrays using streams in Java?
(3 answers)
Closed 1 year ago.
I have two Arrays as below:
k[] = {"K1","K2","K3"}
v[] = {"V1","V2","V3"}
I want to iterate these two arrays using Stream API in such a way so that I collect them as a Map as
[K1=V1,K2=V2,K3=V3]
Assuming both arrays have the same length, you could create a stream with the indexes. This can be done with IntStream.range(start,end) where the start is 0 and the end the size of your array. Because we use range, the end will not be included.
In this stream, you need to collect the result to a map, the key will be the value in first array with the given index, the value will be the value in the second array with the given index.
Do note that an IntStream is not the same as a Stream<Integer>. In this case, we will need a stream of Integers so that we can collect them in the Collector (Collector does not work with primitive types). To do this, call the method .boxed() to convert it to a Stream<Integer>
String k[] = {"K1", "K2", "K3"};
String v[] = {"V1", "V2", "V3"};
Map<String, String> result = IntStream.range(0, k.length).boxed().collect(Collectors.toMap(i -> k[i], i -> v[i]));
This gives the following result
{K1=V1, K2=V2, K3=V3}
IntStream.range(0, k.length).collect(Collectors.toMap(i -> k[i], i -> v[i]));

JavaPairRDD convert key-value into key-list

I have a JavaPairRDD containing (Key, Value) which I want to group by Keys and make the "second column" a list with all values seen for that key. I am currently using the groupby() function, which does the key aggrupation correctly but converts my values to an Iterable of Long. This is,
Key1 Iterable<Long>
Key2 Iterable<Long>
...
Is there any way to force this function to use a List of Longs instead of an Iterable object?
Key1 List<Long>
Key2 List<Long>
...
I read something about a function called combineByKey() but I think this is not a use case. Probably I need to use reduceByKey but I am not seeing it. It should be something like this:
myRDD.reduceByKey((a,b) -> new ArrayList<Long>()) //and add b to a
In the end, I want to combine values to obtain a Key n, List<Long> RDD.
Thank you for your time.
You can try something like this:
JavaPairRDD <String, List<long>> keyValuePairs = rdd.map(t -> {
return new Tuple2(t._1, Arrays.asList(new long[]{t._2}));
}).reduceByKey((a, b) -> {
a.addAll(b);
return a;
});
First, you map to convert the value into a list of longs. Then reduceByKey and combine the lists using addAll method on arraylist.

Stream of two Set flatMap to integers

final Set<Integer> set1 = new HashSet<>();
final Set<Integer> set2 = new HashSet<>();
Stream.of(set1, set2).mapToInt(???).forEach(intValue -> code)
I have 2 Set of Integer and in a Stream but i want to map them all into Integers. I can't find a way to do it using maptoInt or flatMap so i can extract all Integers of both Sets.
You can use Stream.concat(set1.stream(), set2.stream()) which creates a new combined stream so you can then map your values to int
Aside from stream concatenation mentioned at https://stackoverflow.com/a/65675352 you can also use
Stream.of(set1, set2)
.flatMap(set -> set.stream())
.mapToInt(i -> i)
.forEach(System.out::println);
since flatMap expects mapping to Stream of values which should be "used" by current stream.
So in your case you want stream containing values inside each set which can be created via
set -> set.stream()
or Set::stream.

Summing up BigDecimal in a map of list of objects in Java

I'm stuck with some elegant ways to get a summation of BigDecimals in a map. I know how to calculate the sum in a map of BigDecimal but not a List of object with a BigDecimal.
The structure of my objects are as below:
Class Obj {
private BigDecimal b;
// Getter for b, say getB()
}
Map<String, List<Obj>> myMap;
I need to get a sum of all bs in myMap. Looking for some elegant ways to do this in Java, may be using streams?
Stream the values of the Map.
Use flatMap to flatten the stream of lists of BigDecimals to a stream of BigDecimals.
Use map to extract the BigDecimals.
Use reduce with a summing operation.
BigDecimal sum = myMap.values().stream()
.flatMap(List::stream)
.map(Obj::getB)
.reduce(BigDecimal.ZERO, (a, b) -> a.add(b) );
The Stream.reduce method takes an identity value (for summing values, zero), and a BinaryOperator that adds intermediate results together.
You may also use a method reference in place of the lambda above: BigDecimal::add.
BigDecimal sum = myMap.values()
.stream()
.flatMap(Collection::stream)
.map(Obj::getB)
.reduce(BigDecimal.ZERO, BigDecimal::add);

How do I convert lambda parameters to usable objects?

I'm trying to stream a list of Doubles into a Map<Double, Double>, where the keys are the Doubles in the original list, and the values are some computed value.
This is what my code looks like:
// "values" is a List<Double> that was passed in
ImmutableMap<Double, Double> valueMap = values.parallelStream()
.collect(Collectors.toMap(p -> p, p -> doThing(values, p)));
private Double doThing(List<Double>, Double p) {
Double computedValue = 0.0;
// Do math here with p
return computedValue;
}
However, IntelliJ is complaining that p -> p is not a valid lambda expression - it's complaining about a cyclic inference. I'm also getting an error when I call doThing, because p is a lambda parameter and not a Double.
When I try to cast p to a Double in the call to doThing, that fails to cast.
Am I missing something really obvious? It seems like there's no way to actually do anything with my lambda parameters...
I think the problem here is simply that your collecting operation returns a Map<Double, Double> but the type of valueMap is ImmutableMap<Double, Double>.
You also forgot to give a name for the List<Double> parameter in doThing but I assume it was only a typo when writing the question.
As stated, the problem is that Collectors.toMap accumulates the element into Map<K,U> (the current implementation returns an HashMap but it's an internal detail).
If you want to collect into an ImmutableMap, you can use the collectingAndThen collector.
Use Collectors.toMap as the downstream collector and provide the function map -> ImmutableMap.copyOf(map) as a finisher.
ImmutableMap<Double, Double> valueMap = values.stream()
.collect(collectingAndThen(toMap(p -> p, p -> doThing(values, p)), ImmutableMap::copyOf));

Categories