2

I have a Linkedlist with Data ( author, date , LinkedList<Changes(lines, path)> )

now i want to create with a stream out of this a Map< Filepath, Map< Author, changes >>

public Map<String, Map<String, Integer>> authorFragmentation(List<Commit> commits) {

        return commits.stream()
                      .map(Commit::getChangesList)
                      .flatMap(changes -> changes.stream())
                      .collect(Collectors.toMap(
                              Changes::getPath,
                              Collectors.toMap(
                                 Commit::getAuthorName, 
                                 (changes) -> 1,
                                 (oldValue, newValue) -> oldValue + 1)));
}

I try it so but this doesnt work. How can i create this Map in a Map with the Stream and count at the same time the changes ?

Drextor
  • 433
  • 1
  • 6
  • 24
  • 1
    in your second `toMap`, your items are `Changes`, and not `Commit` anymore. – Jeremy Grand Jun 20 '17 at 11:15
  • 1
    Possible duplicate of [Java8: HashMap to HashMap using Stream / Map-Reduce / Collector](https://stackoverflow.com/questions/25903137/java8-hashmapx-y-to-hashmapx-z-using-stream-map-reduce-collector) – T-Bag Jun 20 '17 at 11:27

2 Answers2

4

Jeremy Grand is completely correct in his comment: in your collector it has long been forgotten that you started out from a stream of Commit objects, so you cannot use Commit::getAuthorName there. The challenge is how to keep the author name around to a place where you also got the path. One solution is to put both into a newly created string array (since both are strings).

public Map<String, Map<String, Long>> authorFragmentation(List<Commit> commits) {
    return commits.stream()
            .flatMap(c -> c.getChangesList()
                    .stream()
                    .map((Changes ch) -> new String[] { c.getAuthorName(), ch.getPath() }))
            .collect(Collectors.groupingBy(sa -> sa[1], 
                    Collectors.groupingBy(sa -> sa[0], Collectors.counting())));
}

Collectors.counting() insists on counting into a Long, not Integer, so I have modified your return type. I’m sure a conversion to Integer would be possible if necessary, but I would first consider whether I could live with Long.

It’s not the most beautiful stream code, and I will wait to see if other suggestions come up.

The code is compiled, but since I neither have your classes nor your data, I have not tried running it. If there are any issues, please revert.

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

Your mistake is that map/flatMap call "throws away" the Commit. You do not know which Commit a Change belongs to when trying to collect. In order to keep that information I'd recommend creating a small helper class (you could use a simple Pair, though):

public class OneChange
{
    private Commit commit;
    private Change change;

    public OneChange(Commit commit, Change change)
    {
        this.commit = commit;
        this.change = change;
    }

    public String getAuthorName() { return commit.getAuthorName(); };
    public String getPath()       { return change.getPath(); };
    public Integer getLines()     { return change.getLines(); };
}

You can then flatMap to that, group it by path and author, and then sum up the lines changed:

commits.stream()
       .flatMap(commit -> commit.getChanges().stream().map(change -> new OneChange(commit, change)))
       .collect(Collectors.groupingBy(OneChange::getPath,
                                      Collectors.groupingBy(OneChange::getAuthorName,
                                                            Collectors.summingInt(OneChange::getLines))));

In case you do not want to sum up the lines, but just count the Changes, replace Collectors.summingInt(OneChange::getLines) by Collectors.counting().

Malte Hartwig
  • 4,477
  • 2
  • 14
  • 30