moeTi
moeTi

Reputation: 3904

Convert values in a List of Maps with Streaming API

I have a list of maps with non distinctive keys and like to modify each map value with Streaming API. The result should be a new list, the original one has to remain unchanged.

Example data:

// [ {jpg=firstImage.jpg, png=firstImage.png}, {jpg=secondImage.jpg, png=secondImage.png} ]
Map<String, String> map1 = new HashMap<>();
map1.put("jpg", "firstImage.jpg");
map1.put("png", "firstImage.png");
Map<String, String> map2 = new HashMap<>();
map2.put("jpg", "secondImage.jpg");
map2.put("png", "secondImage.png");
List<Map<String, String>> list = new ArrayList<>();
list.add(map1);
list.add(map2);

Let's assume I want to convert the filename to upper case. With distinctive values I could do the following:

Map<String, String> copy = list.stream()
                .flatMap(e -> e.entrySet().stream())
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toUpperCase()));

But flatMap does not work when keys aren't distinctive, and it would destroy my List<Map<String, String>> structure. I know that the structure is bad, but I can't change it.

Expected outcome:

// [ {jpg=FIRSTIMAGE.JPG, png=FIRSTIMAGE.PNG}, {jpg=SECONDIMAGE.JPG, png=SECONDIMAGE.PNG} ]

I'd like to achieve this with Streaming API and avoid nested loops if possible.

Upvotes: 1

Views: 115

Answers (2)

Anonymous
Anonymous

Reputation: 86242

Here’s an option

    list.forEach(map -> map.replaceAll((t, f) -> f.toUpperCase()));

I am modifying the existing maps in your list. And it’s not using streams.

Edit: Thanks to VGR for the great simplification.

Edit: Since you need to leave your original structure unmodified, Eugene’s solution is fine. You may also apply replaceAll() on a copy. One example:

    List<Map<String, String>> result = new ArrayList<>(list);
    list.replaceAll(m -> {
                Map<String, String> newMap = new HashMap<>(m);
                newMap.replaceAll((t, f) -> f.toUpperCase());
                return newMap;
            });

Or yet a different approach, this one with a stream:

    List<Map<String, String>> result = list.stream()
            .map(m -> {
                Map<String, String> newMap = new HashMap<>(m);
                newMap.replaceAll((t, f) -> f.toUpperCase());
                return newMap;
            })
            .collect(Collectors.toList());

Which to prefer, I’d say it’s mostly a matter of taste.

Upvotes: 1

Eugene
Eugene

Reputation: 120848

You can't use flatMap, because this way you would concat all Entries in a single Stream and thus loose the possibility to get them back.

You need to convert each individual element from your List, like this for example:

  List<Map<String, String>> result = list.stream()
            .map(map -> map.entrySet().stream()
                          .collect(Collectors.toMap(
                                  Map.Entry::getKey, 
                                  e -> e.getValue().toUpperCase())))
            .collect(Collectors.toList());

    System.out.println(result);
    // [{jpg=FIRSTIMAGE.JPG, png=FIRSTIMAGE.PNG}, {jpg=SECONDIMAGE.JPG, png=SECONDIMAGE.PNG}]

Upvotes: 3

Related Questions