Reputation: 491
I have a list of maps as below.
And I want to group the entry sets by key, if a key non exist, fill 0 instead.
List<Map<String, Double>> props = Lists.newArrayList();
Map<String, Double> m1 = Maps.newHashMap();
m1.put("a", 0.1);
m1.put("b", 0.5);
m1.put("c", 0.6);
Map<String, Double> m2 = Maps.newHashMap();
m2.put("a", 0.3);
m2.put("d", 0.1);
Map<String, Double> m3 = Maps.newHashMap();
m3.put("a", 0.2);
props.add(m1); props.add(m2); props.add(m3);
Expected result:
{a=[0.1, 0.3, 0.2], b=[0.5, 0, 0], c=[0.6,0,0], d=[0,0.1,0]}
I got an idea:
Any good ideas?
Upvotes: 4
Views: 9331
Reputation: 4071
It can be done in single line lamda and stream except the case fill every map of missing keys with value 0
Map<String, List<Double>> collect = Stream.of(m1, m2, m3)
.flatMap(map -> map.entrySet().stream())
.collect(groupingBy(
Map.Entry::getKey,
mapping(Map.Entry::getValue, toList()))
);
System.out.println(collect);
And this will produce the output as
{a=[0.1, 0.3, 0.2], b=[0.5], c=[0.6], d=[0.1]}
Here static factory methods Collectors.groupingBy()
is used to group the concatenated Entries of all maps by the Key (classifier
) and mapping method (collector
).
If its the obvious case you must to fill every map of missing keys with value 0 then there is several way of doing that. One that @abhi stated in other answer. Along with his idea I would like to do that this way
Set<String> allKeys = Sets.newHashSet();
props.forEach(prop -> allKeys.addAll(prop.keySet()));
allKeys.forEach(k -> props.forEach(m -> m.putIfAbsent(k, 0.0)));
Infact this will modify your original maps with allKeys
having 0
as value ifNotPresent
. If this modification causes you problem you can prefer to copy your maps to have separate collections.
This will ensure your desired output
{a=[0.1, 0.3, 0.2], b=[0.5, 0.0, 0.0], c=[0.6, 0.0, 0.0], d=[0.0, 0.1, 0.0]}
Note: You can find this article interesting about Grouping using Java 8 streams
Upvotes: 6
Reputation: 4792
java 8 surely does reduce the number of lines, but not to one line of code in your case
Set<String> keys = Sets.newHashSet();
props.forEach(prop -> keys.addAll(prop.keySet()));
Map<String, List<Double>> result = Maps.newHashMap();
keys.forEach(key -> result.put(key, new ArrayList<>(Collections.nCopies(props.size(), 0D))));
props.forEach(prop -> prop.forEach((key, value) -> result.get(key).set(props.indexOf(prop), value)));
System.out.println(result);
Upvotes: 1
Reputation: 1197
The code is not very efficient, but it gets the job done.
List<Map<String, Double>> props = new ArrayList<>();
Map<String, Double> m1 = new HashMap<>();
m1.put("a", 0.1);
m1.put("b", 0.5);
m1.put("c", 0.6);
Map<String, Double> m2 = new HashMap<>();
m2.put("a", 0.3);
m2.put("d", 0.1);
Map<String, Double> m3 = new HashMap<>();
m3.put("a", 0.2);
props.add(m1);
props.add(m2);
props.add(m3);
Set<String> allKeys = new HashSet<>();
for (Map<String, Double> prop : props) {
allKeys.addAll(prop.keySet());
}
HashMap<String, ArrayList<Double>> result = new HashMap<>();
for (Map<String, Double> prop : props) {
for (String key : allKeys) {
if(!result.containsKey(key)){
result.put(key, new ArrayList<>());
}
if(!prop.containsKey(key)){
prop.put(key, 0.0);
}
Double propValue = prop.get(key);
result.get(key).add(propValue);
}
}
System.out.println(result.toString());
You basically want to use a Set<String>
then iterate through all the maps to get their keys
, then when you have all the keys you need to iterate through all the maps, check if the key exists, if it doesn't then add it to the map and now you can add it to the HashMap<String, ArrayList<Double>>
because you know that the key exists for this map
Upvotes: 1