Java8 stream filtrowanie zagniezdzonych list

1

Hej,
Mam taki obiekt:

obj {
    //...some properties.... 
    List<> list1 {
       //...some properties....
      List<LocalDate> dates;
    },
    List<> list2{
       //...some properties....
      List<LocalDate> dates;
    },
    List<> list3{
       //...some properties....
      List<LocalDate> dates;
    },
    List<> list4{
       //...some properties....
      List<LocalDate> dates;
    },
}

Jak w ladny sposob moge znalezc najwieksza date z kazdej z list dates, a pozniej najmniejsza z tych znalezionych?

0

Coś w stylu

val maxDate1 = list1.stream()
    .flatMap(l -> l.getDates())
    .max(LocalDate::compareTo)
    .get();

Powtórzyć dla reszty list, zebrać do nowej listy i zrobić to samo, tylko z funkcją min?
Ewentualnie przerobić ten obiekt na zwyczajną mapę i pracować po ludzku

0
caer napisał(a):

Coś w stylu

val maxDate1 = list1.stream()
    .flatMap(l -> l.getDates())
    .max(LocalDate::compareTo)
    .get();

Powtórzyć dla reszty list, zebrać do nowej listy i zrobić to samo, tylko z funkcją min?
Ewentualnie przerobić ten obiekt na zwyczajną mapę i pracować po ludzku

Filtrowanie pojedynczej listy nie jest najmniejszym problemem. Chodzi mi o filtrowanie zagniezdzonych list z obiektu, ktory zawiera kilka list.

0

No to tak jak mówię, albo zastosować to dla każdej z zagnieżdżonych list albo stworzyć z nich mapę. Niestety Java nie pozwala używać obiektów jak struktur danych, chyba że sięgniesz po refleksję.

Poza tym albo źle zrozumiałem treść pytania albo tam nie ma nic związanego z filtrowaniem

1
    ArrayList<List<LocalDate>> listy = new ArrayList<List<LocalDate>>();
    listy.add(obj.list1.dates);
    listy.add(obj.list2.dates);
    listy.add(obj.list3.dates);
    Stream<LocalDate> str = listy.stream()
      .map(L -> L.stream().max(LocalDate::compareTo))
      .filter(Optional::isPresent)
      .map(Optional::get);
    //str.forEach(d -> {System.out.println("data: " + d);});
    System.out.println("min: " + str.min(LocalDate::compareTo));
1

Sorki, nie zrozumiałem zagnieżdżenia tych list. Ta notacja trochę mnie myli. Ale już chyba kumam.

  1. Nie ma czegoś takiego jak list1.dates, tylko list1.get(0..).dates
  2. Uniknijmy exception, jeżeli żadnej daty nie podano.

Ostatecznie wychodzi mi tak:

    ArrayList<Stream<LocalDate>> strumienieDat =
      new ArrayList<Stream<LocalDate>>();
    strumienieDat.add(obj.list1.stream().flatMap(L -> L.dates.stream()));
    strumienieDat.add(obj.list2.stream().flatMap(L -> L.dates.stream()));
    strumienieDat.add(obj.list3.stream().flatMap(L -> L.dates.stream()));
    Stream<LocalDate> strumienDat = strumienieDat.stream()
      .map(S -> S.max(LocalDate::compareTo))
      // Mamy strumień typu Optional<LocalDate>, odfiltrujmy puste elementy
      .filter(opt -> opt.isPresent())
      // i wyciągnijmy daty z optionali.
      .map(opt -> opt.get());
    // W strumienDat mamy teraz listę dat maksymalnych z poszczególnych list.
    //strumienDat.forEach(d -> {System.out.println("data: " + d);});
    Optional<LocalDate> optD = strumienDat.min(LocalDate::compareTo);
    if (optD.isPresent())
      System.out.println("min: " + optD.get());

Dzięki za fajne ćwiczenie ze streamów i optionali. :)

Ale tak naprawdę wszystkie pola listN powinny implementować jakiś wspólny interfejs posiadający getDates. Inaczej to chyba błąd projektowy.

EDIT:
Jeszcze rozwiązanie w Groovy, czyli funkcjonalna java z dynamicznymi typami. No bo to najlepszy sposób na te pola dates:

    def listaListList = [ obj.list1, obj.list2, obj.list3 ]
    def listaMaksDat = listaListList.collect {
      arrayList -> arrayList.collect { o -> o.dates }
                            .flatten { date -> date }
                            .max()
    }
    def data = listaMaksDat.min()
    println "To jest data albo null: $data"
0
Pytanie czy możesz modyfikować oryginalny obiekt? Jeżeli tak, to wystarczy dorzucić do niego coś w rodzaju: ``` public Stream<t> stream(){ return Stream.of(list1, list2, list3).flatMap(List::stream); } ``` i dalej pracujesz z pojedynczym strumieniem. Trochę gorzej jeżeli nie możesz modyfikować oryginalnej klasy, bo wtedy trzeba przenieść generowanie strumienia w miejsce użycia. W każdym razie, sama zamiana obiekty zawierającego kilka pól tego samego typu na strumień z tych pól nie powinna być dużym problemem. Później już wystarczy: ``` dateStream .min(LocalDate::compareTo) .ifPresent(System.out::println); ```

Po doprecyzowaniu:


Optional<LocalDate> maxFrom(List<<LocalDate> l){
	return l.stream()
		.min(LocalDate::compareTo);
}


Stream.of(maxFrom(lista1), maxFrom(lista2), maxFrom(lista3))
	.filter(Optional::isPresent)
	.map(Optional::get)
	.max(LocalDate::compareTo)

1 użytkowników online, w tym zalogowanych: 0, gości: 1