Javascript equivalente de la función zip de Python

¿Hay un equivalente en javascript de la función zip de Python? Es decir, dados los arreglos múltiples de longitudes iguales, se crea un arreglo de pares.

Por ejemplo, si tengo tres matrices que se parecen a esto:

var array1 = [1, 2, 3]; var array2 = ['a','b','c']; var array3 = [4, 5, 6]; 

La matriz de salida debe ser:

 var output array:[[1,'a',4], [2,'b',5], [3,'c',6]] 

Actualización de 2016:

Aquí hay una versión más deslumbrante de Ecmascript 6:

 zip= rows=>rows[0].map((_,c)=>rows.map(row=>row[c])) 

Ilustración equiv. a Python { zip(*args) }:

 > zip([['row0col0', 'row0col1', 'row0col2'], ['row1col0', 'row1col1', 'row1col2']]); [["row0col0","row1col0"], ["row0col1","row1col1"], ["row0col2","row1col2"]] 

(y FizzyTea señala que ES6 tiene una syntax de argumentos variados, por lo que la siguiente definición de función actuará como python, pero vea a continuación el descargo de responsabilidad … esto no será su propio inverso, por lo que zip(zip(x)) no será igual a x ; aunque como Matt Kramer señala zip(...zip(...x))==x (como en el zip(*zip(*x))==x python regular zip(*zip(*x))==x ))

Definición alternativa equiv. a Python { zip }:

 > zip = (...rows) => [...rows[0]].map((_,c) => rows.map(row => row[c])) > zip( ['row0col0', 'row0col1', 'row0col2'] , ['row1col0', 'row1col1', 'row1col2'] ); // note zip(row0,row1), not zip(matrix) same answer as above 

(Tenga en cuenta que la ... syntax puede tener problemas de rendimiento en este momento, y posiblemente en el futuro, por lo que si usa la segunda respuesta con diversos argumentos, es posible que desee realizar una prueba).


Aquí hay un oneliner:

 function zip(arrays) { return arrays[0].map(function(_,i){ return arrays.map(function(array){return array[i]}) }); } // > zip([[1,2],[11,22],[111,222]]) // [[1,11,111],[2,22,222]]] // If you believe the following is a valid return value: // > zip([]) // [] // then you can special-case it, or just do // return arrays.length==0 ? [] : arrays[0].map(...) 

Lo anterior asume que las matrices son de igual tamaño, como deberían ser. También supone que pasas en un solo argumento de lista de listas, a diferencia de la versión de Python, donde la lista de argumentos es variad. Si desea todas estas “características”, vea más abajo. Se necesitan casi 2 líneas adicionales de código.

