web-dev-qa-db-fra.com

Produit cartésien de plusieurs tableaux en JavaScript

Comment implémenteriez-vous le produit cartésien de plusieurs tableaux en JavaScript?

Par exemple,

cartesian([1,2],[10,20],[100,200,300]) //should be
// [[1,10,100],[1,10,200],[1,10,300],[2,10,100],[2,10,200]...]
73
viebel

Voici une solution fonctionnelle au problème (sans aucune variable mutable!) En utilisant reduce et flatten, fournie par underscore.js:

function cartesianProductOf() {
    return _.reduce(arguments, function(a, b) {
        return _.flatten(_.map(a, function(x) {
            return _.map(b, function(y) {
                return x.concat([y]);
            });
        }), true);
    }, [ [] ]);
};

cartesianProductOf([1, 2], [3, 4], ['a', 'b']); // [[1,3,"a"],[1,3,"b"],[1,4,"a"],[1,4,"b"],[2,3,"a"],[2,3,"b"],[2,4,"a"],[2,4,"b"]] 

Remarque: Cette solution a été inspirée par http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/

84
viebel

Mise à jour 2017: réponse à 2 lignes avec Vanilla JS

Toutes les réponses ici sont trop compliquées, la plupart d’entre elles prennent 20 lignes de code ou même plus.

Cet exemple utilise seulement deux lignes de JavaScript Vanilla, pas de lodash, de soulignement ou d’autres bibliothèques:

let f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesian = (a, b, ...c) => b ? cartesian(f(a, b), ...c) : a;

Mettre à jour:

C’est la même chose que ci-dessus, mais nous avons amélioré le suivi strict du Airbnb JavaScript Style Guide - validé avec ESLint avec eslint-config-airbnb-base :

const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e))));
const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a);

Un merci spécial à ZuBB de m'avoir informé des problèmes rencontrés par linter avec le code original.

Exemple

Voici l'exemple exact de votre question:

let output = cartesian([1,2],[10,20],[100,200,300]);

Sortie

Voici le résultat de cette commande:

[ [ 1, 10, 100 ],
  [ 1, 10, 200 ],
  [ 1, 10, 300 ],
  [ 1, 20, 100 ],
  [ 1, 20, 200 ],
  [ 1, 20, 300 ],
  [ 2, 10, 100 ],
  [ 2, 10, 200 ],
  [ 2, 10, 300 ],
  [ 2, 20, 100 ],
  [ 2, 20, 200 ],
  [ 2, 20, 300 ] ]

Démo

Voir les démos sur:

Syntaxe

La syntaxe que j’ai utilisée ici n’est pas nouvelle ... Mon exemple utilise l’opérateur spread et les paramètres restants - fonctionnalités de JavaScript définies dans la 6e édition de la norme ECMA-262 publiée en juin 2015 et développée bien avant, mieux connue sous le nom de ES6 ou ES2015. Voir:

Ce code rend le code si simple que c’est un péché de ne pas l’utiliser. Pour les anciennes plates-formes qui ne le supportent pas nativement, vous pouvez toujours utiliser Babel ou d’autres outils pour le transpiler avec une syntaxe plus ancienne. vraiment important parce que le résultat de la transpilation n'est pas quelque chose que vous devez comprendre ou maintenir, c'est juste un fait que j'ai trouvé intéressant.

Conclusion

Il n'est pas nécessaire d'écrire des centaines de lignes de code difficiles à gérer et il n'est pas nécessaire d'utiliser des bibliothèques entières pour une chose aussi simple, lorsque deux lignes de JavaScript Vanilla peuvent facilement faire le travail. Comme vous pouvez le constater, il est vraiment rentable d'utiliser les fonctionnalités modernes du langage. Dans les cas où vous devez prendre en charge des plates-formes archaïques sans prise en charge native des fonctionnalités modernes, vous pouvez toujours utiliser Babel ou d'autres outils pour transposer la nouvelle syntaxe dans l'ancienne .

Ne code pas comme en 1995

JavaScript évolue et il le fait pour une raison. TC39 fait un travail incroyable de conception de langage en ajoutant de nouvelles fonctionnalités et les éditeurs de navigateurs réalisent un travail incroyable d’implémentation de ces fonctionnalités.

