Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum of values in the map for specific key

I have text file with a file that on every line contain pair of name and amount like this:

Mike 5
Kate 2
Mike 3

I need to sum these values by key. I resolved this in this way

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    try {
        Files.lines(Paths.get("/Users/walter/Desktop/stuff.txt"))
                .map(line -> line.split("\\s+")).forEach(line -> {
                    String key = line[0];
                    if (map.containsKey(key)) {
                        Integer oldValue = map.get(key);
                        map.put(key, oldValue + Integer.parseInt(line[1]));
                    } else {
                        map.put(line[0], Integer.parseInt(line[1]));
                    }
                });
        map.forEach((k,v) -> System.out.println(k + " " +v));

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    ;

}

How actually I could improve this code in more functional way, with abillity to process data more concurrently (using parallel streams, etc.)

like image 669
walter Avatar asked Jan 07 '23 05:01

walter


1 Answers

When you want to write functional code, the rule is: don't use forEach. This is an imperative solution and breaks functional code.

What you want is to split each line and group by the first part (key) while summing the second part (values):

Map<String, Integer> map = 
    Files.lines(Paths.get("/Users/walter/Desktop/stuff.txt"))
         .map(s -> s.split("\\s+"))
         .collect(groupingBy(a -> a[0], summingInt(a -> Integer.parseInt(a[1]))));

In this code, we are splitting each line. Then, we are grouping the Stream using Collectors.groupingBy(classifier, downstream) where:

  • classifier, which is a function that classifies each item to extract the key of the resulting Map, just returns the first part of the line
  • downstream is a collector that reduces each value having the same key: in this case, it is Collectors.summingInt(mapper) which sums each integer extracted by the given mapper.

As a side-note (and just so you know), you could rewrite your whole forEach more simply using the new Map.merge(key, value, remappingFunction) method, with just a call to:

map.merge(line[0], Integer.valueOf(line[1]), Integer::sum);

This will put a new value with the key line[0] with the value Integer.valueOf(line[1]) if this key did not exist, otherwise, it will update the key with the given remapping function (which is the sum of the old and new value in this case).

like image 68
Tunaki Avatar answered Jan 17 '23 18:01

Tunaki