1

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!

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
dejvid
  • 123
  • 1
  • 4
  • 16
  • 3
    What did you try? This is not a code writing service. – marstran Jan 17 '17 at 14:03
  • 1
    That looks a lot like Javascript, although that could just be pseudo code. :) However, what would the maps look like in actual Java code? I'd assume `id` is the key, but what are `attrib1` and `attrib2` in the first list? Attributes of the value object? Elements in a nested collection? – Thomas Jan 17 '17 at 14:04
  • You can do collections concatenation in java 8. Refer [this](http://stackoverflow.com/questions/4896662/combine-multiple-collections-into-a-single-logical-collection/23998250#23998250). Try it for your problem. – Rohit Gulati Jan 17 '17 at 14:08
  • @Thomas sorry, my I didn't say that this structure represents list of Maps, id, attr1, attr2, attr3 and attr4 are keys. – dejvid Jan 17 '17 at 14:10
  • Does that mean the key type of your maps is `String`? The value type, is it `Integer`, `Character`, `Object` or what? Also, are the lists required to hold maps with the same `id` values (1, 2 and 3 in your example)? What if there’s an “extra” map in one of the lists? Do they come in the same order? – Ole V.V. Jan 18 '17 at 10:05

3 Answers3

4
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());
rostIvan
  • 264
  • 1
  • 8
1

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.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
1

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);
    }
}
Ayhan APAYDIN
  • 850
  • 5
  • 7