Lo siguiente imitará el comportamiento zip de Python en casos de borde donde los arreglos no son del mismo tamaño, simulando silenciosamente que las partes más largas de los arreglos no existen:

 function zip() { var args = [].slice.call(arguments); var shortest = args.length==0 ? [] : args.reduce(function(a,b){ return a.length zip([1,2],[11,22],[111,222,333]) // [[1,11,111],[2,22,222]]] // > zip() // [] 

Esto itertools.zip_longest comportamiento de itertools.zip_longest de Python, insertando undefined donde las matrices no están definidas:

 function zip() { var args = [].slice.call(arguments); var longest = args.reduce(function(a,b){ return a.length>b.length ? a : b }, []); return longest.map(function(_,i){ return args.map(function(array){return array[i]}) }); } // > zip([1,2],[11,22],[111,222,333]) // [[1,11,111],[2,22,222],[null,null,333]] // > zip() // [] 

Si utiliza estas dos últimas versiones (variadic aka. Versiones de múltiples argumentos), entonces zip ya no es su propio inverso. Para imitar el lenguaje zip(*[...]) de Python, deberá zip.apply(this, [...]) cuando desee invertir la función zip o si desea tener una variable similar Número de listas como entrada.


adenda

Para hacer esto manejable en cualquier iterable (por ejemplo, en Python puede usar zip en cadenas, rangos, objetos de mapa, etc.), podría definir lo siguiente:

 function iterView(iterable) { // returns an array equivalent to the iterable } 

Sin embargo, si escribe zip de la siguiente manera , incluso eso no será necesario:

 function zip(arrays) { return Array.apply(null,Array(arrays[0].length)).map(function(_,i){ return arrays.map(function(array){return array[i]}) }); } 

Manifestación:

 > JSON.stringify( zip(['abcde',[1,2,3,4,5]]) ) [["a",1],["b",2],["c",3],["d",4],["e",5]] 

(O podría usar un range(...) función de estilo Python si ya lo ha escrito. Eventualmente, podrá usar las integraciones de matriz de ECMAScript o los generadores).

Echa un vistazo a la biblioteca de subrayado .

Underscore proporciona más de 100 funciones que son compatibles con sus ayudantes funcionales favoritos de trabajo: mapear, filtrar, invocar, y también con funciones más especializadas: enlace de funciones, plantillas de javascript, creación de índices rápidos, pruebas de igualdad profunda, etc.

– Decir las personas que lo hicieron.

Recientemente comencé a usarlo específicamente para la función zip() y me ha dejado una gran primera impresión. Estoy usando jQuery y CoffeeScript, y simplemente va perfectamente con ellos. El guión bajo comienza justo donde lo dejaron y hasta ahora no me ha fallado. Ah, por cierto, es sólo 3kb minified.

Echale un vistazo.

Además de la excelente y completa respuesta de ninjagecko, todo lo que se necesita para comprimir dos matrices JS en una “imitación de tupla” es:

 //Arrays: aIn, aOut Array.prototype.map.call( aIn, function(e,i){return [e, aOut[i]];}) 

Explicación:
Dado que Javascript no tiene un tipo de tuples , las funciones para tuplas, listas y conjuntos no eran de alta prioridad en la especificación del idioma.
De lo contrario, se puede acceder a un comportamiento similar de manera directa a través del mapa de Array en JS> 1.6 . (el map es implementado a menudo por los fabricantes de motores JS en muchos motores> JS 1.4, a pesar de no estar especificado).
La diferencia principal con el zip , izip , … de Python proviene del estilo funcional del map , ya que el map requiere una función-argumento. Adicionalmente es una función de la instancia Array . Uno puede usar Array.prototype.map lugar, si una statement adicional para la entrada es un problema.

Ejemplo:

 _tarrin = [0..constructor, function(){}, false, undefined, '', 100, 123.324, 2343243243242343242354365476453654625345345, 'sdf23423dsfsdf', 'sdf2324.234dfs','234,234fsf','100,100','100.100'] _parseInt = function(i){return parseInt(i);} _tarrout = _tarrin.map(_parseInt) _tarrin.map(function(e,i,a){return [e, _tarrout[i]]}) 

Resultado:

 //'('+_tarrin.map(function(e,i,a){return [e, _tarrout[i]]}).join('),\n(')+')' >> (function Number() { [native code] },NaN), (function (){},NaN), (false,NaN), (,NaN), (,NaN), (100,100), (123.324,123), (2.3432432432423434e+42,2), (sdf23423dsfsdf,NaN), (sdf2324.234dfs,NaN), (234,234fsf,234), (100,100,100), (100.100,100) 

Rendimiento relacionado:

Usando el map sobre for -loops:

Ver: ¿Cuál es la forma más eficiente de fusionar [1,2] y [7,8] en [[1,7], [2,8]]

pruebas de zip

Nota: los tipos base, como false e undefined , no poseen una jerarquía de objetos prototípicos y, por lo tanto, no exponen una función toString . Por lo tanto, estos se muestran como vacíos en la salida.
Como el segundo argumento de parseInt es la base / número radix, al cual convertir el número, y como map pasa el índice como el segundo argumento a su función de argumento, se usa una función de envoltura.

El Python tiene dos funciones: zip e itertools.zip_longest. La implementación en JS / ES6 es así:

Implementación del zip de Python en JS / ES6

 const zip = (...arrays) => { const length = Math.min(...arrays.map(arr => arr.length)); return Array.from({ length }, (value, index) => arrays.map((array => array[index]))); }; 

Resultados:

 console.log(zip( [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [11, 221] )); 

[[1, 667, 111, 11]]

 console.log(zip( [1, 2, 3, 'a'], [667, false, -378, '337'], [111, 212, 323, 433, '1111'] )); 

[[1, 667, 111], [2, falso, 212], [3, -378, 323], [‘a’, ‘337’, 433]]

 console.log(zip( [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[]

Implementación de Python’s zip_longest en JS / ES6

( https://docs.python.org/3.5/library/itertools.html?highlight=zip_longest#itertools.zip_longest )

 const zipLongest = (placeholder = undefined, ...arrays) => { const length = Math.max(...arrays.map(arr => arr.length)); return Array.from( { length }, (value, index) => arrays.map( array => array.length - 1 >= index ? array[index] : placeholder ) ); }; 

Resultados:

 console.log(zipLongest( undefined, [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[[1, 667, 111, indefinido], [2, falso, indefinido, indefinido],
[3, -378, indefinido, indefinido], [‘a’, ‘337’, indefinido, indefinido]]

 console.log(zipLongest( null, [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[[1, 667, 111, nulo], [2, falso, nulo, nulo], [3, -378, nulo, nulo], [‘a’, ‘337’, nulo, nulo]]

 console.log(zipLongest( 'Is None', [1, 2, 3, 'a'], [667, false, -378, '337'], [111], [] )); 

[[1, 667, 111, ‘No hay ninguno’], [2, falso, ‘No hay ninguno’, ‘No hay ninguno’],
[3, -378, ‘Is None’, ‘Is None’], [‘a’, ‘337’, ‘Is None’, ‘Is None’]]

Ejemplo de ES6 moderno con un generador:

 function *zip (...iterables){ let iterators = iterables.map(i => i[Symbol.iterator]() ) while (true) { let results = iterators.map(iter => iter.next() ) if (results.some(res => res.done) ) return else yield results.map(res => res.value ) } } 

Primero, obtenemos una lista de iterables como iterators . Esto suele ocurrir de manera transparente, pero aquí lo hacemos explícitamente, ya que lo hacemos paso a paso hasta que uno de ellos se agota. Verificamos si alguno de los resultados (usando el método .some() ) en la matriz dada está agotado, y si es así, rompemos el ciclo while.

Junto con otras funciones similares a Python, pythonic ofrece una función zip , con el beneficio adicional de devolver un Iterator evaluado perezoso, similar al comportamiento de su contraparte de Python :

 import {zip, zipLongest} from 'pythonic'; const arr1 = ['a', 'b']; const arr2 = ['c', 'd', 'e']; for (const [first, second] of zip(arr1, arr2)) console.log(`first: ${first}, second: ${second}`); // first: a, second: c // first: b, second: d for (const [first, second] of zipLongest(arr1, arr2)) console.log(`first: ${first}, second: ${second}`); // first: a, second: c // first: b, second: d // first: undefined, second: e // unzip const [arrayFirst, arraySecond] = [...zip(...zip(arr1, arr2))]; 

No integrado en el propio Javascript. Algunos de los marcos de JavaScript comunes (como Prototype) proporcionan una implementación, o puede escribir la suya propia.

Como @Brandon, recomiendo la función zip de Underscore . Sin embargo, actúa como zip_longest , agregando valores undefined según sea necesario para devolver algo a la longitud de la entrada más larga.

Utilicé el método mixin para extender el guión bajo con un zipShortest , que actúa como el zip de Python’s, basado en la propia fuente de zip de la biblioteca .

Puede agregar lo siguiente a su código JS común y luego _.zipShortest([1,2,3], ['a']) como si fuera parte del guión bajo: _.zipShortest([1,2,3], ['a']) devuelve [[1, 'a']] , por ejemplo.

 // Underscore library addition - zip like python does, dominated by the shortest list // The default injects undefineds to match the length of the longest list. _.mixin({ zipShortest : function() { var args = Array.Prototype.slice.call(arguments); var length = _.min(_.pluck(args, 'length')); // changed max to min var results = new Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(args, "" + i); } return results; }}); 

Podría reducir la matriz de matrices y asignar una nueva matriz tomando el resultado del índice de la matriz interna.

 var array1 = [1, 2, 3], array2 = ['a','b','c'], array3 = [4, 5, 6], array = [array1, array2, array3], transposed = array.reduce((r, a) => a.map((v, i) => (r[i] || []).concat(v)), []); console.log(transposed); 

Encontré un módulo npm que se puede usar como una versión javascript de python zip :

zip-array – Un equivalente en javascript de la función zip de Python. Fusiona los valores de cada una de las matrices.

https://www.npmjs.com/package/zip-array

Otra opción alternativa es para los usuarios de Tensorflow.js: si necesita una función zip en python para trabajar con conjuntos de datos de tensorflow en Javascript, puede usar tf.data.zip() en Tensorflow.js.

tf.data.zip () en Tensorflow.js documentado aquí

Puede hacer la función de utilidad utilizando ES6.

 const zip = (arr, ...arrs) => { return arr.map((val, i) => arrs.reduce((a, arr) => [...a, arr[i]], [val])); } // example const array1 = [1, 2, 3]; const array2 = ['a','b','c']; const array3 = [4, 5, 6]; console.log(zip(array1, array2)); // [[1, 'a'], [2, 'b'], [3, 'c']] console.log(zip(array1, array2, array3)); // [[1, 'a', 4], [2, 'b', 5], [3, 'c', 6]] 

La biblioteca Mochikit proporciona esta y muchas otras funciones similares a Python. El desarrollador de Mochikit también es un fan de Python, por lo que tiene el estilo general de Python, y también la envoltura de las llamadas asíncronas en un marco retorcido.

Hice una prueba de esto en JS puro preguntándome cómo los complementos publicados anteriormente hicieron el trabajo. Aquí está mi resultado. Prologo esto diciendo que no tengo idea de cuán estable será esto en IE y similares. Es sólo una maqueta rápida.

 init(); function init() { var one = [0, 1, 2, 3]; var two = [4, 5, 6, 7]; var three = [8, 9, 10, 11, 12]; var four = zip(one, two, one); //returns array //four = zip(one, two, three); //returns false since three.length !== two.length console.log(four); } function zip() { for (var i = 0; i < arguments.length; i++) { if (!arguments[i].length || !arguments.toString()) { return false; } if (i >= 1) { if (arguments[i].length !== arguments[i - 1].length) { return false; } } } var zipped = []; for (var j = 0; j < arguments[0].length; j++) { var toBeZipped = []; for (var k = 0; k < arguments.length; k++) { toBeZipped.push(arguments[k][j]); } zipped.push(toBeZipped); } return zipped; } 

Una variación de la solución del generador perezoso :

 function* iter(it) { yield* it; } function* zip(...its) { its = its.map(iter); while (true) { let rs = its.map(it => it.next()); if (rs.some(r => r.done)) return; yield rs.map(r => r.value); } } for (let r of zip([1,2,3], [4,5,6,7], [8,9,0,11,22])) console.log(r.join()) // the only change for "longest" is some -> every function* zipLongest(...its) { its = its.map(iter); while (true) { let rs = its.map(it => it.next()); if (rs.every(r => r.done)) return; yield rs.map(r => r.value); } } for (let r of zipLongest([1,2,3], [4,5,6,7], [8,9,0,11,22])) console.log(r.join()) 

Esto elimina una línea de la respuesta basada en el iterador de Ddi :

 function* zip(...toZip) { const iterators = toZip.map((arg) => arg[Symbol.iterator]()); const next = () => toZip = iterators.map((iter) => iter.next()); while (next().every((item) => !item.done)) { yield toZip.map((item) => item.value); } } 

Si estás bien con ES6:

 const zip = (arr,...arrs) =>( arr.map( (v,i) => arrs.reduce((a,arr)=>[...a, arr[i]], [v])))