web-dev-qa-db-ger.com

Javascript entspricht der Python-Zip-Funktion

Gibt es ein Javascript-Äquivalent der Zip-Funktion von Python? Das heißt, wenn mehrere Arrays gleicher Länge ein Array von Paaren erzeugen.

Wenn ich beispielsweise drei Arrays habe, die folgendermaßen aussehen:

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

Das Ausgabearray sollte Folgendes sein:

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

Update 2016:

Hier ist eine schickere Ecmascript 6-Version:

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

Abbildung äquiv. zu Python {Zip(*args)}:

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

(und FizzyTea weist darauf hin, dass ES6 eine variable Argument-Syntax hat, daher verhält sich die folgende Funktionsdefinition wie Python, siehe unten für den Haftungsausschluss ... dies ist keine eigene Umkehrung, sodass Zip(zip(x)) nicht gleich x ist; Matt Kramer weist auf Zip(...Zip(...x))==x hin (wie in regulären python Zip(*Zip(*x))==x)

Alternative Definition Äquiv. zu 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

(Beachten Sie, dass die ... -Syntax derzeit und möglicherweise in Zukunft Leistungsprobleme aufweist. Wenn Sie also die zweite Antwort mit unterschiedlichen Argumenten verwenden, möchten Sie sie möglicherweise testen.)


Hier ist ein 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(...)

Das Obige setzt voraus, dass die Arrays die gleiche Größe haben, wie sie sein sollten. Es wird auch davon ausgegangen, dass Sie ein einzelnes Listenargument übergeben, im Gegensatz zu Pythons Version, bei der die Argumentliste variabel ist. Wenn Sie alle diese "Funktionen" möchten, siehe unten. Es dauert nur etwa 2 zusätzliche Codezeilen.

Das folgende Beispiel ahmt das Zip -Verhalten von Python in Edge-Fällen nach, in denen die Arrays nicht die gleiche Größe haben und so tun, als gäbe es keine längeren Teile von Arrays:

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()
// []

Dies ahmt Pythons itertools.Zip_longest -Verhalten nach und fügt undefined ein, wenn keine Arrays definiert sind:

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()
// []

Wenn Sie die letzten beiden Versionen (variadic aka. Multiple-argument versions) verwenden, ist Zip nicht länger seine eigene Umkehrung. Um das Zip(*[...])-Idiom von Python zu imitieren, müssen Sie Zip.apply(this, [...]) ausführen, wenn Sie die Zip-Funktion umkehren oder auf ähnliche Weise eine variable Anzahl von Listen als Eingabe haben möchten.


Nachtrag :

Um dieses Handle iterabel zu machen (z. B. können Sie in Python Zip für Zeichenfolgen, Bereiche, Kartenobjekte usw. verwenden), können Sie Folgendes definieren:

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

Wenn Sie jedoch Zip wie folgt schreiben way , ist auch dies nicht erforderlich:

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

Demo:

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

(Oder Sie können eine range(...)-Funktion im Python-Stil verwenden, wenn Sie bereits eine geschrieben haben. Schließlich können Sie ECMAScript-Array-Verständnisse oder -Generatoren verwenden.)

157
ninjagecko

Schauen Sie sich die Bibliothek Underscore an.

Underscore bietet über 100 Funktionen, die sowohl Ihre bevorzugten funktionalen Helfer für den Arbeitsalltag unterstützen: map, filter, invoke - als auch speziellere Goodies: Funktionsbindung, Javascript-Templating, schnelle Indexerstellung, tiefe Gleichheitsprüfungen usw.

- Sag die Leute, die es geschafft haben

Ich habe vor kurzem damit begonnen, es speziell für die Funktion Zip() zu verwenden, und es hat einen guten ersten Eindruck hinterlassen. Ich verwende jQuery und CoffeeScript und es passt einfach perfekt dazu. Underscore greift dort an, wo sie aufgehört haben und hat mich bisher nicht enttäuscht. Ach übrigens, es sind nur 3kb vermint.

Hör zu.

28
Brandon

Neben der hervorragenden und umfassenden Antwort von ninjagecko reicht es aus, zwei JS-Arrays in ein "Tuple-Mimic" zu packen:

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

Erklärung:
Da Javascript keinen tuples-Typ hat, hatten die Funktionen für Tupel, Listen und Sets keine hohe Priorität in der Sprachspezifikation.
Andernfalls ist ein ähnliches Verhalten über Array-Zuordnung in JS> 1.6 auf einfache Weise zugänglich. (map wird von JS-Engine-Herstellern häufig in vielen> JS 1.4-Engines implementiert, obwohl dies nicht angegeben ist).
Der Hauptunterschied zu Pythons Zip, izip, ... ergibt sich aus dem Funktionsstil von map, da map ein Funktionsargument benötigt. Zusätzlich ist es eine Funktion der Array- Instanz. Sie können stattdessen Array.prototype.map verwenden, wenn eine zusätzliche Deklaration für die Eingabe ein Problem ist.

Beispiel:

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

Ergebnis:

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

Related Leistung:

Verwenden von map über for- Schleifen:

Siehe: Was ist der effizienteste Weg, [1,2] und [7,8] zu [[1,7], [2,8]] zu verschmelzen?

Zip tests

Hinweis: Die Basistypen wie false und undefined besitzen keine prototypische Objekthierarchie und stellen daher keine toString-Funktion bereit. Daher werden diese in der Ausgabe als leer angezeigt.
Das zweite Argument von parseInt ist die Basis/Zahlen-Basis, in die die Zahl umgewandelt werden soll, und da map den Index als zweites Argument an seine Argumentfunktion übergibt, wird eine Wrapper-Funktion verwendet.

12
Lorenz Lo Sauer

Der Python hat zwei Funktionen: Zip und itertools.Zip_longest. Die Implementierung auf JS/ES6 sieht folgendermaßen aus:

Implementation Python`s Zip auf 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])));
};

Ergebnisse:

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, falsch, 212], [3, -378, 323], ['a',. '337', 433]]

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

[]

Implementation Python`s Zip_longest auf 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
        )
    );
};

