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.
4 Réponses :
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() {...} }
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.
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() ) );
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;
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.
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.