vvye
vvye

Reputation: 1300

How can I merge multiple maps by the value of a certain key?

This is about Java, but for readability's sake, I'm going to write the examples down in JSON.

Say I have a List of Maps set up like this:

[{
    "id": 1,
    "foo": 12,
    "bar": 34
}, {
    "id": 1,
    "baz": 56
}, {
    "id": 2,
    "foo": 78
}, {
    "id": 2,
    "bar": 90
}]

What I'd like to do is merge maps that have the same id. Basically, I want to end up with something like this:

[{
    "id": 1,
    "foo": 12,
    "bar": 34,
    "baz": 56
}, {
    "id": 2,
    "foo": 78,
    "bar": 90
}]

All other questions I found deal with merging maps in unrelated ways, and most are only concerned about two maps, not a variable amount.

This code seems to work, but strikes me as a little verbose:

List<Map<String, Integer>> toRemove = new ArrayList<Map<String, Integer>>();

for (Map<String, Integer> map : list) {
    if (toRemove.contains(map)) {
        continue;
    }
    int id = map.get("id");
    for (Map<String, Integer> otherMap : list) {
        if (map.equals(otherMap)) {
            continue;
        }
        int otherId = otherMap.get("id");
        if (id == otherId) {
            map.putAll(otherMap);
            toRemove.add(otherMap);
        }
    }
}

list.removeAll(toRemove);

Is there a more elegant way to achieve this?

Upvotes: 1

Views: 1739

Answers (3)

Zefick
Zefick

Reputation: 2119

My сrazy solution with streams:

List<Map<String, Integer>> result = list.stream()
        .collect(Collectors.groupingBy(m -> m.get("id")))
        .values().stream()
        .map(m -> m.stream().<Map<String, Integer>>collect(HashMap::new, Map::putAll, Map::putAll))
        .collect(Collectors.toList());

Upvotes: 1

Antony Dao
Antony Dao

Reputation: 425

Just make a groupMap that is a new Map, with id is the key and element map is value:

    List<Map<String, Integer>> toRemove = new ArrayList<Map<String, Integer>>();
    Map<Integer, Map<String, Integer>> groupMap = new HashMap<>();

    for (Map<String, Integer> m : toRemove) {
        Integer id = m.get("id");
        Map<String, Integer> tmp = groupMap.get(id);
        if (tmp == null) {
            groupMap.put(id, m);
        } else {
            tmp.putAll(m);
        }
    }

    List<Map<String, Integer>> newList = new ArrayList<>(groupMap.values());

Then, the newList is your result now.

Upvotes: 2

fustaki
fustaki

Reputation: 1614

I would organize the result in a Map of Maps

Map<Integer, Map<String,Integer>> mapOfMaps = new HashMap<Integer, Map<String,Integer>>();

for(Map<String,Integer> map : list){
    Integer id = map.get("id");
    Map<String,Integer> existingMap = mapOfMaps.get(id);
    if(existingMap == null){
        mapOfMaps.put(id, map);
    }else{
        existingMap.putAll(map);
    }
}

As I commented above: this in the case you don't need to sum values (apart from id maps do not share other keys, or if they do values would be replaced)

Upvotes: 3

Related Questions