Équivalent Javascript de la fonction zip De Python

Existe-t-il un équivalent javascript de la fonction zip De Python? C'est-à-dire, étant donné des réseaux multiples de longueurs égales, créer un réseau de paires.

Par exemple, si j'ai trois tableaux qui ressemblent à ceci:

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

le tableau de sortie doit être:

var output array:[[1,'a',4], [2,'b',5], [3,'c',6]]
144
demandé sur Phasmal 2011-02-01 01:08:11

13 réponses

2016 mise à jour:

Voici une version snazzier Ecmascript 6:

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

Illustration equiv. à Python { zip(*args) }:

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

(et FizzyTea souligne que ES6 a une syntaxe d'argument variadique, de sorte que la définition de fonction suivante agira comme python, mais voir ci-dessous pour disclaimer... ce ne sera pas sa propre inverse donc zip(zip(x)) ne sera pas égal à x ; bien que Matt Kramer points zip(...zip(...x))==x (comme régulièrement python zip(*zip(*x))==x ))

définition Alternative equiv. à 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

(notez que la syntaxe ... peut avoir des problèmes de performance à ce moment, et peut-être dans le futur, donc si vous utilisez la deuxième réponse avec des arguments variadiques, vous pouvez perf de test.)


Voici 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(...)

ce qui précède suppose que les tableaux sont de taille égale, comme ils devraient l'être. Il suppose également que vous passez dans une seule liste d'arguments listes, contrairement à la version de Python où la liste d'arguments est variadique. si vous voulez tous ces " caractéristiques", Voir ci-dessous. Il faut environ 2 lignes supplémentaires de code.

ce qui suit imitera le comportement de Python zip sur les cas de bord où les tableaux ne sont pas de taille égale, prétendant silencieusement que les parties plus longues des tableaux n'existent pas:

function zip() {
    var args = [].slice.call(arguments);
    var shortest = args.length==0 ? [] : args.reduce(function(a,b){
        return a.length<b.length ? a : b
    });

    return shortest.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]]]

// > zip()
// []

cela imitera le comportement itertools.zip_longest de Python, en insérant undefined où les tableaux ne sont pas définis:

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 vous utilisez ces deux dernières versions (variadic aka. les versions à arguments multiples), alors zip n'est plus son propre inverse. Pour imiter L'idiome zip(*[...]) de Python, vous devrez faire zip.apply(this, [...]) lorsque vous voulez inverser la fonction zip ou si vous voulez avoir un nombre variable de listes comme entrée.


addendum :

pour rendre cette poignée itérable (par exemple en Python vous pouvez utiliser zip sur les chaînes de caractères, les gammes, les objets map, etc.), vous pouvez définir ce qui suit:

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

cependant si vous écrivez zip dans le suivant voie , même cela ne sera pas nécessaire:

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

Démo:

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

(ou vous pouvez utiliser une fonction range(...) de style Python Si vous en avez déjà écrit une. Finalement, vous pourrez utiliser des compressions ou des générateurs de tableaux ECMAScript.)

133
répondu ninjagecko 2018-10-06 00:35:25

découvrez la bibliothèque trait de Soulignement .

Underscore fournit plus de 100 fonctions qui prennent en charge à la fois vos assistants fonctionnels favoris workaday: map, filter, invoke - ainsi que des goodies plus spécialisés: liaison de fonction, Templier javascript, création d'index rapides, tests d'égalité profonde, et ainsi de suite.

- disent les gens qui l'ont fait

j'ai récemment commencé à utiliser il spécifiquement pour la fonction zip() et il a laissé une grande première impression. J'utilise jQuery et CoffeeScript, et ça va parfaitement avec eux. Trait de soulignement reprend là où ils s'arrêtent et jusqu'à présent cela n'a pas marché pour moi. Oh au fait, c'est seulement 3KB minidifié.

regardez ça.

27
répondu Brandon 2015-07-26 02:06:34

en plus de la réponse excellente et complète de ninjagecko, tout ce qu'il faut pour zip deux js-arrays dans un "tuple-mimic" est:

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

explication:

Puisque Javascript n'a pas de type tuples , les fonctions pour les tuples, les listes et les ensembles n'étaient pas une grande priorité dans la spécification du langage.

Autrement, un comportement similaire est accessible d'une manière simple via Array carte en JS >1.6 . ( map est en fait souvent mis en œuvre par les motoristes JS dans de nombreux moteurs >js 1.4, bien que non spécifié).

La différence majeure par rapport à Python zip , izip ,... résulte de map l 'style fonctionnel, depuis map nécessite un argument de fonction. En outre, il est une fonction de l'instance Array . On peut utiliser Array.prototype.map à la place, si une déclaration supplémentaire pour l'entrée est question.

