Partage
  • Partager sur Facebook
  • Partager sur Twitter

Générer des combinaisons à partir d'un objet

    5 décembre 2017 à 20:18:45

    Bonjour bonjour !

    après plusieurs heures de tests.... je fini par demander de l'aide :)

    J'ai besoin de générer un tableau de déclinaisons à partir d'un objet ayant la forme suivante : 

    var attributesTable = [
    { attributeName: "Color", values: ["Blue", "Red", "Yellow"] }, { attributeName: "Size", values: ["S", "M", "L", "XL"] }, { attributeName: "Collection", values: ["Winter", "Summer"] }, ]

    Sachant que le nombre d'attribut est variable ainsi que ses valeurs. 
    Mon but est de générer un tableau de déclinaisons unique. 

    Dans le cas présent on devrait donc obtenir 24 déclinaisons... 

    var declinaisons = [
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "S"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "M"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "L"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "XL"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "S"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "M"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "L"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Blue"
        }, {
            attributeName : "Size",
            Value : "XL"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "S"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "M"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "L"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "XL"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "S"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "M"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "L"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Red"
        }, {
            attributeName : "Size",
            Value : "XL"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "S"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "M"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "L"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "XL"
        }, {
            attributeName: "Collection",
            Value : "Winter"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "S"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "M"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "L"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
        [{
            attributeName : "Color",
            Value : "Yellow"
        }, {
            attributeName : "Size",
            Value : "XL"
        }, {
            attributeName : "Collection",
            Value : "Summer"
        }],
    ]
     


    Je voulais faire une boucle récurcive...  Mais sans succès. J'ai essayé de générer une sorte de table matricielle, mais je me suis perdu en route :D.

    Je me retrouve soit avec des boucles sans fins...  Soir avec toutes les valeurs dans chaque tableau de chaque combinaison, soit des trucs hors sujet.. 

    J'ai commencé par compter le nombre de combi possibles... 

                var combiQty = function (){
    
                    // calcul du nombre de combinaison = valeur ^valeur
    
                    var valuesLength = [];
    
                    for (var i = 0; i < attributesTable.length; i++) {
                        var attribute = attributesTable[i];
                        valuesLength.push(attribute.values.length);
                    }
    
                    var totalLength = 1
                    for (var i = 0; i < valuesLength.length; i++) {
                        currentVal =  valuesLength[i];
    
                        totalLength = currentVal * totalLength;  
                    }
                    return totalLength;
                }


    Puis pour chaque combinaison, insérer une valeur une fois sur X selon le nombre d'attribut...

                var combiQty = combiQty();
    
    
                var showCombi = function (combiQty){
                    var combiQty = combiQty; 
                    var allCombi = [];
                    
    
                    for (var i = 0; i < combiQty; i++) {
                        var combi = [];
                        allCombi.push(combi);
    
                        for (var j = 0; j < attributesTable.length; j++) {
                            var attribute = attributesTable[j];
    
                            for (var k = 0; k < attribute.values.length; k++) {
                                var value = attribute.values[k];
    
                                if (Number.isInteger(i / attribute.values.length)) {
                                    allCombi[i].push(value)
    
                                }
    
                            }
    
                        }
                    }
    
    
    
                    return allCombi;
    
    
    
                }
    
                console.log(showCombi(combiQty));

    Mais évidemment ça ne marche pas :). 

    Enfin ce n'est que l'une des inombrables méthodes que j'ai essayé d'appliquer...  J'ai bien trouvé des exemples de générateur de déclinaisons sur le net, mais généralement ça créer les combinaisons d'un même tableau.. 

    Je suis preneur de tout ce qui peut me faire avancer :)



    -
    Edité par mike57190 5 décembre 2017 à 21:19:29

    • Partager sur Facebook
    • Partager sur Twitter
      5 décembre 2017 à 22:47:10

      Salut,

      Exercice intéressant. C'est un jeu d'enfant si le nombre d'attributs et fixe, on imbrique autant de boucle for qu'il y a d'attribut et le tour est joué.

      const allCombi = [];
      for (const color of attributesTable[0]) {
        for (const size of attirbutedTable[1]) {
          for (const collection of attributesTable[2] {
            allCombi.push(color + ' ' + size + ' ' + collection);
          }
        }
      }

      Evidemment, si le nombre d'attributs varie on peut pas mettre en oeuvre cette solution. Il faut déjà voir comment tu décrit une combinaison, moi j'aurais tendance à mettre ça sous forme d'un tableau de tableau :

      [
         ["Blue", "S"] // une combinaison 
         ["Red", "S"], // une autre
         ["Yellow", "S"],
         ["Blue", "M"]
         // etc...
      ]


      A partir de là, tu peux créer une fonction qui prend en argument une liste de combinaisons comme celle-ci, et une nouvelle source de combinaison, comme ["Winter", "Summer"], et qui renvoie la nouvelle liste de combinaison possible. 

      Cette fonction doit faire une double boucle : parcourir toutes les combinaisons précédentes, et pour chacune d'elle, créé autant de nouvelle combinaison qu'il y a de valeurs dans la nouvelle source.

      Une fois que tu as ta fonction, t'as plus qu'à parcourir ton tableau d'attributs et d'appeler ta fonction pour chaque liste de valeur.

      -
      Edité par LCaba 6 décembre 2017 à 12:22:31

      • Partager sur Facebook
      • Partager sur Twitter
        6 décembre 2017 à 8:48:45

        Bonjour,

        Pas très facile à faire en effet, une bonne stratégie à base de map, de reduce et de concaténation de tableau peut te permettre d'arriver à un résultat correct sans utiliser de récursivité (sachant que JavaScript n'est pas très bon en terme de Tail Call Optimization, ce qui signifie qu'il faut dans la mesure du possible éviter la récursion avec ce langage).

        Démo JSFiddle.

        • Partager sur Facebook
        • Partager sur Twitter
          6 décembre 2017 à 13:19:39

          Puisqu'on y va de sa petite démo ;) https://jsfiddle.net/7ff6yhjj/ Le plus (sinon ça sert à rien de poster) c'est que ça fonctionne quelques soit le nombre d'éléments dans le tableau attributesTable, sans récurcivité. 

          ECMAScript2015 offre des outils très puissants, comme l'opérateur de décomposition et les nom de propriétés calculés. Ainsi, mes lignes 32 à 37...

          nouvellesDéclinaisons.push( 
            {
              ...combinaison,
              [nouvelleSource.attributeName]: valeur
            }
          )


          ...nécessiterait plus de code et des variables supplémentaire pour fonctionner sur un vieux navigateur :

          var nouvelObjet = {};
          var key;
          
          for (key in combinaison) {
            nouvelObjet[key] = combinaison[key];
          }
          nouvelObjet[nouvelleSource.attributeName] = value;
          
          nouvellesDéclinaisons.push(nouvelObjet);
          
          // note, ne pas essayer ça :
          var nouvelObjet = combinaison;
          nouvelObjet[nouvelleSource.attributeName] = value;
          // car combinaison et nouvelObjet font référence à un
          // seul et même objet : modifier nouvelObjet modifie 
          // combinaison, donc ça foire les tours de boucle 
          // forEach qui suivent



          -
          Edité par LCaba 6 décembre 2017 à 13:20:10

          • Partager sur Facebook
          • Partager sur Twitter
            6 décembre 2017 à 18:26:52

            Merci à tous pour vos réponses ! 

            Heureux de voir que le sujet en a titillé certains ^^

            LCaba, ta solution marche à merveille ! Franchement c'est beau et rapide en plus ! (1 million de combinaisons par seconde :) )

            Je vais analyser ça de plus près :D

            • Partager sur Facebook
            • Partager sur Twitter
              7 décembre 2017 à 15:55:08

              Alors oui c'est beau, mais ça tient beaucoup aux fonctions fléchées et à l'opérateur de décomposition ;)

              Par contre, c'est pas hyper rapide, et surtout c'est très gourmant en mémoire : si tu as 20 attributs différents, mon algorithme fait d'abord un tableau avec les valeurs du premier attribut. Puis il fait un tableau qui combine ses valeurs avec les valeurs du 2nd attributs. Puis encore un tableau pour combiner ces combinaisons avec les valeurs du 3e attributs. Etc... donc beaucoup d'objet créer qui au final vont prendre beaucoup de place en mémoire et vont pas être réutilisés par la suite.

              Si tu as quelques dizaines de combinaisons, c'est pas la mort, mais si tu en as des millions faut changer de méthodes. Voici un exemple plus économe : (Attention, patientez plusieurs secondes sur certaines machines) https://jsfiddle.net/u3xs5gq8/2/ (à comparer avec https://jsfiddle.net/7ff6yhjj/3/ ) 

              Le principe est le suivant : si je prends ton tableau initial, y a 3 attributs, donc je créer un tableau de 3 indexs [0, 0, 0]. Pour faire ma première combinaison, je prends la valeur d'index 0 dans le 1er attributs, la valeur d'index 0 dans le 2nd, et la valeur d'index 0 dans le 3e.

              Ensuite j'incrémente : [0, 0, 1]. Je prends les 2 même valeurs (index 0) pour les 2 premiers attributs, et la valeur d'index 1 pour le 3e.

              Et je continue mon incrémentaiton, en utilisant un tableau de la forme [2, 3, 1] (length des tableau values - 1) pour savoir quand repartir de 0 en incrémentant l'index voisin : [0, 1, 0], [0, 1, 1], [0, 2, 0], [0, 2, 1], [0, 3, 0], [0, 3, 1], [1, 0, 0], [1, 0, 1], etc...

              -
              Edité par LCaba 7 décembre 2017 à 15:56:32

              • Partager sur Facebook
              • Partager sur Twitter

              Générer des combinaisons à partir d'un objet

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown