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]...]
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/
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;
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.
Voici l'exemple exact de votre question:
let output = cartesian([1,2],[10,20],[100,200,300]);
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 ] ]
Voir les démos sur:
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.
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 .
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:
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);
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
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]]
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]]
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%; }
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]))));
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)));
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)
, [ [] ])
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; }
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)));
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
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[][])
}
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]))
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))), []);
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')
);
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;
}
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 ] ];
*/
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 )
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?
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]
]);
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;
}
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]))
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) )
Une solution simple "d'esprit et visuellement conviviale".
// 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']
));
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.