exemple:

_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]]})

résultat:

//'('+_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)

Performances Connexes:

utilisant map sur for - boucles:

Voir: Quel est le moyen le plus efficace de fusion [1,2] et [7,8] dans [[1,7], [2,8]]

zip tests

Note: les types de base tels que false et undefined ne possèdent pas de hiérarchie d'objet prototypique et n'exposent donc pas une fonction toString . Par conséquent, ceux-ci sont affichés comme vides dans la sortie.

Comme parseInt 's second argument est la base/nombre radix, à laquelle convertir le nombre à, et depuis map passe l'indice comme le deuxième argument à son argument-fonction, une fonction wrapper est utilisée.

11
répondu Lorenz Lo Sauer 2017-05-23 12:34:39

Pas intégré dans le Javascript lui-même. Certains cadres Javascript courants (comme le Prototype) fournissent une implémentation, ou vous pouvez écrire les vôtres.

4
répondu Amber 2011-01-31 22:11:47

le Python a deux fonctions: zip et itertools.zip_longest. La mise en œuvre sur JS/ES6 est comme ceci:

la mise en Œuvre Python zip sur 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])));
};

Résultats:

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, faux, 212 ], [ 3, -378, 323 ], [ 'un", "337", 433 ]]

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

[]

la mise en Œuvre Python zip_longest sur 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
        )
    );
};

Résultats:

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

[ [ 1, 667, 111, Non défini ], [ 2, faux, undefined, undefined ],

[ 3, -378, undefined, undefined], ['a', '337', undefined, Non défini]]

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

[ [ 1, 667, 111, null ], [ 2, false, null, null ], [ 3, -378, null, null ], [ 'a', '337', null, null ] ]

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

[ [ 1, 667, 111, 'Est None' ], [ 2, false, 'None', 'None' ],

[ 3, -378, 'None', 'None' ], [ 'a', '337', 'None', 'Est Néant']

4
répondu Seti Volkylany 2016-12-30 13:50:42

exemple ES6 moderne avec un générateur:

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 )
    }
}

tout d'abord, nous obtenons une liste de itérables comme iterators . Cela se produit habituellement de manière transparente, mais ici nous le faisons explicitement, car nous cédons pas à pas jusqu'à ce que l'un d'eux soit épuisé. Nous vérifions si l'un des résultats (en utilisant la méthode .some() ) dans le tableau donné est épuisé, et si c'est le cas, nous rompons la boucle while.

4
répondu Dimitris 2018-01-17 17:31:40

pythonic offre zip le long de certaines autres fonctions de type python:

import {zip} 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
3
répondu Keyvan 2018-04-07 09:34:40

comme @Brandon, je recommande Underscore 's zip fonction. Cependant , il agit comme zip_longest , ajoutant des valeurs undefined nécessaires pour retourner quelque chose de la longueur de la plus longue entrée.

j'ai utilisé la méthode mixin pour étendre le underscore avec un zipShortest , qui agit comme zip de Python, basé sur la propre source de la bibliothèque pour zip .

vous pouvez ajouter ce qui suit à votre code JS commun et l'appeler ensuite comme s'il faisait partie de underscore: _.zipShortest([1,2,3], ['a']) retourne [[1, 'a']] , par exemple.

// 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;
}});
2
répondu Pat 2012-11-29 03:34:15

la bibliothèque Mochikit fournit cette fonction et bien d'autres semblables à celles de Python. développeur de Mochikit est aussi un fan de Python, donc il a le style général de Python, et aussi les enveloppements des appels asynchrones dans un cadre de type tordu.

0
répondu Keith 2011-01-31 22:25:10

j'ai fait un essai dans pure JS en me demandant comment les plugins affichés ci-dessus ont fait le travail. Voici mon résultat. Je vais commencer en disant que je n'ai aucune idée de la stabilité de ce sera en mi et les autres. C'est juste une petite maquette.

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;
}

ce n'est pas pare-balles, mais c'est quand même intéressant.

0
répondu Mr. Polywhirl 2015-02-23 17:53:16

Ce rase une ligne de Ddi itérateur de réponse:

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);
  }
}
-1
répondu Steven Kalt 2018-02-20 21:55:02

une autre variante de Ddi :

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())

Et voici comment écrire le classique de py zip(*[iter(a)]*n) :

triples = [...zip(...Array(3).fill(iter(a)))]
-1
répondu georg 2018-03-24 12:50:15
  const zip = (arr,...arrs)=>arr.map((v,i)=>arrs.reduce((a,arr)=>[...a, arr[i]], [v]))
-1
répondu T.Chmelevskij 2018-04-25 16:25:56