Produit cartésien de plusieurs tableaux en JavaScript
Comment voulez-vous mettre en œuvre le produit Cartésien de plusieurs tableaux en JavaScript?
Comme un exemple,
cartesian([1, 2], [10, 20], [100, 200, 300])
doit retourner
[
[1, 10, 100],
[1, 10, 200],
[1, 10, 300],
[2, 10, 100],
[2, 10, 200]
...
]
- double possible de retrouvez toutes les combinaisons d'options dans une boucle
- Cette mise en œuvre dans le js-combinatoire module: github.com/dankogai/js-combinatorics
- double possible de la Génération de combinaisons de n matrices de m éléments
- fixe. merci @le_m
- Je suis d'accord sur underscore.js mais je ne suis pas sûr que je vois comment enlever fonctionnelle de la programmation de la balise aidera @le_m
- Fwiw, d3 ajouté
d3.cross(a, b[, reducer])
en février. github.com/d3/d3-array#cross
Vous devez vous connecter pour publier un commentaire.
Ici est une fonctionnelle de la solution pour le problème (sans mutable variable!) à l'aide de
reduce
etflatten
, fournis parunderscore.js
:JS:
HTML:
Remarque: Cette solution a été inspiré par http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/
flatten
est de faire la mise à plat peu profond. Il est obligatoire ici!true
avec trait de soulignement et utiliserfalse
avec lodash pour vous assurer superficielle de l'aplatissement.2017 mise à Jour: 2-ligne de la réponse à la vanille JS
Toutes les réponses ici sont trop compliqué, plus de 20 lignes de code ou même plus.
Cet exemple utilise seulement deux lignes de vanille JavaScript, pas de lodash, le trait de soulignement ou d'autres bibliothèques:
Mise à jour:
C'est le même que ci-dessus, mais l'amélioration de suivre strictement le Airbnb JavaScript Guide De Style validées à l'aide de ESLint avec eslint-config-airbnb-base:
Un merci spécial à ZuBB pour me laisser savoir à propos de linter des problèmes avec le code d'origine.
Exemple
C'est exactement l'exemple de votre question:
Sortie
C'est la sortie de cette commande:
Démo
Voir les démos sur:
Syntaxe
La syntaxe que j'ai utilisé ici n'est rien de nouveau.
Mon exemple utilise la propagation de l'opérateur et le reste des paramètres - les fonctionnalités de JavaScript définies dans la 6e édition de la norme ECMA-262 publiée en juin 2015 et s'est développée beaucoup plus tôt, mieux connu sous le nom ES6 ou ES2015. Voir:
Il rend le code comme ce si simple que c'est un péché de ne pas l'utiliser. Pour les anciennes plates-formes, qui ne prennent pas en charge en mode natif, vous pouvez toujours utiliser Babel ou d'autres outils pour transpile il à l'ancienne syntaxe - et, en fait, mon exemple transpiled par Babel est encore plus court et plus simple que la plupart des exemples ici, mais il n'a pas vraiment d'importance parce que la sortie de transpilation n'est pas quelque chose que vous avez besoin de comprendre ou de les maintenir, c'est juste un fait que je trouve intéressant.
Conclusion
Il n'y a pas besoin d'écrire des centaines de lignes de code qui est difficile à maintenir et il n'est pas nécessaire d'utiliser de bibliothèques entières, pour une chose si simple, lorsque deux lignes de vanille JavaScript peut facilement faire le travail. Comme vous pouvez le voir c'est vraiment rentable d'utiliser des fonctions de la langue et dans le cas où vous avez besoin de soutien archaïque plates-formes avec pas de support natif des fonctionnalités modernes, vous pouvez toujours utiliser Babel ou d'autres outils pour transpile la nouvelle syntaxe de l'ancien.
Ne pas un code comme il est 1995
JavaScript évolue et il le fait pour une raison. TC39 fait un travail incroyable de la langue de conception avec l'ajout de nouvelles fonctionnalités et le navigateur les fournisseurs font un travail remarquable de mise en œuvre de ces fonctionnalités.
Pour voir l'état actuel de la prise en charge native de toute fonction dans les navigateurs, voir:
De voir le soutien du Nœud versions, voir:
À utiliser la syntaxe moderne sur les plates-formes qui ne supportent pas nativement, l'utilisation Babel:
a
et 2 locaux varsb
)['a', 'b'], [1,2], [[9], [10]]
qui donnera[ [ 'a', 1, 9 ], [ 'a', 1, 10 ], [ 'a', 2, 9 ], [ 'a', 2, 10 ], [ 'b', 1, 9 ], [ 'b', 1, 10 ], [ 'b', 2, 9 ], [ 'b', 2, 10 ] ]
comme un résultat. Je veux dire, ne pas garder le type des éléments de[[9], [10]]
.cartesian(['a'], ['b'])
sera de retour[[ 'a', 'b']]
, maiscartesian([['a']])
sera de retour['a']
cartesian()
devrait être[]
, pas indéfini. (Un peu comme un vide somme est égale à zéro.)...
déjà, ne devrait pas[].concat(...[array])
simplement devenir[...array]
?Voici une version modifiée de @viebel du code dans la plaine, Javascript, sans utiliser de bibliothèque:
JS:
.concat(y)
au lieu de.concat([ y ])
il semble que la communauté pense de ce qui est insignifiant et ou facile de trouver une implémentation de référence, lors de la brève inspection, je ne pouvais pas ou peut-être c'est juste que j'aime ré-inventer la roue ou de la résolution de la salle de classe-comme les problèmes de programmation de toute façon c'est votre jour de chance:
référence complète de mise en œuvre qui est relativement efficace... 😀
sur l'efficacité: vous pourriez gagner quelques en prenant le cas de la boucle et avoir 2 boucles séparées, car il est techniquement constante et vous aurait aider avec la direction de la prévision et de tout ce gâchis, mais ce point est une sorte de plaidoirie en javascript
anywho, profitez -ck
reduce
fonction de tableau?result = result.concat(...)
et en n'utilisant pasargs.slice(1)
. Malheureusement, je n'étais pas en mesure de trouver un moyen de se débarrasser decurr.slice()
et la récursivité.Suivantes efficace générateur de fonction retourne le produit cartésien de toutes données iterables:
JS:
Il accepte tableaux, chaînes de caractères, les décors et tous les autres objets de la mise en œuvre de la itérable protocole.
Suivant la spécification de la n-ary produit cartésien il rendements
[]
si un ou plusieurs iterables sont vides, par exemple[]
ou''
[[a]]
si un seul objet iterable contenant une seule valeura
est donné.Tous les autres cas sont traités comme prévu, comme l'ont démontré par la suite de cas de test:
JS:
function* cartesian(head, ...tail) { for (let h of head) { const remainder = tail.length > 0 ? cartesian(...tail) : [[]]; for (let r of remainder) yield [h, ...r] } }
Voici un non-fantaisie, simple solution récursive:
JS:
Ici est la façon récursive qui utilise un ECMAScript 2015 générateur de fonction de sorte que vous n'avez pas à créer l'ensemble des n-uplets à la fois:
JS:
cartesian([[1],[2]],[10,20],[100,200,300])
.concat()
construit dans la propagation de l'opérateur peut parfois devenir trompeuse.À l'aide d'un typique retour en arrière avec ES6 générateurs,
JS:
CSS:
Ci-dessous il y a une version similaire compatible avec les anciens navigateurs.
JS:
CSS:
C'est un pur ES6 solution à l'aide de flèche fonctions
JS:
Un coffeescript version avec lodash:
Une seule ligne d'approche, pour un meilleur confort de lecture avec des indentations.
Il prend un seul tableau avec des tableaux de voulu cartésien éléments.
JS:
CSS:
if (arr.length === 1) return arr[0].map(el => [el]);
C'est taggés fonctionnelle de la programmation donc, nous allons jeter un oeil à la Liste monade:
Bien que cela sonne comme un parfait ajustement pour
cartesian
. JavaScript nous donneArray
et la monadique la fonction de liaison estArray.prototype.flatMap
, nous allons donc mettre à utiliser -JS:
Au lieu de
loop
ci-dessus,t
peut être ajouté comme un curry de paramètre -JS:
Quelques réponses à ces questions dans cette rubrique échouer lorsque l'un quelconque de l'entrée des tableaux contient un élément de tableau. Vous vous mieux de vérifier qu'.
De toute façon pas besoin de souligner, lodash que ce soit. Je crois que celui-ci devrait le faire avec de la pure JS ES6, aussi fonctionnel qu'il obtient.
Ce morceau de code utilise une réduction et une étude de la carte, il suffit d'obtenir le produit cartésien de deux ensembles toutefois, le deuxième tableau provient d'un appel récursif à la même fonction d'une matrice, d'où..
a[0].cartesian(...a.slice(1))
JS:
Dans mon contexte particulier, la "vieille" approche semble être plus efficace que les méthodes basées sur des plus modernes. Ci-dessous est le code (y compris une petite comparaison avec d'autres solutions postées dans ce fil par @rsp et @sebnukem) doit-elle s'avérer utile à quelqu'un d'autre aussi.
L'idée est la suivante. Disons que nous construisons le produit extérieur de
N
tableaux,a_1,...,a_N
dont chacune am_i
composants. Le produit extérieur de ces tableaux aM=m_1*m_2*...*m_N
éléments et nous pouvons identifier chacun d'eux avec uneN-
dimensions vecteur dont les composantes sont des nombres entiers positifs eti
-ème composante est strictement bornée parm_i
. Par exemple, le vecteur(0, 0, ..., 0)
correspond à la combinaison particulière au sein de laquelle on prend le premier élément de chaque tableau, alors que(m_1-1, m_2-1, ..., m_N-1)
est identifié avec la combinaison où l'on prend le dernier élément de chaque tableau. Ainsi, pour construire tous lesM
combinaisons, la fonction ci-dessous consécutivement construit toutes ces vecteurs et pour chacun d'entre eux identifie le correspondant de la combinaison des éléments de la saisie des tableaux.avec
node v6.12.2
, je reçois suivantes horaires:Un non-récursive approche qui ajoute la possibilité de filtrer et de modifier les produits avant de les ajouter à l'ensemble de résultats. Notez l'utilisation de .la carte plutôt que .forEach. Dans certains navigateurs .la carte s'exécute plus rapidement.
Juste pour un choix d'une réelle simplicité de mise en oeuvre à l'aide de la matrice de
reduce
:Une simple version modifiée de @viebel du code dans la plaine Javascript:
Pour ceux qui les besoins de la Machine (réimplémentée @Danny réponse)
J'ai remarqué que personne n'a posté une solution qui permet à une fonction qui doit être transmis à chaque combinaison, alors voici ma solution:
De sortie:
Plaine JS approche par force brute qui prend un tableau de tableaux en entrée.
Un simple "esprit et visuellement sympathique" solution.
JS:
HTML:
Juste converti @dummersl de l' réponse de CoffeScript de JavaScript. Il fonctionne, tout simplement.
Encore une autre mise en œuvre. Pas le plus court ou de fantaisie, mais rapide:
Pas de bibliothèques nécessaires! 🙂
Besoins flèche fonctions et probablement pas très efficace. :/
JS:
Pour l'enregistrement
Ici, il va de ma version de celui-ci. Je l'ai fait à l'aide de la plus simple javascript itérateur "pour()", il est donc compatible sur tous les cas et a la meilleure performance.
Meilleures salutations.