Сортировать список объектов по списку заданных входных значений - PullRequest
0 голосов
/ 26 апреля 2020

У меня есть объект-значение, структура которого выглядит следующим образом:

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

    // Getters and setters
}

В этом я веду список путешественников и стран, которые они посетили. например,

Ричард -> Индия, Польша, Австралия

Джон -> США, Австралия, Мальдивы

Эмма -> США

Теперь я добавляю функцию фильтра, которая выдаст список всех путешественников, посетивших выбранные страны.

В качестве входных данных будет предоставлен список стран.

List<String> countryFilter;

Это фильтр имеет опцию множественного выбора.

Отфильтрованный результат должен содержать как AND, так и OR результаты.

То есть, если входные данные - США и Австралия, результат будет:

Джон -> США, Австралия, Мальдивы

Ричард -> Индия, Польша, Австралия

Эмма -> США

Результат должен быть отсортирован по порядку совпадения AND должны быть показаны выше совпадений OR.

Вопрос:

Как отсортировать совпадения?

Должен ли я написать компаратор? Если да, какой-то пример будет очень полезным.

Пожалуйста, предложите.

Спасибо.

Ответы [ 3 ]

1 голос
/ 26 апреля 2020

Попробуйте:

Построение объектов.

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);

Фильтр

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

Тестовые предикаты. Это суть дела.

  • они оба передают countryFilter и проверяют, есть ли эти
    страны в списке пройденных стран.
  • or возвращает значение true, если страны путешественников содержат хотя бы одну страну
  • and возвращает значение true, если страны путешественников содержат все страны. объекты
    и применить фильтры. Затем печатает результаты.
    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));
    

    Печать

    Filtering on: [US, Maldives]
    
    Visited all of the countries
    John
    
    Visited some of the countries
    John
    Emma
    
    

    Класс поддержки класса

    class Traveler {
        String traveler;
        List<String> countries;
    
        public String toString() {
            return traveler + " => " + countries.toString();
        }
    }
    
0 голосов
/ 27 апреля 2020

Я попробовал подход, при котором вы получаете упорядоченную Карту совпадений стран:

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;
}

сначала совпадения подсчитываются и записываются во временную Map<String, Long>, где String - это travellerName и в качестве значения указывается количество совпадений этого путешественника.

на втором шаге создается новая карта, в которой в качестве ключа хранится количество совпадений и в качестве значений travellerNames путешественников, посетивших так много стран. следовательно, listCollector используется:

Collector<Map.Entry, List, List> listCollector =
    Collector.of(ArrayList::new,
                (l, e) -> l.add(e.getKey()), 
                (l1, l2) -> { l1.addAll(l2); return l1;});
0 голосов
/ 26 апреля 2020

Вместо сортировки выполните итерацию дважды, один раз для совпадений И и один раз для совпадений ИЛИ:

// 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;
        }
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...