Makky
Makky

Reputation: 17461

Filter and Sort internal Map java

I have class Person

 private String name;
    private int age;
    private Map<String, LocalDate> carsBoughWithDate;

You can ignore name and age. The important one here is carsBoughWithDate

Due to some reason I am saving person cars bough in a map with the date

Test Data

 Map<String, LocalDate> carsbought = new HashMap<>();
        carsbought.put("Toyota", LocalDate.of(2017, 2, 1));
        carsbought.put("Corolla", LocalDate.of(2017, 2, 1));

        Person john = new Person("John", 22, carsbought);


        carsbought = new HashMap<>();
        carsbought.put("Vauxhall", LocalDate.of(2017, 1, 1));
        carsbought.put("BMW", LocalDate.of(2017, 1, 1));
        carsbought.put("Toyota", LocalDate.of(2017, 1, 1));

        Person michael = new Person("Michael", 44, carsbought);

        List<Person> personList = new ArrayList<>();
        personList.add(john);
        personList.add(michael);

Output:

[Person{name='John', age=22, carsBoughWithDate={Toyota=2017-02-01, Corolla=2017-02-01}},

 Person{name='Michael', age=44, carsBoughWithDate={Vauxhall=2017-01-01, Toyota=2017-01-01, BMW=2017-01-01}}]

Now, I have to find out the person which has bought cars but then sort the person who bought the car earliest on the top in the list

Example: search person who has cars "Toyota" or BMW

This is what I have done

**

System.out.println("Before sort >" + personList);
        List<Person> sortedList = Lists.newArrayList();
        HashMap<LocalDate, Person> collect = Maps.newHashMap();
        for (Person person : personList) {
            Map<String, LocalDate> docCarsBoughWithDate = person.getCarsBoughWithDate();
            collect.putAll(docCarsBoughWithDate.entrySet().stream()
                    .filter(map -> Lists.newArrayList("Toyota", "BMW").contains(map.getKey()))
                    .collect(HashMap::new,
                            (m, v) -> m.put(
                                    v.getValue(),
                                    person),
                            HashMap::putAll
                    ));
        }
        Map<String, List<Person>> collect1 = collect.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(m -> m.getValue()).collect(Collectors.groupingBy(Person::getName));
        collect1.keySet().forEach(key -> sortedList.add(collect1.get(key).get(0)));
        System.out.println("after sort > " + sortedList
        );

This all works

Before sort >

[Person{name='John', age=22, carsBoughWithDate={Toyota=2017-02-01, Corolla=2017-02-01}}, Person{name='Michael', age=44, carsBoughWithDate={Vauxhall=2017-01-01, Toyota=2017-01-01, BMW=2017-01-01}}]

after sort >

[Person{name='Michael', age=44, carsBoughWithDate={Vauxhall=2017-01-01, Toyota=2017-01-01, BMW=2017-01-01}}, Person{name='John', age=22, carsBoughWithDate={Toyota=2017-02-01, Corolla=2017-02-01}}]

I feel this is bit cumbersome. Can I simplify the logic?

Upvotes: 1

Views: 63

Answers (2)

123-xyz
123-xyz

Reputation: 637

Here you go:

List<Person> sortedList = personList.stream() //
        .flatMap(p -> p.getCarsBoughWithDate().entrySet().stream() // 
                .filter(e -> targetCarNames.contains(e.getKey())) // filter the bought cars which are in the target bought cars.
                .sorted(Entry.comparingByValue()).limit(1) // sorted and only fetch the entry with earliest bought date.
                .map(e -> new SimpleEntry<>(p, e.getValue()))) // composite a new entry with the person and the earliest bought date. 
        .sorted(Entry.comparingByValue()).map(e -> e.getKey()).collect(toList()); //

Upvotes: 2

Hoopje
Hoopje

Reputation: 12932

First of all, are you sure that "this all works"? I tried your code with your test data with the following additional person:

carsbought = new HashMap<>();
carsbought.put("BMW", LocalDate.of(2017, 2, 1));
Person sally = new Person("Sally", 25, carsbought);

and she overwrote John because she happened to have bought a car at the same date.

Second, the strategy to solve complex problems is to break them down into simpler problems. For example, I would first add a method which determines the first date at which a person bought one of a set of cars:

private Optional<LocalDate> firstDateOf(Person person, Collection<String> cars)
{
    return person.getCarsBoughWithDate().entrySet().stream()
        .filter(e -> cars.contains(e.getKey()))
        .map(Map.Entry::getValue)
        .min(Comparator.naturalOrder());
}

This will be the sort key of the people. Then use this method to map each person to the sort key and finally sort the list:

List<Person> sortCarOwners(Collection<Person> people, Collection<String> cars)
{
    Map<Person, Optional<LocalDate>> personToDateMap = people.stream()
        .collect(Collectors.toMap(p -> p, p -> firstDateOf(p, cars)));
    return personToDateMap.entrySet().stream()
        .filter(e -> e.getValue().isPresent())
        .sorted(Comparator.comparing(e -> e.getValue().get()))
        .map(e -> e.getKey())
        .collect(Collectors.toList());
}

I don't know if you consider this "less cumbersome", but I hope it helps.

Upvotes: 1

Related Questions