# Probleme beim Filtern über Predicate



## bugbunny (10. Mrz 2018)

Hallo, 
ich komme leider wieder einmal nicht weiter. 
Ich habe in einer Klasse Spielerbestand folgende Methode:


```
public Set<Player> getPlayers(Predicate<Player> filter)
{
    Set<Player> result = new HashSet<Player>();

    for (Player player : players)
    {
        if (filter.test( player ))
        {
            result.add(player);
        }
    }
    return result;
```

Nun habe ich noch eine Klasse PlayerFilter, in der verschiedene Filtermöglichkeiten gegeben werden sollen. 


```
public static Predicate<Player> getLastNameFilter(String lastName)
{    Validate.requireNonNullNotEmpty( lastname );
    return player -> player.getMetadata().getLastName().orElse( "" ).equals( lastName );
}
```

Das funktioniert noch, aber jetzt soll ich noch 2 Methoden schreiben die die Filter kombinieren.
public static Predicate<Player> combineAnd(Collection<Predicate<Player>> filters) 

Es wird eine Collection der definierten Filter übergeben und Spieler zurückgegeben die alle übergebenen Filter erfüllen(AND) bzw. mindestens einen(OR)

Das man predicates mit .and kombinieren kann habe ich gesehen, aber versteht nicht wie ich das auf die variable Collection anwenden könnte. 

Grüße Leonie


----------



## Robat (10. Mrz 2018)

Hier mal ein kleines Beispiel anhand von Integern.

```
List<Predicate<Integer>> predicates = List.of(e -> e % 2 == 0, e -> e > 2);
List<Integer> values = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
values.stream().filter(predicates.stream().reduce(Predicate::and).orElse(t -> true)).forEach(System.out::println);
```

`Predicate::and` kann man dann auch mit `Predicate::or` tauschen.


----------



## bugbunny (10. Mrz 2018)

Danke für das Beispiel, allerdings kann ich es nicht übertragen.

List<Predicate<Integer>> predicates
=Liste mit den Filtern
Meine Methode bekommt eine Collection übergeben, also erstelle ich in der Methode eine neue Liste und übergebe der die Werte der Collection oder?

List<Integer> values
Eine Liste mit den Inhalten der Integer.
Wäre in meinem Fall also eine Liste vom Typ Spieler?
Also brauche ich eine 2. Liste mit allen Spielern?

Ich habe eine Methode
public Set<Player> getPlayers in der Klasse Spielerbestand die ein Hashset von Spielern zurückgibt, müsste ich die dann der 2. Liste übergeben?
Kann es nicht ausprobieren, da ich aus der Filterklasse gar nicht auf die Methode zugreifen kann?

Beziehungsweise müsste ich dazu ja ein Objekt vom Typ Spielerbestand in der Methode erzeugen, ist ja sicher nicht so gedacht oder?

Grüße


----------



## looparda (10. Mrz 2018)

bugbunny hat gesagt.:


> Danke für das Beispiel, allerdings kann ich es nicht übertragen.


Du musst in Robat's Beispiel wirklich nur noch alles von Integer auf Player umstellen und das wars.
Statt _List<Integer> values_ benutzt du dann halt _Set<Player>_ um konform mit deiner Methode _Set<Player> getPlayers()_ zu sein.
Momentan werden die Elemente, die durch den Filter gekommen sind ausgegeben - die kannst du natürlich stattdessen aufsammeln und dann hast du deine Ergebnisliste mit den gefilterten Elementen.


----------



## bugbunny (10. Mrz 2018)

Ja das habe ich gemacht, das geht auch, aber ich kann es nicht als return Statement verwenden und genau das brauche ich aber?


----------



## Robat (10. Mrz 2018)

Dann mach mit collect wieder eine Collection draus.


----------



## looparda (10. Mrz 2018)

```
public static Collection<Player> combineAnd(List<Predicate<Player>> predicates, Collection<Player> collection) {
    return collection.stream().filter(predicates.stream().reduce(Predicate::and).orElse(t -> true)).collect(Collectors.toList());
}
```

