Vikas
Vikas

Reputation: 7165

Merge nested Maps with specified key

I have a Map as below,

Map<String, Map<String, String>> dateFormatsMap;

Map<String,String> m1 = Map.of("A","DD", "B", "MM", "C","YY");
Map<String,String> m2 = Map.of("A","DD-MM", "X", "MM", "C","YYYY");
Map<String,String> m3 = Map.of("X","DD", "Y", "MM", "C","YY");

dateFormatsMap = Map.of("P2",m2,"P1",m1, "p3",m3);

This map is populated from DB and has project-wise data.

What I want is to get merged Map of a specific project and one default project and in case of a duplicate, I want to keep values of a specified project.

I tried below code,

private static Map<String, String> getFormats(String project) {
    Set<String> projects = Set.of("P1", project);   //e.g "P1" as default project
    return dateFormatsMap.entrySet().stream()
            .filter(e->projects.contains(e.getKey()))
            .flatMap(e->e.getValue().entrySet().stream())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (o,n)->o));
} 

I call this method as,

getFormats("P2");

But this gives the varying results having values sometimes from "P1" and sometimes from "P1".

{A=DD, B=MM, C=YY, X=MM}

{A=DD-MM, B=MM, C=YYYY, X=MM}

I need to have a fixed result as,

{A=DD-MM, B=MM, C=YYYY, X=MM}

You can check ideone

I hope, I have detailed the question. I appreciate any help.

Upvotes: 1

Views: 172

Answers (4)

Naman
Naman

Reputation: 31858

One way to deal with this without using Streams, you could also seek is using putAll -

private static Map<String, String> getFormats(String project) {
    Map<String, String> baseProjFormats = dateFormatsMap.get("P1");
    Map<String, String> currentProjFormats = dateFormatsMap.getOrDefault(project, Collections.emptyMap());
    baseProjFormats.putAll(currentProjFormats);
    return baseProjFormats;
}

Upvotes: 1

cegredev
cegredev

Reputation: 1579

I'm just going to be the guy who doesn't use streams:

public static void main(String[] args) {
    Map<String, String> defaultMap = Map.of("A", "DD", "B", "MM", "C", "YY");
    Map<String, String> mapToMerge = Map.of("A", "DD-MM", "X", "MM", "C", "YYYY");

    // Do the magic
    Map<String, String> result = new HashMap<String, String>();
    result.putAll(mapToMerge);

    for (Map.Entry<String, String> entry : defaultMap.entrySet())
        if (!result.containsKey(entry.getKey()))
            result.put(entry.getKey(), entry.getValue());

    // Print results
    result.entrySet().stream().forEach(System.out::println);
}

Upvotes: 0

pero_hero
pero_hero

Reputation: 3184

I added all from the default project which are not in the specific project map and afterwards added everything from the specific project map:

 private static Map<String, String> getMyFormats(String project) {
    String defaultProject = "P1";
    Map<String, String> specificMap = dateFormatsMap.get(project);

    Map<String, String> collect = dateFormatsMap.get(defaultProject).entrySet().stream()
               .filter(e -> !specificMap.containsKey(e.getKey()))
               .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));

    collect.putAll(specificMap);
    return collect;
 }

or using collectingAndThen:

private static Map<String, String> getFormats(String project) {
    String defaultProject = "P1";
    Map<String, String> collect = dateFormatsMap.get(defaultProject).entrySet().stream()
                .filter(e -> !dateFormatsMap.get(project).containsKey(e.getKey()))
                .collect(collectingAndThen(
                        toMap(Map.Entry::getKey, Map.Entry::getValue),
                        map -> {
                            map.putAll(dateFormatsMap.get(project));
                            return map;
                        })
                );    
   return collect;
}

Upvotes: 1

Vikas
Vikas

Reputation: 7165

I got the solution. Created two maps and then merged as below,

 private static Map<String, String> getFormats(String project) {
    Map<String, String> baseProjFormats = dateFormatsMap.get("P1");
    Map<String, String> currentProjFormats = dateFormatsMap.get(project);

    return Stream.of(baseProjFormats, currentProjFormats)
            .flatMap(e -> e.entrySet().stream())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (o, n) -> n));
 }

I would welcome any better solution.

Upvotes: 0

Related Questions