0

I've a value object whose structure is like:

class MyValueObject {
    String travellerName;
    List<String> countryTags;

    // Getters and setters
}

In this I'm maintaining a list of traveller and which countries they've visited. e.g.

Richard -> India, Poland, Australia

John -> US, Australia, Maldives

Emma -> US

Now I'm adding a filter feature, which will give list of all travellers who've visited selected countries.

List of countries will be provided as an input.

List<String> countryFilter;

This filter has a multiselect option.

The filtered result should contain both AND & OR results.

That is, if input is US & Australia, result will be:

John -> US, Australia, Maldives

Richard -> India, Poland, Australia

Emma -> US

The result should be sorted in manner that AND matches should be shown above OR matches.

Question:

How should I sort the matches?

Shall I write a comparator? If yes, some example will be very helpful.

Please suggest.

Thanks.

3
  • Does this answer your question? How to sort List of objects by some property Commented Apr 26, 2020 at 15:49
  • @dawis11, it is sorting on multiple fields whereas I want to sort most matches within given input list. Commented Apr 26, 2020 at 15:54
  • 1
    Note: countryTags should be a Set. Commented Apr 26, 2020 at 18:52

3 Answers 3

1

Try this:

Building the objects.

List<Traveler> travelers = new ArrayList<>();
Traveler t1 = new Traveler();
t1.traveler = "Richard";
t1.countries = List.of("India", "Poland", "Australia");     
travelers.add(t1);

t1 = new Traveler();
t1.traveler = "John";
t1.countries = List.of("US", "Australia", "Maldives");
travelers.add(t1);


t1 = new Traveler();        
t1.traveler = "Emma";       
t1.countries = List.of("US");       
travelers.add(t1);

The filter

List<String> countryFilter = List.of("US", "Maldives");

The test predicates. These are the heart of the matter.

  • they both stream countryFilter and check to see if those
    countries are in the list of traveled countries.
  • or returns true if the travelers countries contain at least one country
  • and returns true if the traveler's countries contain all the countries.
Predicate<Traveler> or = t -> countryFilter.stream()
                .anyMatch(cc -> t.countries.contains(cc));

Predicate<Traveler> and = t -> countryFilter.stream()
                .allMatch(cc -> t.countries.contains(cc));

Now just stream just stream the traveler objects
and apply the filters. Then prints the results.

System.out.println("Filtering on: " + countryFilter);
System.out.println("\nVisited all of the countries");
travelers.stream().filter(and)
    .forEach(t -> System.out.println(t.traveler));
System.out.println("\nVisited some of the countries");
travelers.stream().filter(or)
    .forEach(t -> System.out.println(t.traveler));

Prints

Filtering on: [US, Maldives]

Visited all of the countries
John

Visited some of the countries
John
Emma

The class supporting class

class Traveler {
    String traveler;
    List<String> countries;

    public String toString() {
        return traveler + " => " + countries.toString();
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, but what about cases in between. Say filter has 3 values - US, India, Maldives. Will it handle cases like all 3 match, any 2 match & only one match?
No.. It was AND and OR which implies all match to be true or any match to be true. Sorry if it doesn't work for you.
One thing you can do is construct other Predicates. And they can be chained and negated. So you could do something like (and.negate().or and so forth. You can even create others. You may want to read up on the Predicate interface to see the options.
0

Instead of sorting, iterate twice, once for the AND matches and once for the OR matches:

// Say you have a list of MyValueObject type called travelers
ArrayList<MyValueObject> copyTravelers = new ArrayList<>(travelers);
List<MyValueObject> filtered = new ArrayList<>();

// AND
for (MyValueObject t : copyTravelers) {
    MyValueObject traveler = t;
    for (String country : countryFilter)
        if (!traveler.countryTags.contains(country)) {
            traveler = null;
            break;
        }
    if (traveler != null) {
        filtered.add(traveler);
        copyTravelers.remove(traveler);
    }
}

// OR
for (MyValueObject t : copyTravelers) {
    for (String country : countryFilter)
        if (traveler.countryTags.contains(country)) {
            filtered.add(t);
            copyTravelers.remove(t);
            break;
        }
}

Comments

0

i tried an approach where you get an ordered Map of country matches:

public Map<Long, String> sortMatches(List<MyValueObject> travellers, List<String> countries){

        TreeMap<Long, List> collect = travellers.stream()
                .flatMap(t -> t.countries.stream()
                        .filter(c -> countries.contains(c))
                        .map(c -> t.getTravellerName())
                        .collect(groupingBy(Function.identity(), counting()))
                .entrySet()
                .stream()
                ).collect(groupingBy(
                        e -> e.getValue(),
                        TreeMap::new,
                        listCollector
                ));
        return collect;
}

first the matches are counted and written to a temporary Map<String, Long> where the String is the travellerName and as value there is the match count of this traveller.

in a second step a new map is created which stores as key the amount of matches and as values the travellerNames of the travellers who have visited so many countries. therefore, the listCollector is used:

Collector<Map.Entry, List, List> listCollector =
    Collector.of(ArrayList::new,
                (l, e) -> l.add(e.getKey()), 
                (l1, l2) -> { l1.addAll(l2); return l1;});

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.