Pour voir l'état actuel de la prise en charge native d'une fonctionnalité donnée dans les navigateurs, voir:

Pour voir le support dans les versions de nœud, voir:

Pour utiliser la syntaxe moderne sur les plates-formes qui ne la prennent pas en charge de manière native, utilisez Babel:

64
rsp

Voici une version modifiée du code de @ viebel en Javascript simple, sans utiliser aucune bibliothèque:

function cartesianProduct(arr)
{
    return arr.reduce(function(a,b){
        return a.map(function(x){
            return b.map(function(y){
                return x.concat(y);
            })
        }).reduce(function(a,b){ return a.concat(b) },[])
    }, [[]])
}

var a = cartesianProduct([[1, 2,3], [4, 5,6], [7, 8], [9,10]]);
console.log(a);
33
Danny

il semble que la communauté pense que ceci est trivial et ou facile à trouver une implémentation de référence, après une brève inspection, je ne pouvais pas ou peut-être que c'est juste que j'aime réinventer la roue ou résoudre des problèmes de programmation semblables à ceux d'une salle de classe, de toute façon, c'est votre jour de chance :

function cartProd(paramArray) {

  function addTo(curr, args) {

    var i, copy, 
        rest = args.slice(1),
        last = !rest.length,
        result = [];

    for (i = 0; i < args[0].length; i++) {

      copy = curr.slice();
      copy.Push(args[0][i]);

      if (last) {
        result.Push(copy);

      } else {
        result = result.concat(addTo(copy, rest));
      }
    }

    return result;
  }


  return addTo([], Array.prototype.slice.call(arguments));
}


>> console.log(cartProd([1,2], [10,20], [100,200,300]));
>> [
     [1, 10, 100], [1, 10, 200], [1, 10, 300], [1, 20, 100], 
     [1, 20, 200], [1, 20, 300], [2, 10, 100], [2, 10, 200], 
     [2, 10, 300], [2, 20, 100], [2, 20, 200], [2, 20, 300]
   ]

mise en oeuvre complète de référence relativement efficace ... :-D 

sur l'efficacité: vous pourriez en gagner en éliminant le si de la boucle et en ayant 2 boucles distinctes, puisque c'est techniquement constant et que vous aideriez avec la prédiction de branche et tout ce bazar, mais ce point est un peu discutable en javascript

n'importe qui, profitez -ck

27
ckozl

Voici une solution récursive simple et sans fantaisie:

function cartesianProduct(a) { // a = array of array
    var i, j, l, m, a1, o = [];
    if (!a || a.length == 0) return a;

    a1 = a.splice(0, 1)[0]; // the first array of a
    a = cartesianProduct(a);
    for (i = 0, l = a1.length; i < l; i++) {
        if (a && a.length) for (j = 0, m = a.length; j < m; j++)
            o.Push([a1[i]].concat(a[j]));
        else
            o.Push([a1[i]]);
    }
    return o;
}

console.log(cartesianProduct([[1,2], [10,20], [100,200,300]]));
// [[1,10,100],[1,10,200],[1,10,300],[1,20,100],[1,20,200],[1,20,300],[2,10,100],[2,10,200],[2,10,300],[2,20,100],[2,20,200],[2,20,300]]
18
sebnukem

La fonction efficace generator suivante renvoie le produit cartésien de tous les iterables donnés:

// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
  const remainder = tail.length > 0 ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

// Example:
console.log(...cartesian([1, 2], [10, 20], [100, 200, 300]));

Il accepte les tableaux, les chaînes, les ensembles et tous les autres objets implémentant le protocole iterable .

Suivant les spécifications du produit cartésien n-ary , il donne

  • [] si un ou plusieurs itérables donnés sont vides, par ex. [] ou ''
  • [[a]] si un seul itératif contenant une seule valeur a est donné.

Tous les autres cas sont traités comme prévu, comme le démontrent les cas de test suivants:

// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
  const remainder = tail.length > 0 ? cartesian(...tail) : [[]];
  for (let r of remainder) for (let h of head) yield [h, ...r];
}