Ergebnisse:

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

[[1, 667, 111, undefined], [2, falsch, undefined, undefined],
[3, -378, undefined, undefined], ['a', '337', undefined, nicht definiert ] ]

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

[[1, 667, 111, null], [2, falsch, 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, "ist keine"], [2, falsch, "ist keine", "ist keine"],
[3, -378, 'Ist Keine', 'Ist Keine'], ['a', '337', 'Ist Keine', 'Ist Keiner' ] ]

4
Seti Volkylany

Modernes ES6-Beispiel mit Generator:

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

Zuerst erhalten wir eine Liste von Iterables als iterators. Dies geschieht normalerweise transparent, aber hier machen wir es explizit, da wir Schritt für Schritt nachgeben, bis einer von ihnen erschöpft ist. Wir prüfen, ob Ergebnisse (mit der .some()-Methode) im angegebenen Array erschöpft sind, und wenn ja, unterbrechen wir die while-Schleife.

4
Dimitris

Sie können die Utility-Funktion mit ES6 aktivieren.

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]]

In der obigen Lösung definiert die Länge des ersten Arrays jedoch die Länge des Ausgangsarrays.

Hier ist die Lösung, bei der Sie mehr Kontrolle darüber haben. Es ist etwas komplex, aber es lohnt sich.

function _Zip(func, args) {
  const iterators = args.map(arr => arr[Symbol.iterator]());
  let iterateInstances = iterators.map((i) => i.next());
  ret = []
  while(iterateInstances[func](it => !it.done)) {
    ret.Push(iterateInstances.map(it => it.value));
    iterateInstances = iterators.map((i) => i.next());
  }
  return ret;
}
const array1 = [1, 2, 3];
const array2 = ['a','b','c'];
const array3 = [4, 5, 6];

const zipShort = (...args) => _Zip('every', args);

const zipLong = (...args) => _Zip('some', args);

console.log(zipShort(array1, array2, array3)) // [[1, 'a', 4], [2, 'b', 5], [3, 'c', 6]]
console.log(zipLong([1,2,3], [4,5,6, 7]))
// [
//  [ 1, 4 ],
//  [ 2, 5 ],
//  [ 3, 6 ],
//  [ undefined, 7 ]]
3
Bhargav Patel

Nicht in Javascript selbst eingebaut. Einige der gängigen Javascript-Frameworks (z. B. Prototype) bieten eine Implementierung, oder Sie können Ihre eigene schreiben.

3
Amber

Pythonic bietet Zip zusammen mit anderen pythonartigen Funktionen:

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
Keyvan

1. Npm-Modul: Zip-array

Ich habe ein npm-Modul gefunden, das als Javascript-Version von Python verwendet werden kann Zip:

Zip-Array - Ein Javascript-Äquivalent zu Pythons Zip-Funktion. Führt die Werte der einzelnen Arrays zusammen.

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

2. tf.data.Zip() in Tensorflow.js

Eine andere Alternative ist für Benutzer von Tensorflow.js: Wenn Sie eine Zip -Funktion in Python benötigen, um mit Tensorflow-Datensätzen in Javascript zu arbeiten, können Sie tf.data.Zip() in Tensorflow.js verwenden.

tf.data.Zip () in Tensorflow.js dokumentiert unter hier

2
Huan

Wie @Brandon empfehle ich die Funktion Underscore 's Zip . Es verhält sich jedoch wie Zip_longest, wobei undefined-Werte nach Bedarf angehängt werden, um etwas von der Länge der längsten Eingabe zurückzugeben. 

Ich habe die mixin -Methode verwendet, um den Unterstrich mit einer zipShortest zu erweitern, die wie Pythons Zip wirkt, basierend auf der eigenen Quelle der Bibliothek für Zip

Sie können Ihrem allgemeinen JS-Code Folgendes hinzufügen und ihn dann so aufrufen, als wäre er Teil von Unterstrich: _.zipShortest([1,2,3], ['a']) gibt beispielsweise [[1, 'a']] zurück.

// 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
Pat

Sie können das Array von Arrays verkleinern und ein neues Array zuordnen, indem Sie das Ergebnis des Index des inneren Arrays übernehmen.

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

1
Nina Scholz

Die Bibliothek Mochikit bietet diese und viele andere Python-ähnliche Funktionen. Der Entwickler von Mochikit ist auch ein Python-Fan, daher hat er den allgemeinen Stil von Python und auch die Async-Aufrufe werden in einem verdrehten Rahmen eingebettet.

0
Keith

Eine Variation der Lazy Generator-Lösung :

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

Und dies ist das klassische "n-group" Idiom des Pythons Zip(*[iter(a)]*n):

triples = [...Zip(...Array(3).fill(iter(a)))]
0
georg

Ich habe mich in Pure JS darüber gefragt, wie die oben genannten Plugins die Arbeit erledigt haben. Hier ist mein Ergebnis. Ich möchte dem vorausgehen, dass ich keine Ahnung habe, wie stabil dies in IE und dergleichen sein wird. Es ist nur ein kurzes Modell.

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

Es ist nicht kugelsicher, aber dennoch interessant.

0
user1385191