Warum heißt das Ding eigentlich combineAnd und nicht filterAnd?


----------



## bugbunny (10. Mrz 2018)

ich darf die übergebenen Parameter der methode nicht ändern, die Methode muss 
	
	
	
	





```
public static Predicate<Player> combineAnd(Collection<Predicate<Player>> filters)
```
 sein.


----------



## looparda (10. Mrz 2018)

Dann wird es so gemeint sein:

```
final Collection<Player> filteredPlayers = filter(predicates, values);
filteredPlayers.forEach(System.out::println);
[...]

public static Collection<Player> filter(List<Predicate<Player>> predicates, Collection<Player> collection) {
    return collection.stream().filter(combineAnd(predicates)).collect(Collectors.toList());
}
public static Predicate<Player> combineAnd(Collection<Predicate<Player>> filters) {
    return filters.stream().reduce(Predicate::and).orElse(t -> true);
}
```


----------



## bugbunny (10. Mrz 2018)

Die Vorgabe dazu lautet:
"Für die Methode getPlayerByFilter(Predicate<Player> filter) der
Klasse Spielerbestand lassen sich auch Filter vordefinieren.
Implementieren Sie dazu die Klasse PlayerFilterFactory, die einige statische Methoden zur Verfügung stellt, um Filter zu erzeugen und durch und oder oder zu verknüpfen."
Die schaut bei mir so aus:

```
public Set<Player> getPlayers(Predicate<Player> filter)
{
    Set<Player> result = new HashSet<Player>();

    for (Player player : players)
    {
        if (filter.test( player ))
        {
            result.add(player);
        }
    }
    return result;
}
```

"public static Predicate<Player> combineAnd(Collection<Predicate<Player>>
filters)
Gibt einen Filter zurück, der einen Spieler genau dann akzeptiert, wenn er alle in der übergebenen Collection enthaltenen Filter akzeptiert.Ist die übergebene Collection leer, soll eine java.lang.IllegalArgumentException geworfen werden.
Tipp: Nutzen Sie die Default-Methode and der Schnittstelle Predicate."

Deswegen sollte es meinem "Verständnis" nach ohne die zusätzliche  public static Collection<Player> filter funktionieren oder verstehe ich das falsch?


----------



## Robat (10. Mrz 2018)

Deine getPlayers() Methode ersetzt quasi die filter-Methode von @looparda
Du kannst quasi sowas machen:

```
Set<Player> filteredPlayers = getPlayers(combineAnd(predicateCollection));
//oder konkreter
List<Predicate<Player>> collection = Arrays.asList(PlayerFilterFactory.predicateMethod1(..), PlayerFilterFactory.predicateMethode2(..));
Set<Player> filteredPlayers = getPlayers(combineAnd(collection));
```


----------



## bugbunny (11. Mrz 2018)

Ich verstehe es einfach nicht 
Ich kann der Collection mit Arrays.asList doch nicht die einzelnen Filtermethoden übergeben, wenn ich nicht weiß, welche Filter später mal in der Collection sind???
Und auch die getPlayer Methode kann ich doch nicht ohne Objekt nicht verwenden?
Wahnsinn wie kompliziert das ist.


----------



## Robat (11. Mrz 2018)

Woher kommt denn die Collection mit den Predicates? Gibt es da eine vorgefertigte zum testen, kommt die aus deinem Programm, sollst du sie dir selber erstellen? Du musst doch Vorgaben zum testen haben und wenn nicht musst du dir selber Testdaten einfallen lassen. 

Das du eine Instanz brauchst um die getPlayers() Methode aufzurufen ist mir bewusst. Wollte nur nicht so viel schreiben, da ich dachte es sei klar  
Ich habe das oben nur so geschrieben um dir zu demonstrieren wie du das ganze jetzt verwenden könntest.

Du hast jetzt die combineAnd() Methode + Deine verschiedenen Filtermethoden (in der FilterFactory-Klasse?) und die getPlayers() Methode (in der Spielstand-Klasse [hab den Namen vergessen]).

Dann musst du dir doch jetzt nur noch ein paar Testfälle einfallen lassen.


----------

