Reputation: 123
Is there any way in Java8 using Streams API to join two lists of maps? Let's say I do have:
List #1
[
{
id=1, attr1='a', attr2='b'
},
{
id=2, attr1='c', attr2='d'
},
{
id=3, attr1='e', attr2='f'
}
]
List #2
[
{
id=1, attr3='x', attr4='y'
},
{
id=2, attr3='x', attr4='z'
},
{
id=3, attr3='z', attr4='y'
}
]
Now I would like to join these 2 lists by key ID (just list an SQL join), the output would be:
[
{
id=1, attr1='a', attr2='b', attr3='x', attr4='y'
},
{
id=2, attr1='c', attr2='d', attr3='x', attr4='z'
},
{
id=3, attr1='e', attr2='f', attr3='z', attr4='y'
}
]
Thanks a lot!
Upvotes: 1
Views: 3400
Reputation: 294
final List<HashMap<String, String>> joinedById = list1.stream()
.flatMap(m1 -> list2.stream()
.filter(y -> m1.get("id").equals(y.get("id")))
.map(m2 -> new HashMap<String, String>() {{
putAll(m1);
putAll(m2);
}}))
.collect(Collectors.toList());
Upvotes: 4
Reputation: 1648
Your data structure of using id key in map; kind of makes this a bit unusual but here is an example with stream collector. Since your map values are both int
and String
I used Map<String, Object>
import java.util.*;
public class JoinTwoListofMaps {
public static void main(String... args){
List<Map<String, Object>> list1 = new ArrayList<>();
List<Map<String, Object>> list2 = new ArrayList<>();
Map<String, Object> map1_1 = new HashMap<>();
map1_1.put("id",1);
map1_1.put("attr1","a");
map1_1.put("attr2","b");
list1.add(map1_1);
Map<String, Object> map2_1 = new HashMap<>();
map2_1.put("id",1);
map2_1.put("attr3","x");
map2_1.put("attr4","y");
list2.add(map2_1);
System.out.println(joinTwoListOfMaps(list1, list2));
}
@SafeVarargs
public static List<Map<String, Object>> joinTwoListOfMaps(List<Map<String, Object>>... listsOfMaps){
List<Map<String, Object>> finalList = Arrays.stream(listsOfMaps).collect(ArrayList::new, List::addAll, List::addAll);
return finalList.stream().collect(ArrayList::new, (list, mapToMerge) ->{
Optional<Map<String, Object>> mapWithSameID = list.stream()
.filter(map -> map.get("id").equals(mapToMerge.get("id"))).findFirst();
if (mapWithSameID.isPresent()) mapWithSameID.get().putAll(mapToMerge);
else list.add(mapToMerge);
}, ArrayList::addAll);
}
}
Upvotes: 1
Reputation: 86276
There are lots of ways, with and without streams. The best way would depend on the more exact input requirements, which you haven’t given us. The following would work for the example lists in your question:
if (list1.size() != list2.size()) {
throw new IllegalStateException("Lists don’t match, not same size");
}
List<Map<String, Character>> comnbinedList = IntStream.range(0, list1.size())
.mapToObj(i -> {
Map<String, Character> m1 = list1.get(i);
Character id1 = m1.get("id");
Map<String, Character> m2 = list2.get(i);
Character id2 = m1.get("id");
if (! id1.equals(id2)) {
throw new IllegalStateException("Lists don’t match, id " + id1 + " != " + id2);
}
HashMap<String, Character> mergedMap = new HashMap<>(m1);
mergedMap.putAll(m2);
return mergedMap;
})
.collect(Collectors.toList());
The result is:
[{id=1, attr2=b, attr1=a, attr4=y, attr3=x}, {id=2, attr2=d, attr1=c, attr4=z, attr3=x}, {id=3, attr2=f, attr1=e, attr4=y, attr3=z}]
You will want to declare "id"
a constant, of course.
Upvotes: 1