// Test cases:
console.log([...cartesian([])]);              // []
console.log([...cartesian([1])]);             // [[1]]
console.log([...cartesian([1, 2])]);          // [[1], [2]]

console.log([...cartesian([1], [])]);         // []
console.log([...cartesian([1, 2], [])]);      // []

console.log([...cartesian([1], [2])]);        // [[1, 2]]
console.log([...cartesian([1], [2], [3])]);   // [[1, 2, 3]]
console.log([...cartesian([1, 2], [3, 4])]);  // [[1, 3], [2, 3], [1, 4], [2, 4]]

console.log([...cartesian('')]);              // []
console.log([...cartesian('ab', 'c')]);       // [['a','c'], ['b', 'c']]
console.log([...cartesian([1, 2], 'ab')]);    // [[1, 'a'], [2, 'a'], [1, 'b'], [2, 'b']]

console.log([...cartesian(new Set())]);       // []
console.log([...cartesian(new Set([1]))]);    // [[1]]
console.log([...cartesian(new Set([1, 1]))]); // [[1]]

16
le_m

En utilisant un retour arrière typique avec les générateurs ES6,

function cartesianProduct(...arrays) {
  let current = new Array(arrays.length);
  return (function* backtracking(index) {
    if(index == arrays.length) yield current.slice();
    else for(let num of arrays[index]) {
      current[index] = num;
      yield* backtracking(index+1);
    }
  })(0);
}
for (let item of cartesianProduct([1,2],[10,20],[100,200,300])) {
  console.log('[' + item.join(', ') + ']');
}
div.as-console-wrapper { max-height: 100%; }

Vous trouverez ci-dessous une version similaire compatible avec les anciens navigateurs.

function cartesianProduct(arrays) {
  var result = [],
      current = new Array(arrays.length);
  (function backtracking(index) {
    if(index == arrays.length) return result.Push(current.slice());
    for(var i=0; i<arrays[index].length; ++i) {
      current[index] = arrays[index][i];
      backtracking(index+1);
    }
  })(0);
  return result;
}
cartesianProduct([[1,2],[10,20],[100,200,300]]).forEach(function(item) {
  console.log('[' + item.join(', ') + ']');
});
div.as-console-wrapper { max-height: 100%; }

9
Oriol

Voici un moyen récursif qui utilise un ECMAScript 2015 fonction de générateur } afin que vous n'ayez pas à créer tous les nuplets à la fois:

function* cartesian() {
    let arrays = arguments;
    function* doCartesian(i, prod) {
        if (i == arrays.length) {
            yield prod;
        } else {
            for (let j = 0; j < arrays[i].length; j++) {
                yield* doCartesian(i + 1, prod.concat([arrays[i][j]]));
            }
        }
    }
    yield* doCartesian(0, []);
}

console.log(JSON.stringify(Array.from(cartesian([1,2],[10,20],[100,200,300]))));
console.log(JSON.stringify(Array.from(cartesian([[1],[2]],[10,20],[100,200,300]))));

9
heenenee

Ceci est une solution pure ES6 utilisant les fonctions de flèche

function cartesianProduct(arr) {
  return arr.reduce((a, b) =>
    a.map(x => b.map(y => x.concat(y)))
    .reduce((a, b) => a.concat(b), []), [[]]);
}

var arr = [[1, 2], [10, 20], [100, 200, 300]];
console.log(JSON.stringify(cartesianProduct(arr)));

7
Christopher Moore

Une version de coffeescript avec lodash:

_ = require("lodash")
cartesianProduct = ->
    return _.reduceRight(arguments, (a,b) ->
        _.flatten(_.map(a,(x) -> _.map b, (y) -> x.concat(y)), true)
    , [ [] ])
6
dsummersl

Une approche en une seule ligne, pour une meilleure lecture avec des empreintes.

result = data.reduce(
    (a, b) => a.reduce(
        (r, v) => r.concat(b.map(w => [].concat(v, w))),
        []
    )
);

Il faut un seul tableau avec des tableaux d'éléments cartésiens recherchés.

var data = [[1, 2], [10, 20], [100, 200, 300]],
    result = data.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));

console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

3
Nina Scholz

