Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre averti

    Profil pro
    Enseignant
    Inscrit en
    juillet 2003
    Messages
    285
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : juillet 2003
    Messages : 285
    Points : 439
    Points
    439
    Par défaut Filtrer un TreeSet selon plusieurs critères (différents)
    Bonjour !
    Je me remets dans le monde du Java depuis quelques jours (après des années d’inactivité en programmation ), et je suis visiblement rouillé.
    J’ai un TreeSet peuplé d’éléments que j’aimerais filtrer selon plusieurs critères. Je sais filtrer via filter().collect(), mais pour un seul élément…

    Je m’explique un peu mieux :
    J’ai la classe Person
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    import java.time.LocalDate;
    import java.time.temporal.ChronoUnit;
    
    public class Person implements Comparable<Person> {
        private String firstName;
        private String lastName;
        private LocalDate birthday;
        private Smtg something;
    
        public Person(String firstName, String lastName, LocalDate birthday, Smtg something) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.birthday = birthday;
            this.something = something;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public LocalDate getBirthday() {
            return birthday;
        }
    
        public void setBirthday(LocalDate birthday) {
            this.birthday = birthday;
        }
    
        public void setSomething(Smtg something) {
            this.something = something;
        }
    
        public Smtg getSomething() {
            return something;
        }
    
        public Category getCategory() {
            var age = ChronoUnit.YEARS.between(birthday, LocalDate.now());
            if (age >= 0 && age < 2) return Category.BABY;
            else if (age >= 2 && age < 12) return Category.CHILD;
            else if (age >= 12 && age < 18) return Category.TEEN;
            else if (age >= 18 && age < 65) return Category.ADULT;
            else if (age >= 65) return Category.SENIOR;
            else return Category.UNKNOWN;
        }
    
        @Override
    public int compareTo(Person o) {
            if (this.firstName.equalsIgnoreCase(o.firstName)) return this.lastName.compareTo(o.lastName);
            else return this.firstName.compareTo(o.firstName);
        }
    
        @Override
    public String toString() {
            return firstName + " " + lastName + " " + getCategory().toString() + " " + something.toString();
        }
    
        public enum Category {
            BABY,
            CHILD,
            TEEN,
            ADULT,
            SENIOR,
            UNKNOWN
    }
    
        public enum Smtg {
            SOMETHING_1,
            SOMETHING_2,
            SOMETHING_3
    }
    }
    et une MainApp pour faire de la console:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import java.time.LocalDate;
    import java.util.SortedSet;
    import java.util.TreeSet;
    
    public class TestApp {
        private TreeSet<Person> people = new TreeSet<>();
    
        public static void main(String[] args) {
            var app = new TestApp();
    
            app.people.add(new Person("Lemon", "Ray", LocalDate.of(1981, 8, 12), Person.Smtg.SOMETHING_1));
            app.people.add(new Person("Onyme", "Anne", LocalDate.of(1980, 7, 20), Person.Smtg.SOMETHING_1));
            app.people.add(new Person("Lemoche", "Rocky", LocalDate.of(2007, 4, 1), Person.Smtg.SOMETHING_2));
            app.people.add(new Person("Fonfec", "Sophie", LocalDate.of(2017, 4, 12), Person.Smtg.SOMETHING_3));
    
            app.people.forEach(System.out::println);
    
        }
    
        private SortedSet<Person> filter(Object... tags) {
            var data = new TreeSet<>(people);
            if (tags == null) return data;
            //TODO
    }
    }
    
    J’aimerais pouvoir filtrer mes personnes soit sur le nom/prénom, soit les personnes nées en 2007, soit les personnes adultes,etc…
    Soit j’aimerais récupérer une liste des personnes ADULT et SOMETHING_1 dont le nom contiendrait un R…

    Dans l’idéal, j’aimerais associer ça à un Observable pour pouvoir faire des recherches "temps réel". En JavaFX, il existe une méthode, mais j’aimerais pouvoir appliquer ça en console…

    Quelqu’un peut éclairer ma lanterne ?
    Merci !

  2. #2
    Modérateur
    Avatar de wax78
    Homme Profil pro
    Chef programmeur
    Inscrit en
    août 2006
    Messages
    3 884
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chef programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2006
    Messages : 3 884
    Points : 7 645
    Points
    7 645
    Par défaut
    Bonjour,

    Peut être tout simplement en utilisant les Stream() et ses filtres ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Set<Person> retour1 = 
            	app.people.stream().
                	filter(p -> p.getCategory()==Category.ADULT).
                	filter(p -> p.getBirthday().getYear()==2007).
                	filter(p -> p.getLastName().startsWith("R")).
                	collect(Collectors.toSet());
    ou

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Set<Person> retour2 = 
            	app.people.stream().
                	filter((p) -> 
                		p.getCategory()==Category.ADULT &&
                		p.getBirthday().getYear()==2007 &&
                		p.getLastName().startsWith("R")
                	).collect(Collectors.toSet());
    (Les "ça ne marche pas", même écrits sans faute(s), vous porteront discrédit ad vitam æternam et malheur pendant 7 ans)

    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    septembre 2009
    Messages
    11 794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2009
    Messages : 11 794
    Points : 27 819
    Points
    27 819
    Billets dans le blog
    2
    Par défaut
    Salut.

    En complément, si on veut sélectionner tous les éléments qui ont l'un des "somethings":

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    private SortedSet<Person> filter(Person.Smtg ... tags) {
    	var tagfilter = EnumSet.copyOf(Arrays.asList(tags));
            var data = new TreeSet<>(people);
            return data.stream()
                       .filter(person-> tagfilter.contains(person.getSomething()))
                       .collect(Collectors.toCollection(TreeSet::new));
    }
    condition qu'on peut combiner avec d'autres de la même manière qu'indiqué par wax78.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  4. #4
    Membre averti

    Profil pro
    Enseignant
    Inscrit en
    juillet 2003
    Messages
    285
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : juillet 2003
    Messages : 285
    Points : 439
    Points
    439
    Par défaut
    Bonjour !
    Oui, j’y ai effectivement pensé, c’est une solution qui marche, mais qui m’embête un peu…
    Je vais tenter une analogie avec une interface graphique :

    Admettons que j’aie une liste avec mes personnes, un champ de recherche et deux combo box affichant les catégories et les Something.

    J’aimerais qu’en sélectionnant un élément d’une combobox, ma liste soie filtrée sur cet élément. Et si par la suite, je souhaite ajouter ou retirer des contraintes, ma liste s’adapte.
    De plus, dans mon champs de recherche, je peux inscrire plusieurs "tags" (séparés par des espaces) pour rechercher chaque tag dans ma liste.

    Le tout pouvant être combiné indifféremment.
    Je peux obtenir ce comportement avec des filter() ?

  5. #5
    Modérateur
    Avatar de wax78
    Homme Profil pro
    Chef programmeur
    Inscrit en
    août 2006
    Messages
    3 884
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Chef programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2006
    Messages : 3 884
    Points : 7 645
    Points
    7 645
    Par défaut
    Je ne suis pas sure de t'avoir bien compris, mais je pense que tu peux faire cela avec filter() oui.

    C'est a dire combiner plusieurs predicats (que tu peux activé ou non) avec des AND et des OR.

    Avec quelques chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Predicate<Person> predicate_category = p -> p.getCategory()==Category.ADULT;
    Predicate<Person> predicate_namecontains = p -> p.getLastName().contains("R");
    // ...
     
    List<Predicate<Person>> predicats_list = new ArrayList<Predicate<Person>>();
     
    if (true) // Une condition, par exemple activé le filtrage par categorie
    	predicats_list.add(predicate_category);
    if (true) // ..
    	predicats_list.add(predicate_namecontains);
    // ...
     
    Predicate<Person> predicats = predicats_list.stream().reduce(x -> true, Predicate::and);
    Set<Person> retour3 = app.people.stream().filter(predicats).collect(Collectors.toSet());
    (Les "ça ne marche pas", même écrits sans faute(s), vous porteront discrédit ad vitam æternam et malheur pendant 7 ans)

    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    septembre 2009
    Messages
    11 794
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2009
    Messages : 11 794
    Points : 27 819
    Points
    27 819
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Raylemon Voir le message
    Admettons que j’aie une liste avec mes personnes, un champ de recherche et deux combo box affichant les catégories et les Something.
    En théorie, si plusieurs champs de filtre sont présents, tous devraient s'appliquer et le and reste valable.

    Citation Envoyé par Raylemon Voir le message
    J’aimerais qu’en sélectionnant un élément d’une combobox, ma liste soie filtrée sur cet élément. Et si par la suite, je souhaite ajouter ou retirer des contraintes, ma liste s’adapte.
    Si le formulaire permet d'ajouter/retirer des critères, tu peux construire l'expression de filtre en construisant des Predicate séparés, et les combiner par la méthode and.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    private Predicate<Person> combine(Predicate<Person> p1, Predicate<Person> p2) {
        if ( p1==null ) {
             return p2;
        }
        else {
             return p1.and(p2);
        }    
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public Predicate<Person> buildFilter() {
        Predicate<Person> predicate=null;
        if ( nameFilter.isActivated() {
            predicate = person-> person.getName().contains(nameFilter.gerValue());
        }
        if ( birthDateFilter.isActivated() {
            predicate = combine(predicate, person-> person.getCategory()==birthDateFilter.getValue());
        }
        /*...*/
        if ( predicate==null ) {
            predicate = person-> false;
        }
        return predicate;
    }
    (même éventuellement en faisant une méthode getPredicate(Person) pour chaque filtre de l'UI)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    treeset.stream().filter(buildFilter())./*...*
    Citation Envoyé par Raylemon Voir le message
    De plus, dans mon champs de recherche, je peux inscrire plusieurs "tags" (séparés par des espaces) pour rechercher chaque tag dans ma liste.

    Avec mon exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    private Predicate<Person> buildTagPredicate(Person.Smtg ... tags) {
    	var tagfilter = EnumSet.copyOf(Arrays.asList(tags));
            return person-> tagfilter.contains(person.getSomething());
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    private Predicate<Person> buildTagPredicate(String tags) {
        	StringBuilder sbUnknownTags = new StringBuilder(); 
        	Person.Smtg[] array = Arrays.stream(tags.split("\\s+"))
        	      .map(tag-> {
        	    	  Person.Smtg smtg;
        	    	  try{
        	    		  smtg= Person.Smtg.valueOf(tag.toUpperCase());
    		    	  }catch (IllegalArgumentException e) {
    		    	    	  sbUnknownTags.append(tag);
    		    	    	  smtg= null;
    				  }
        	    	  return smtg;
        	   }).filter(Objects::nonNull)
        	   .toArray(Person.Smtg[]::new);
        	// faire éventuellement quelque chose de sbUnknownTags, ou pas
        	return buildTagPredicate(array);
        }
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  7. #7
    Membre averti

    Profil pro
    Enseignant
    Inscrit en
    juillet 2003
    Messages
    285
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : juillet 2003
    Messages : 285
    Points : 439
    Points
    439
    Par défaut
    Merci Wax78 et joel.drigo,
    Je pense avoir avancé dans mon problème et résolu une partie.
    J’ai créé une classe qui gère le filtrage selon plusieurs critères différents.
    Je continue de creuser pour tenter de rendre cette classe Observable, afin de refléter les changements directement.
    Je la poste ici pour avoir une confirmation que je n’ai pas codé une monstruosité

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    import org.fluttercode.datafactory.impl.DataFactory; //permet de générer des données aléatoires
    
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.util.Set;
    import java.util.TreeSet;
    import java.util.function.Predicate;
    import java.util.stream.Collectors;
    
    public class PersonManager {
        private TreeSet<Person> data;
        private Person.Category filterCategory;
        private Person.Smtg filterSmtg;
    
        PersonManager() {
            data = new TreeSet<>();
            var factory = new DataFactory();
            for (int i = 0; i < 15; i++) {
                var instant = factory.getBirthDate().toInstant();
                var ld = LocalDate.ofInstant(instant, ZoneId.systemDefault());
                data.add(new Person(factory.getFirstName(),
                        factory.getLastName(),
                        ld,
                        factory.getItem(Person.Smtg.values())));
            }
    
        }
    
        private Predicate<Person> combine(Predicate<Person> pre1, Predicate<Person> pre2) {
            if (pre1 == null) return pre2;
            else return pre1.and(pre2);
        }
    
        public TreeSet<Person> getData() {
            return data;
        }
    
        Set<Person> filter() {
            return filter("");
        }
    
        Set<Person> filter(String tag) {
            Predicate<Person> predicate = null;
            if (!tag.isEmpty()) {
                var tags = tag.split(" ");
    
                for (var t : tags) {
                    if (predicate == null) {
                        predicate = person -> person.getFirstName().contains(t);
                        predicate  = predicate.or(person -> person.getLastName().contains(t));
                    } else {
                        predicate =predicate.or(person -> person.getFirstName().contains(t));
                        predicate = predicate.or(person -> person.getLastName().contains(t));
                    }
                }
            }
            if (filterCategory != null)
                predicate = combine(predicate, person -> person.getCategory() == filterCategory);
            if (filterSmtg != null) predicate = combine(predicate, person -> person.getSomething() == filterSmtg);
    
            if (predicate == null) return new TreeSet<>(data);
            else return data.parallelStream().filter(predicate).collect(Collectors.toSet());
        }
    
        public Person.Category getFilterCategory() {
            return filterCategory;
        }
    
        public void setFilterCategory(Person.Category filterCategory) {
            this.filterCategory = filterCategory;
        }
    
        public Person.Smtg getFilterSmtg() {
            return filterSmtg;
        }
    
        void setFilterSmtg(Person.Smtg filterSmtg) {
            this.filterSmtg = filterSmtg;
        }
    
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. filtrer les articles selon plusieurs critères
    Par kabbaj.med dans le forum Excel
    Réponses: 2
    Dernier message: 25/07/2016, 09h08
  2. Réponses: 0
    Dernier message: 27/03/2015, 11h31
  3. Réponses: 2
    Dernier message: 17/02/2014, 09h23
  4. Afficher un message selon plusieurs critères
    Par NEC14 dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 02/04/2009, 16h43
  5. [HASHING] Trie selon plusieurs critères
    Par hush dans le forum APIs
    Réponses: 6
    Dernier message: 24/07/2006, 06h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo