12

I have the following class.

class Person {

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        return birthday.until(IsoChronology.INSTANCE.dateNow()).getYears();
    }

    public String getName() {
        return name;
    }
}

I'd like to be able to group by age and then collect the list of the persons names rather than the Person object itself; all in a single nice lamba expression.

To simplify all of this I am linking my current solution that store the result of the grouping by age and then iterates over it to collect the names.

ArrayList<OtherPerson> members = new ArrayList<>();

members.add(new OtherPerson("Fred", IsoChronology.INSTANCE.date(1980, 6, 20), OtherPerson.Sex.MALE, "[email protected]"));
members.add(new OtherPerson("Jane", IsoChronology.INSTANCE.date(1990, 7, 15), OtherPerson.Sex.FEMALE, "[email protected]"));
members.add(new OtherPerson("Mark", IsoChronology.INSTANCE.date(1990, 7, 15), OtherPerson.Sex.MALE, "[email protected]"));
members.add(new OtherPerson("George", IsoChronology.INSTANCE.date(1991, 8, 13), OtherPerson.Sex.MALE, "[email protected]"));
members.add(new OtherPerson("Bob", IsoChronology.INSTANCE.date(2000, 9, 12), OtherPerson.Sex.MALE, "[email protected]"));

Map<Integer, List<Person>> collect = members.stream().collect(groupingBy(Person::getAge));

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

collect.keySet().forEach(key -> {
            result.put(key, collect.get(key).stream().map(Person::getName).collect(toList()));
});

Current solution

Not ideal and for the sake of learning I'd like to have a more elegant and performing solution.

0

3 Answers 3

17

When grouping a Stream with Collectors.groupingBy, you can specify a reduction operation on the values with a custom Collector. Here, we need to use Collectors.mapping, which takes a function (what the mapping is) and a collector (how to collect the mapped values). In this case the mapping is Person::getName, i.e. a method reference that returns the name of the Person, and we collect that into a List.

Map<Integer, List<String>> collect = 
    members.stream()
           .collect(Collectors.groupingBy(
               Person::getAge,
               Collectors.mapping(Person::getName, Collectors.toList()))
           );
Sign up to request clarification or add additional context in comments.

2 Comments

Ops, this was easy eh! Cheers!
In this case, only String is there. what to do if members have List<String> names. And, all remaining same. What modification is required inside Collectors.mapping method. Also if any method to modify the object before returning it as value part. For example, Name one I need to modify to do some custom String operation.
3

You can use a mapping Collector to map the list of Person to a list of person names :

Map<Integer, List<String>> collect = 
    members.stream()
           .collect(Collectors.groupingBy(Person::getAge,
                                          Collectors.mapping(Person::getName, Collectors.toList())));

1 Comment

In this case, only String is there. what to do if members have List<String> names. And, all remaining same. What modification is required inside Collectors.mapping method. Also if any method to modify the object before returning it as value part. For example, Name one I need to modify to do some custom String operation.
2

You can also use Collectors.toMap and provide mapping for key, value and merge function(if any).

Map<Integer, String> ageNameMap = 
    members.stream()
            .collect(Collectors.toMap(
              person -> person.getAge(), 
              person -> person.getName(), (pName1, pName2) -> pName1+"|"+pName2)
    );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.