Certaines réponses sous cette rubrique échouent lorsqu'un des tableaux d'entrée contient un élément de tableau. Tu ferais mieux de vérifier ça.

En tout cas pas besoin de soulignement, lodash que ce soit. Je crois que celui-ci devrait le faire avec du JS ES6 pur, aussi fonctionnel que possible.

Ce morceau de code utilise une carte réduite et une carte imbriquée, simplement pour obtenir le produit cartésien de deux tableaux, mais le second tableau provient d'un appel récursif à la même fonction avec un tableau de moins; par conséquent .. a[0].cartesian(...a.slice(1))

Array.prototype.cartesian = function(...a){
  return a.length ? this.reduce((p,c) => (p.Push(...a[0].cartesian(...a.slice(1)).map(e => a.length > 1 ? [c,...e] : [c,e])),p),[])
                  : this;
};

var arr = ['a', 'b', 'c'],
    brr = [1,2,3],
    crr = [[9],[8],[7]];
console.log(JSON.stringify(arr.cartesian(brr,crr))); 

3
Redu

Dans mon contexte particulier, l'approche "à l'ancienne" semblait être plus efficace que les méthodes basées sur des fonctionnalités plus modernes. Vous trouverez ci-dessous le code (incluant une petite comparaison avec d'autres solutions publiées dans ce fil par @rsp et @sebnukem) si cela s'avérait utile également pour quelqu'un d'autre.

L'idée suit. Supposons que nous construisons le produit extérieur de tableaux N, a_1,...,a_N, chacun d'entre eux ayant des composants m_i. Le produit extérieur de ces tableaux a des éléments M=m_1*m_2*...*m_N et nous pouvons identifier chacun d'eux avec un vecteur N-dimensionnel dont les composants sont des entiers positifs et i- ème composant est strictement lié d'en haut par m_i. Par exemple, le vecteur (0, 0, ..., 0) correspond à la combinaison particulière dans laquelle on prend le premier élément de chaque tableau, alors que (m_1-1, m_2-1, ..., m_N-1) est identifié à la combinaison dans laquelle on prend le dernier élément de chaque tableau. Ainsi, afin de construire toutes les combinaisons M, la fonction ci-dessous construit consécutivement tous ces vecteurs et identifie pour chacun d'eux la combinaison correspondante des éléments des matrices d'entrée.

function cartesianProduct(){
    const N = arguments.length;

    var arr_lengths = Array(N);
    var digits = Array(N);
    var num_tot = 1;
    for(var i = 0; i < N; ++i){
        const len = arguments[i].length;
        if(!len){
            num_tot = 0;
            break;
        }
        digits[i] = 0;
        num_tot *= (arr_lengths[i] = len);
    }

    var ret = Array(num_tot);
    for(var num = 0; num < num_tot; ++num){

        var item = Array(N);
        for(var j = 0; j < N; ++j){ item[j] = arguments[j][digits[j]]; }
        ret[num] = item;

        for(var idx = 0; idx < N; ++idx){
            if(digits[idx] == arr_lengths[idx]-1){
                digits[idx] = 0;
            }else{
                digits[idx] += 1;
                break;
            }
        }
    }
    return ret;
}
//------------------------------------------------------------------------------
let _f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesianProduct_rsp = (a, b, ...c) => b ? cartesianProduct_rsp(_f(a, b), ...c) : a;
//------------------------------------------------------------------------------
function cartesianProduct_sebnukem(a) {
    var i, j, l, m, a1, o = [];
    if (!a || a.length == 0) return a;

    a1 = a.splice(0, 1)[0];
    a = cartesianProduct_sebnukem(a);
    for (i = 0, l = a1.length; i < l; i++) {
        if (a && a.length) for (j = 0, m = a.length; j < m; j++)
            o.Push([a1[i]].concat(a[j]));
        else
            o.Push([a1[i]]);
    }
    return o;
}
//------------------------------------------------------------------------------
const L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const args = [L, L, L, L, L, L];

let fns = {
    'cartesianProduct': function(args){ return cartesianProduct(...args); },
    'cartesianProduct_rsp': function(args){ return cartesianProduct_rsp(...args); },
    'cartesianProduct_sebnukem': function(args){ return cartesianProduct_sebnukem(args); }
};

Object.keys(fns).forEach(fname => {
    console.time(fname);
    const ret = fns[fname](args);
    console.timeEnd(fname);
});

avec node v6.12.2, je reçois les timings suivants:

cartesianProduct: 427.378ms
cartesianProduct_rsp: 1710.829ms
cartesianProduct_sebnukem: 593.351ms
2
ewcz

Pour ceux qui ont besoin de TypeScript (réimplémenté @ réponse de Danny)

/**
 * Calculates "Cartesian Product" sets.
 * @example
 *   cartesianProduct([[1,2], [4,8], [16,32]])
 *   Returns:
 *   [
 *     [1, 4, 16],
 *     [1, 4, 32],
 *     [1, 8, 16],
 *     [1, 8, 32],
 *     [2, 4, 16],
 *     [2, 4, 32],
 *     [2, 8, 16],
 *     [2, 8, 32]
 *   ]
 * @see https://stackoverflow.com/a/36234242/1955709
 * @see https://en.wikipedia.org/wiki/Cartesian_product
 * @param arr {T[][]}
 * @returns {T[][]}
 */
function cartesianProduct<T> (arr: T[][]): T[][] {
  return arr.reduce((a, b) => {
    return a.map(x => {
      return b.map(y => {
        return x.concat(y)
      })
    }).reduce((c, d) => c.concat(d), [])
  }, [[]] as T[][])
}
1
artnikpro

Ceci est marqué functional-programming alors jetons un coup d'œil au List monad :

Une application pour cette liste monadique représente le calcul non déterministe. List peut contenir les résultats pour tous les chemins d'exécution dans un algorithme ...

Cela ressemble à un parfait digne de cartesian. JavaScript nous donne Array et la fonction de liaison monadique est Array.prototype.flatMap, alors mettons-les à utiliser -

const cartesian = (...all) =>
{ const loop = (t, a, ...more) =>
    a === undefined
      ? [ t ]
      : a .flatMap (x => loop ([ ...t, x ], ...more))
  return loop ([], ...all)
}

console .log (cartesian ([1,2], [10,20], [100,200,300]))

Au lieu de loop ci-dessus, t peut être ajouté en tant que paramètre au curry -

const makeCartesian = (t = []) => (a, ...more) =>
  a === undefined
    ? [ t ]
    : a .flatMap (x => makeCartesian ([ ...t, x ]) (...more))

const cartesian =
  makeCartesian ()

console .log (cartesian ([1,2], [10,20], [100,200,300]))
1
user633183

Juste pour faire un choix, une implémentation réelle et simple utilisant la variable reduce de array:

const array1 = ["day", "month", "year", "time"];
const array2 = ["from", "to"];
const process = (one, two) => [one, two].join(" ");

const product = array1.reduce((result, one) => result.concat(array2.map(two => process(one, two))), []);
1
Simple.Js

JavaScript moderne en quelques lignes. Aucune bibliothèque ou dépendance externe comme Lodash.

function cartesian(...arrays) {
  return arrays.reduce((a, b) => a.flatMap(x => b.map(y => x.concat([y]))), [ [] ]);
}

console.log(
  cartesian([1, 2], [10, 20], [100, 200, 300])
    .map(arr => JSON.stringify(arr))
    .join('\n')
);
1
Miles Elam

Une approche non récursive qui ajoute la possibilité de filtrer et de modifier les produits avant de les ajouter au jeu de résultats. Notez l'utilisation de .map plutôt que .forEach. Dans certains navigateurs, .map s'exécute plus rapidement.

function crossproduct(arrays,rowtest,rowaction) {
      // Calculate the number of elements needed in the result
      var result_elems = 1, row_size = arrays.length;
      arrays.map(function(array) {
            result_elems *= array.length;
      });
      var temp = new Array(result_elems), result = [];

      // Go through each array and add the appropriate element to each element of the temp
      var scale_factor = result_elems;
      arrays.map(function(array)
      {
        var set_elems = array.length;
        scale_factor /= set_elems;
        for(var i=result_elems-1;i>=0;i--) {
            temp[i] = (temp[i] ? temp[i] : []);
            var pos = i / scale_factor % set_elems;
            // deal with floating point results for indexes, this took a little experimenting
            if(pos < 1 || pos % 1 <= .5) {
                pos = Math.floor(pos);
            } else {
                pos = Math.min(array.length-1,Math.ceil(pos));
            }
            temp[i].Push(array[pos]);
            if(temp[i].length===row_size) {
                var pass = (rowtest ? rowtest(temp[i]) : true);
                if(pass) {
                    if(rowaction) {
                        result.Push(rowaction(temp[i]));
                    } else {
                        result.Push(temp[i]);
                    }
                }
            }
        }
      });
      return result;
    }
