Comment implémenteriez-vous le produit cartésien de plusieurs tableaux en JavaScript?
Par exemple,
cartesian([1, 2], [10, 20], [100, 200, 300])
devrait revenir
[
[1, 10, 100],
[1, 10, 200],
[1, 10, 300],
[2, 10, 100],
[2, 10, 200]
...
]
d3.cross(a, b[, reducer])
en février. github.com/d3/d3-array#crossRéponses:
Mise à jour 2017: réponse en 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 juste deux lignes de JavaScript vanilla , pas de lodash, de trait de soulignement ou d'autres bibliothèques:
Mettre à jour:
Ceci est le même que ci-dessus mais amélioré pour suivre strictement le guide de style JavaScript Airbnb - validé avec ESLint avec eslint-config-airbnb-base :
Un merci spécial à ZuBB pour m'avoir informé des problèmes de linter avec le code d'origine.
Exemple
Voici l'exemple exact de votre question:
Production
Voici le résultat de cette commande:
Démo
Voir les démos sur:
Syntaxe
La syntaxe que j'ai utilisée ici n'a rien de nouveau. Mon exemple utilise l'opérateur de propagation et les paramètres rest - des fonctionnalités de JavaScript définies dans la 6e édition de la norme ECMA-262 publiée en juin 2015 et développée beaucoup plus tôt, mieux connue sous le nom d'ES6 ou ES2015. Voir:
Cela rend un code comme celui-ci si simple que c'est un péché de ne pas l'utiliser. Pour les anciennes plates-formes qui ne le prennent pas en charge de manière native, vous pouvez toujours utiliser Babel ou d'autres outils pour le transpiler dans une syntaxe plus ancienne - et en fait mon exemple transpilé par Babel est toujours plus court et plus simple que la plupart des exemples ici, mais ce n'est pas le cas. 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 à maintenir et il n'est pas nécessaire d'utiliser des bibliothèques entières pour une chose aussi simple, quand deux lignes de JavaScript vanilla peuvent facilement faire le travail. Comme vous pouvez le voir, il est vraiment avantageux d'utiliser les fonctionnalités modernes du langage et dans les cas où vous devez prendre en charge des plates-formes archaïques sans support natif des fonctionnalités modernes, vous pouvez toujours utiliser Babel ou d'autres outils pour transposer la nouvelle syntaxe à l'ancienne. .
Ne codez pas comme si c'était 1995
JavaScript évolue et il le fait pour une raison. TC39 fait un travail incroyable de conception du langage en ajoutant de nouvelles fonctionnalités et les fournisseurs de navigateurs font un travail incroyable pour implémenter ces fonctionnalités.
Pour voir l'état actuel de la prise en charge native d'une fonctionnalité donnée dans les navigateurs, consultez:
Pour voir la prise en charge dans les versions Node, voir:
Pour utiliser la syntaxe moderne sur les plates-formes qui ne la prennent pas en charge de manière native, utilisez Babel:
la source
a
et 2 variables localesb
)['a', 'b'], [1,2], [[9], [10]]
ce 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 ] ]
en conséquence. Je veux dire ne conservera pas le type d'articles[[9], [10]]
....
déjà, ne devrait pas[].concat(...[array])
devenir simplement[...array]
?Voici une solution fonctionnelle au problème (sans aucune variable mutable !) En utilisant
reduce
etflatten
, fourni parunderscore.js
:Remarque: Cette solution a été inspirée par http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/
la source
flatten
est de rendre l'aplatissement superficiel. C'est obligatoire ici!true
avec un trait de soulignement et utilisezfalse
avec du lodash pour assurer un aplatissement peu profond.Voici une version modifiée du code de @ viebel en Javascript brut, sans utiliser de bibliothèque:
la source
.concat(y)
au lieu de.concat([ y ])
il semble que la communauté pense que c'est trivial et / ou facile de 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 de type salle de classe de toute façon c'est votre jour de chance :
implémentation de référence complète qui est relativement efficace ... :-D
sur l'efficacité: vous pourriez en gagner en retirant le if de la boucle et en ayant 2 boucles séparées car il est techniquement constant et vous aideriez à la prédiction de branche et tout ce désordre, mais ce point est un peu discutable en javascript
anywho, appréciez -ck
la source
reduce
fonction de tableau?result = result.concat(...)
et en n'utilisant pasargs.slice(1)
. Malheureusement, je n'ai pas pu trouver un moyen de me débarrasser decurr.slice()
la récursivité.La fonction de générateur efficace suivante renvoie le produit cartésien de tous les itérables donnés :
Il accepte les tableaux, les chaînes, les ensembles et tous les autres objets implémentant le protocole itérable .
Suite à la spécification du produit cartésien n-aire qu'il donne
[]
si un ou plusieurs itérables donnés sont vides, par exemple[]
ou''
[[a]]
si un seul itérable contenant une seule valeura
est donné.Tous les autres cas sont traités comme prévu, comme le montrent les cas de test suivants:
Afficher l'extrait de code
la source
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 une solution récursive simple et simple:
la source
Voici une méthode récursive qui utilise une fonction de générateur ECMAScript 2015 pour que vous n'ayez pas à créer tous les tuples à la fois:
la source
cartesian([[1],[2]],[10,20],[100,200,300])
.concat()
opérateur de diffusion intégré peut parfois devenir trompeur.Voici un one-liner utilisant l'ES2019 natif
flatMap
. Aucune bibliothèque nécessaire, juste un navigateur moderne (ou transpilateur):C'est essentiellement une version moderne de la réponse de viebel, sans lodash.
la source
En utilisant un retour en arrière typique avec des générateurs ES6,
Ci-dessous, une version similaire compatible avec les anciens navigateurs.
Afficher l'extrait de code
la source
Il s'agit d'une solution ES6 pure utilisant des fonctions fléchées
la source
Une version coffeescript avec lodash:
la source
Une approche sur une seule ligne, pour une meilleure lecture avec des indentations.
Il prend un seul tableau avec des tableaux d'éléments cartésiens voulus.
la source
if (arr.length === 1) return arr[0].map(el => [el]);
Ceci est étiqueté programmation fonctionnelle, alors jetons un coup d'œil à la monade List :
Eh bien, cela semble être un ajustement parfait pour
cartesian
. JavaScript nous donneArray
et la fonction de liaison monadique estArray.prototype.flatMap
, alors mettons-les à profit -Au lieu de
loop
ci-dessus,t
peut être ajouté en tant que paramètre curry -la source
Quelques-unes des réponses sous cette rubrique échouent lorsque l'un des tableaux d'entrée contient un élément de tableau. Vous feriez mieux de vérifier cela.
Quoi qu'il en soit, pas besoin de soulignement, de lodash que ce soit. Je pense que celui-ci devrait le faire avec du JS ES6 pur, aussi fonctionnel que possible.
Ce morceau de code utilise une réduction et une carte imbriquée, simplement pour obtenir le produit cartésien de deux tableaux, mais le deuxième 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))
la source
Dans mon contexte particulier, l'approche «à l'ancienne» m'a semblé plus efficace que les méthodes basées sur des fonctionnalités plus modernes. Vous trouverez ci-dessous le code (y compris une petite comparaison avec d'autres solutions publiées dans ce fil par @rsp et @sebnukem) si cela s'avère également utile à quelqu'un d'autre.
L'idée suit. Disons que nous construisons le produit externe de
N
tableaux, donta_1,...,a_N
chacun a desm_i
composants. Le produit extérieur de ces tableaux a desM=m_1*m_2*...*m_N
éléments et nous pouvons identifier chacun d'eux avec unN-
vecteur dimensionnel dont les composants sont des entiers positifs et lei
-ème composant est strictement délimité par le haut parm_i
. Par exemple, le vecteur(0, 0, ..., 0)
correspondrait à la combinaison particulière dans laquelle on prend le premier élément de chaque tableau, tandis qu'il(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 toutM
combinaisons de , la fonction ci-dessous construit consécutivement tous ces vecteurs et pour chacun d'eux identifie la combinaison correspondante des éléments des tableaux d'entrée.avec
node v6.12.2
, j'obtiens les horaires suivants:la source
Pour ceux qui ont besoin de TypeScript (réimplémentation de la réponse de @ Danny)
la source
Juste pour un choix une implémentation vraiment simple utilisant des tableaux
reduce
:la source
JavaScript moderne en quelques lignes. Pas de bibliothèques externes ou de dépendances comme Lodash.
la source
Vous pourriez
reduce
le tableau 2D. UtilisezflatMap
sur le tableau d'accumulateurs pour obtenir leacc.length x curr.length
nombre de combinaisons dans chaque boucle.[].concat(c, n)
est utilisé car ilc
s'agit d'un nombre dans la première itération et d'un tableau après.(Ceci est basé sur la réponse de Nina Scholz )
la source
Une approche non récursive qui ajoute la possibilité de filtrer et de modifier les produits avant de les ajouter réellement à l'ensemble de résultats. Notez l'utilisation de .map plutôt que de .forEach. Dans certains navigateurs, .map est plus rapide.
la source
Une solution simple "esprit et visuellement".
la source
Une version simple et modifiée du code de @ viebel en Javascript brut:
la source
Une implémentation plus lisible
la source
Ceci est pour 3 tableaux.
Certaines réponses ont donné un moyen pour n'importe quel nombre de tableaux.
Cela peut facilement se contracter ou s'étendre à moins ou plus de baies.
J'avais besoin de combinaisons d'un ensemble avec des répétitions, alors j'aurais pu utiliser:
mais utilisé:
la source
J'ai remarqué que personne n'a posté de solution permettant de passer une fonction pour traiter chaque combinaison, voici donc ma solution:
Production:
la source
Approche de force brute JS simple qui prend un tableau de tableaux en entrée.
la source
Je viens de convertir la réponse de @ dummersl de CoffeScript en JavaScript. Cela fonctionne juste.
la source
Encore une autre mise en œuvre. Pas le plus court ou le plus sophistiqué, mais rapide:
la source
Aucune bibliothèque nécessaire! :)
Nécessite des fonctions de flèche et probablement pas aussi efficaces. : /
la source
Pour la petite histoire
Voici ma version de celui-ci. Je l'ai fait en utilisant l'itérateur javascript le plus simple "for ()", il est donc compatible dans tous les cas et offre les meilleures performances.
Meilleures salutations.
la source