5
votes

Filtrer les objets d'une liste qui ont le même membre

J'ai une liste d'objets. L'objet ressemble à ceci:

Set<String> agendas = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getAgendaCode)
    .collect(Collectors.toUnmodifiableSet());

Set<String> visitTypeCode = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getVisitTypeCode)
    .collect(Collectors.toUnmodifiableSet());

Set<String> scheduledTime = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getScheduledTime)
    .collect(Collectors.toUnmodifiableSet());

List<Slots> collect = slotsResponse.getContent().stream()
    .filter(c -> agendas.contains(c.getVisit().getAgendaCode()))
    .filter(c -> visitTypeCode.contains(c.getVisit().getVisitTypeCode()))
    .filter(c -> scheduledTime.contains(c.getVisit().getScheduledTime()))
    .collect(Collectors.toList());

J'ai besoin de trouver les éléments qui ont les mêmes agendaCode , visitTypeCode et planifiéTime et pour la vie de moi je ne peux pas le faire.

J'ai essayé ceci:

public class Slots {
  String slotType;
  Visits visit;
}


public class Visits {
  private long visitCode;
  private String agendaCode;
  private String scheduledTime;
  private String resourceType;
  private String resourceDescription;
  private String visitTypeCode;
  ...
}

Mais ça ne fait pas ce que je pensais. Idéalement, j'aurais une liste de listes, où chaque sous-liste est une liste d'objets Slots qui partagent les mêmes agendaCode , visitTypeCode et planifiéTime . J'ai du mal avec la programmation fonctionnelle donc toute aide ou pointeur serait génial!

C'est Java 11 et j'utilise également vavr.


1 commentaires

Vous pouvez éventuellement jeter un œil à la logique du filtre dans cette réponse , c'est ce qui résoudra ce vous êtes à la hauteur je crois.


4 Réponses :


1
votes

Le moyen le plus simple est de définir une nouvelle classe avec les champs nécessaires ( agendaCode , visitTypeCode et planifiéTime ). N'oubliez pas equals/hashcode .

Map<Code, List<Slots>> map = slotsResponse.getContent().stream()
            .collect(Collectors.groupingBy(s -> s.getVisit().getCode()));

Ensuite, vous pouvez utiliser groupingBy comme:

public class Visits {
    private long visitCode;
    private String resourceType;
    private String resourceDescription;
    private Code code;
    ...
}
class Code {
    private String agendaCode;
    private String scheduledTime;
    private String visitTypeCode;
    ...
    @Override
    public boolean equals(Object o) {...}
    @Override
    public int hashCode() {...}
}


0 commentaires

1
votes

J'aime l'idée de Ruslan d'utiliser Collectors :: groupingBy . Néanmoins, je n'aime pas créer une nouvelle classe ou définir une nouvelle méthode equals . Les deux vous contraignent à une seule version de Collectors :: groupingBy . Que faire si vous souhaitez regrouper par d'autres champs dans d'autres méthodes?

Voici un bout de code qui devrait vous permettre de surmonter ce problème:

slotsResponse.getContent()
    .stream()
    .collect(Collectors.groupingBy(s -> Arrays.asList(s.getVisit().getAgendaCode(), s.getVisit().getVisitTypeCode(), s.getVisit().getScheduledTime())))
    .values();

Mon idée était de créer un nouveau conteneur pour chaque champ nécessaire (agendaCode, visitTypeCode, scheludedTime) et comparez les emplacements sur ces conteneurs nouvellement créés. J'aurais aimé le faire avec un simple tableau Object , mais cela ne fonctionne pas - les tableaux doivent être comparés à Arrays.equals qui n'est pas la méthode de comparaison utilisée par Collectors :: groupingBy .

Veuillez noter que vous devez stocker quelque part ou utiliser une méthode pour définir les champs par lesquels vous souhaitez regrouper.


0 commentaires

3
votes

Puisque vous avez mentionné que vous utilisez vavr, voici la manière vavr de résoudre cette question.

Supposons que vous ayez votre io.vavr.collection.List (ou Array ou Vector ou Stream ou collection vavr similaire) de visites:

List<Visits> visits = ...;

Map<Tuple3<String, String, String>, List<Visits>> grouped = visits.stream().collect(
    Collectors.groupingBy(
        visit ->
            Tuple.of(
                visit.getAgendaCode(),
                visit.getVisitTypeCode(),
                visit.getScheduledTime()
            )
    )
);

Ou avec un java.util.List de visites:

List<Visits> visits = ...;

final Map<Tuple3<String, String, String>, List<Visits>> grouped =
    visits.groupBy(visit ->
        Tuple.of(
            visit.getAgendaCode(),
            visit.getVisitTypeCode(),
            visit.getScheduledTime()
        )
    );


0 commentaires

1
votes

Les champs que vous souhaitez regrouper sont tous des chaînes. Vous pouvez définir une fonction qui concatène les valeurs de ces champs et l'utilise comme clé pour vos groupes. Exemple

Map<String,List<Slots>> result = slotsResponse.getContent().stream()
                               .collect(Collectors.groupingBy(myFunc));

Et puis groupez comme ci-dessous:

Function<Slots,String> myFunc = s -> s.getVisit().agendaCode + s.getVisit().visitTypeCode + s.getVisit().scheduledTime;
                               // or s.getVisit().agendaCode +"-"+ s..getVisit().visitTypeCode +"-"+ s.getVisit().scheduledTime;


3 commentaires

La syntaxe semble invalide, vous voudrez peut-être simplement l'essayer sur un compilateur et la sortie serait une seule entrée mappée à une clé basée sur la même logique que myFunc pour extraire la List . Mais l'approche est bonne à mon humble avis.


Je voulais dire dans le sens que - 1. les champs font partie de Visites et non de Slots donc la définition de la fonction doit être mise à jour pour la compiler. 2. En cherchant dans le résultat de la carte, il faudrait faire la même logique de s.agendaCode + s.visitTypeCode + s.scheduledTime pour former une clé correcte pour lookup. ( result.get ) <- bien que cela devrait simplement être myFunc appliquer si le Slot aurait été connu.


De plus, x = a + b + c = d + e + f ils seraient regroupés dans le même groupe tout en ayant des valeurs différentes pour les composants individuels. La concaténation n'est donc pas garantie d'être correcte.