1
AnyWhichWay

Une version simple et modifiée du code de @ viebel en Javascript simple:

function cartesianProduct(...arrays) {
  return arrays.reduce((a, b) => {
    return [].concat(...a.map(x => {
      const next = Array.isArray(x) ? x : [x];
      return [].concat(b.map(y => next.concat(...[y])));
    }));
  });
}

const product = cartesianProduct([1, 2], [10, 20], [100, 200, 300]);

console.log(product);
/*
[ [ 1, 10, 100 ],
  [ 1, 10, 200 ],
  [ 1, 10, 300 ],
  [ 1, 20, 100 ],
  [ 1, 20, 200 ],
  [ 1, 20, 300 ],
  [ 2, 10, 100 ],
  [ 2, 10, 200 ],
  [ 2, 10, 300 ],
  [ 2, 20, 100 ],
  [ 2, 20, 200 ],
  [ 2, 20, 300 ] ];
*/
0
Juan S. Gaitán V.

Vous pourriez reduce le tableau 2D. Utilisez flatMap sur le tableau accumulateur pour obtenir acc.length x curr.length nombre de combinaisons dans chaque boucle. [].concat(c, n) est utilisé car c est un nombre dans la première itération et un tableau par la suite.

const data = [ [1, 2], [10, 20], [100, 200, 300] ];

const output = data.reduce((acc, curr, i) =>
  acc.flatMap(c => curr.map(n => [].concat(c, n)))
)

console.log(JSON.stringify(output))

(Ceci est basé sur réponse de Nina Scholz )

0
adiga

J'ai remarqué que personne n'a publié de solution permettant de passer une fonction pour traiter chaque combinaison.

const _ = require('lodash')

function combinations(arr, f, xArr = []) {
    return arr.length>1 
    ? _.flatMap(arr[0], x => combinations(arr.slice(1), f, xArr.concat(x)))
    : arr[0].map(x => f(...xArr.concat(x)))
}

// use case
const greetings = ["Hello", "Goodbye"]
const places = ["World", "Planet"]
const punctuationMarks = ["!", "?"]
combinations([greetings,places,punctuationMarks], (greeting, place, punctuationMark) => `${greeting} ${place}${punctuationMark}`)
  .forEach(row => console.log(row))

Sortie:

Hello World!
Hello World?
Hello Planet!
Hello Planet?
Goodbye World!
Goodbye World?
Goodbye Planet!
Goodbye Planet?
0
Lezorte

Méthode simple de force brute JS qui prend un tableau de tableaux en entrée.

var cartesian = function(arrays) {
    var product = [];
    var precals = [];
    var length = arrays.reduce(function(acc, curr) {
        return acc * curr.length
    }, 1);
    for (var i = 0; i < arrays.length; i++) {
        var array = arrays[i];
        var mod = array.length;
        var div = i > 0 ? precals[i - 1].div * precals[i - 1].mod : 1;
        precals.Push({
            div: div,
            mod: mod
        });
    }
    for (var j = 0; j < length; j++) {
        var item = [];
        for (var i = 0; i < arrays.length; i++) {
            var array = arrays[i];
            var precal = precals[i];
            var k = (~~(j / precal.div)) % precal.mod;
            item.Push(array[k]);
        }
        product.Push(item);
    }
    return product;
};

cartesian([
    [1],
    [2, 3]
]);

cartesian([
    [1],
    [2, 3],
    [4, 5, 6]
]);
0
Samuel Ventura

Encore une autre implémentation. Pas le plus court ou chic, mais rapide:

function cartesianProduct() {
    var arr = [].slice.call(arguments),
        intLength = arr.length,
        arrHelper = [1],
        arrToReturn = [];

    for (var i = arr.length - 1; i >= 0; i--) {
        arrHelper.unshift(arrHelper[0] * arr[i].length);
    }

    for (var i = 0, l = arrHelper[0]; i < l; i++) {
        arrToReturn.Push([]);
        for (var j = 0; j < intLength; j++) {
            arrToReturn[i].Push(arr[j][(i / arrHelper[j + 1] | 0) % arr[j].length]);
        }
    }

    return arrToReturn;
}
0
flare256

Aucune bibliothèque nécessaire! :)

Needs arrow fonctionne bien et probablement pas si efficace. : /

const flatten = (xs) => 
    xs.flat(Infinity)

const binaryCartesianProduct = (xs, ys) =>
    xs.map((xi) => ys.map((yi) => [xi, yi])).flat()

const cartesianProduct = (...xss) =>
    xss.reduce(binaryCartesianProduct, [[]]).map(flatten)
      
console.log(cartesianProduct([1,2,3], [1,2,3], [1,2,3]))
0
Chris Vouga

var chars = ['A', 'B', 'C']
var nums = [1, 2, 3]

var cartesianProduct = function() {
  return _.reduce(arguments, function(a, b) {
    return _.flatten(_.map(a, function(x) {
      return _.map(b, function(y) {
        return x.concat(y);
      });
    }), true);
  }, [
    []
  ]);
};

console.log(cartesianProduct(chars, nums))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Just converted @dummersl's answer from CoffeScript to JavaScript. It just works.

var chars = ['A', 'B', 'C']
var nums = [1, 2, 3]

var cartesianProduct = function() {
  return _.reduce(arguments, function(a, b) {
    return _.flatten(_.map(a, function(x) {
      return _.map(b, function(y) {
        return x.concat(y);
      });
    }), true);
  }, [[]]);
};

console.log( cartesianProduct(chars, nums) )
0
guneysus

Une solution simple "d'esprit et visuellement conviviale".

 enter image description here


// t = [i, length]

const moveThreadForwardAt = (t, tCursor) => {
  if (tCursor < 0)
    return true; // reached end of first array

  const newIndex = (t[tCursor][0] + 1) % t[tCursor][1];
  t[tCursor][0] = newIndex;

  if (newIndex == 0)
    return moveThreadForwardAt(t, tCursor - 1);

  return false;
}

const cartesianMult = (...args) => {
  let result = [];
  const t = Array.from(Array(args.length)).map((x, i) => [0, args[i].length]);
  let reachedEndOfFirstArray = false;

  while (false == reachedEndOfFirstArray) {
    result.Push(t.map((v, i) => args[i][v[0]]));

    reachedEndOfFirstArray = moveThreadForwardAt(t, args.length - 1);
  }

  return result;
}

// cartesianMult(
//   ['a1', 'b1', 'c1'],
//   ['a2', 'b2'],
//   ['a3', 'b3', 'c3'],
//   ['a4', 'b4']
// );

console.log(cartesianMult(
  ['a1'],
  ['a2', 'b2'],
  ['a3', 'b3']
));
0
zero.zero.seven

Pour le compte rendu

Voici ma version de celui-ci. Je l'ai fait en utilisant le plus simple des javascript, "pour ()", de sorte qu'il soit compatible dans tous les cas et qu'il offre les meilleures performances.

function cartesian(arrays){
    var quant = 1, counters = [], retArr = [];

    // Counts total possibilities and build the counters Array;
    for(var i=0;i<arrays.length;i++){
        counters[i] = 0;
        quant *= arrays[i].length;
    }

    // iterate all possibilities
    for(var i=0,nRow;i<quant;i++){
        nRow = [];
        for(var j=0;j<counters.length;j++){
            if(counters[j] < arrays[j].length){
                nRow.Push(arrays[j][counters[j]]);
            } else { // in case there is no such an element it restarts the current counter
                counters[j] = 0;
                nRow.Push(arrays[j][counters[j]]);
            }
            counters[j]++;
        }
        retArr.Push(nRow);
    }
    return retArr;
}

Meilleures salutations.

0
LeandroP