Pythonのzip関数に相当するJavaScript


215

Pythonのzip関数に相当するJavaScriptはありますか?つまり、同じ長さの複数の配列を指定すると、ペアの配列が作成されます。

たとえば、次のような3つの配列があるとします。

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

出力配列は次のようになります。

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

5
私たちのPythonプログラマーは、ループを含むダムメソッドが遅いので「恐れている」と言ってもよいので、常に組み込みのメソッドを探します。しかし、Javascriptでは、ループが特に遅いわけではないので、それに進んでループを作成するだけでよいのでしょうか。
LondonRob

3
@LondonRobループはループであり、「高速」メソッドの背後に隠れています。JavaScriptは間違いなくアレイのの導入により、より高次の機能のためのより多くのサポートを得るされたforEachreducemapeveryそれはちょうどケースだった、などzip(「カットを作る」はなかったflatMapにも存在しない)、ないパフォーマンスを考慮して-しかし、公平を期すために、.NET(3.5)にはEnumerableのZipが2年間ありませんでした。underscore / lodash(lodash 3.xには遅延シーケンス評価があります)のような「機能的な」ライブラリは、同等のzip関数を提供します。
user2864740

@ user2864740解釈されたループ(Pythonなど)は、常にマシンコードループよりもはるかに遅くなります。JITでコンパイルされたループ(最近のJSエンジンなど)は、ネイティブCPUの速度に近づく可能性があるため、マシンコードループを使用することによってもたらされるゲインは、匿名関数呼び出しのオーバーヘッドによって相殺される可能性があります。それでも、これらの組み込み関数を用意し、いくつかのJSエンジンで「内部ループ」のいくつかのバリエーションをプロファイルすることは理にかなっています。結果は明らかではないかもしれません。
トビア2015年

回答:


177

2016年の更新:

以下は、すっきりとしたEcmascript 6バージョンです。

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

イラスト相当 Pythonの { zip(*args)}:

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

(とFizzyTeaは、次の関数定義は、Pythonのように振る舞うが、免責事項は下記を参照してください...これがそう、それ自身の逆ではありませんでしょうのでES6は、可変長引数の構文を持っていることを指摘zip(zip(x))しません等しいxマット・クレイマーが指摘するようにかかわらず、zip(...zip(...x))==xのような(通常のPython zip(*zip(*x))==xでは))

同等の代替定義。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

...現時点では、また将来的には構文にパフォーマンスの問題が発生する可能性があるため、可変引数を使用して2番目の回答を使用する場合は、パフォーマンステストを実行することをお勧めします。)


ここにワンライナーがあります:

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

上記は、配列が同じサイズであることを前提としています。また、引数リストが可変長であるPythonのバージョンとは異なり、リスト引数の単一のリストを渡すことを前提としています。これらすべての「機能」が必要な場合は、以下を参照してください。わずか約2行のコードが追加されます。

以下はzip、配列のサイズが等しくないエッジケースでのPythonの動作を模倣し、配列の長い部分が存在しないふりをします。

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

これは、配列が定義されていない場所itertools.zip_longestに挿入してundefined、Pythonの動作を模倣します。

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

これら最後の2つのバージョン(さまざまな別名、複数引数バージョン)を使用する場合、zipは独自の逆ではなくなります。zip(*[...])Python のイディオムを模倣するにはzip.apply(this, [...])、zip関数を反転する場合、または同様に可変数のリストを入力として使用する場合に行う必要があります。


補遺

これを反復可能にするには(たとえば、Pythonではzip文字列、範囲、マップオブジェクトなどで使用できます)、次のように定義できます。

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

ただしzip、次のように記述すれば、それは不要です。

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

デモ:

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

(または、range(...)すでに記述している場合はPythonスタイルの関数を使用できます。最終的には、ECMAScript配列内包表記またはジェネレーターを使用できるようになります。)


1
これは私にとっては機能しません:TypeError:Object 1にはメソッド 'map'がありません
Emanuele Paolini

7
そして、可変引数および任意の反復可能変数のES6:zip = (...rows) => [...rows[0]].map((_,c) => rows.map(row => row[c]));
1983年

「オブジェクト1にはメソッド 'map'がありません」は、おそらくこの投稿の付録に記載されているマップメソッド(ノードリストや文字列など)を持たないオブジェクトでこれを使用しようとした場合です。
ninjagecko 2017年

可変のES6バージョンが保持しないことは事実zip(zip(x)) = xですが、それでも信頼を積むことができますzip(...zip(...x)) = x
Matt Kramer

const the_longest_array_length = Math.max(...(arrays.map(array => array.length)));
КонстантинВан

34

ライブラリUnderscoreをチェックしてください。

Underscoreは100を超える関数を提供します。これは、お気に入りの実用的な機能ヘルパーであるマップ、フィルター、呼び出しの両方をサポートします。さらに、関数バインディング、JavaScriptテンプレート、クイックインデックスの作成、深い等価性テストなど、より特化した機能もサポートします。

–それを作った人々を言う

最近はzip()機能専用に使い始めて、第一印象がいいです。私はjQueryとCoffeeScriptを使用していますが、それはそれらと完全に一致します。アンダースコアは彼らが去るところから正しくピックアップし、今のところそれは私を失望させていません。ちなみに、3kbに縮小されています。

見てみな:

_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
// returns [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]

3
Underscoreを使用すると、Haskellの明快さと論理的な居心地の良さに少し近づきます。
CamilB 2013

12
下線の代わりに、これを試してください:lodash.com-ドロップイン置換、同じ素晴らしいフレーバー、より多くの機能、より多くのブラウザー間の一貫性、より良いパフォーマンス。説明については、kitcambridge.be / blog / say-hello-to-lo-dashを参照してください。
Merlyn Morgan-Graham 2014

16

ninjageckoの優れた包括的な答えに加えて、2つのJS配列を「タプル模倣」に圧縮するために必要なことは、次のとおりです。

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

説明:
Javascriptにはtuplesタイプがないため、タプル、リスト、およびセット用の関数は、言語仕様では優先度が高くありませんでした。
それ以外の場合、JS> 1.6の配列マップを介して、同様の動作に簡単にアクセスできます。(map指定されていないにもかかわらず、実際には多くの場合、JSエンジンメーカーが多くの> JS 1.4エンジンに実装しています)。
Pythonのへの主な違いはzipizip...からの結果mapの機能的なスタイル、以降はmap関数の引数が必要です。さらに、それはArray-instanceの関数です。Array.prototype.map入力の追加宣言が問題である場合は、代わりに使用できます。

例:

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

結果:

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

関連するパフォーマンス:

mapover forループの使用:

参照:[1,2]と[7,8]を[[1,7]、[2,8]]にマージする最も効率的な方法は何ですか

zipテスト

注:のような基本型falseとするundefined原型オブジェクト階層をposessないので、露出していないtoString機能。したがって、これらは出力で空として表示されます。第二引数はに番号を変換するれる、ベース/数の基数であり、そしてので、その引数関数に対する第2の引数としてインデックスを渡し、ラッパー関数が使用されます。
parseIntmap


最初の例では、「aInは関数ではありません」と言っています。プロトタイプとしてではなく、配列から.mapを呼び出すと機能しaIn.map(function(e, i) {return [e, aOut[i]];})ます。何が問題なのですか?
Noumenon

1
@Noumenon、されるArray.prototype.mapべきだったArray.prototype.map.call、答えを修正しました。
ユーザー

11

ジェネレーターを備えた最新のES6の例:

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

最初に、イテラブルのリストをとして取得しますiterators。通常、これは透過的に行われますが、ここでは明示的に行います。1つが使い果たされるまで、段階的に実行するためです。.some()指定された配列内の(メソッドを使用した)結果のいずれかが使い果たされているかどうかをチェックし、そうである場合は、whileループを中断します。


この回答では、より多くの説明を使用できます。
cmaher 2018年

1
イテラブルからイテレータのリストを取得します。これは通常、透過的に行われます。ここでは明示的に行います。そのうちの1つが使い果たされるまで、段階的に進みます。配列内のそれらのいずれか(.some()メソッド)が使い果たされているかどうかを確認し、そうである場合は中断します。
Dimitris

11

他のPythonのような関数に加えpythoniczip、遅延対応のを返すという追加の利点を持つ関数を提供します。これは、対応Iteratorする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))];

開示私はPythonicの作者であり、メンテナーです


7

Pythonには、zipとitertools.zip_longestの2つの関数があります。JS / ES6での実装は次のようになります。

JS / ES6でのPythonのzipの実装

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

結果:

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、false、212]、[3、-378、323]、['a'、 '337'、433]]

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

[]

JS / ES6でのPythonのzip_longestの実装

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

結果:

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

[[
1、667、111 、undefined]、[2、false、undefined、undefined]、[3、-378、undefined、undefined]、['a'、 '337'、undefined、undefined]]

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、 'Is None' ]、[2、false、 'Is None'、 'Is None']、[3、-378、 'Is None'、 'Is None']、['a '、' 337 '、' Is None '、' Is None ']]


4

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

ただし、上記のソリューションでは、最初の配列の長さが出力配列の長さを定義します。

ここでは、それをより詳細に制御できるソリューションを示します。少し複雑ですが、それだけの価値があります。

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


4

1. Npmモジュール: zip-array

私はPythonのJavaScriptバージョンとして使用できるnpmモジュールを見つけましたzip

zip-array -Pythonのzip関数に相当するJavaScript。各配列の値をマージします。

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

2. tf.data.zip()Tensorflow.js内

別の選択肢はTensorflow.jsユーザー向けですzip。JavaScript tf.data.zip()でテンソルフローデータセットを操作するためにPythonで関数が必要な場合は、Tensorflow.jsで使用できます。

ここにドキュメント化されているTensorflow.jsのtf.data.zip()


3

Javascript自体には組み込まれていません。一般的なJavascriptフレームワークのいくつか(Prototypeなど)は実装を提供しますが、独自に作成することもできます。


1
リンク?jQueryのは、それをしなかった場合には、私が...使用しているものですので、また、私は、もっと興味があると思います
PQを。


2
ただし、jQueryの動作はPythonの動作とは少し異なり、配列ではなくオブジェクトを返すため、2つ以上のリストをまとめて圧縮することはできません。
アンバー、

そうです、作成者はjQueryを同等のものと呼ぶべきではありません。
pq。

3

@Brandonと同様に、Underscorezip関数をお勧めします。ただし、のように機能しzip_longestundefined必要に応じて値を追加して、最も長い入力の長さの何かを返します。

このmixinメソッドを使用して、アンダースコアをで拡張しましたzipShortest。これzip、ライブラリ独自ののソースにzip基づいて、Pythonのように機能ます。

次のコードを一般的なJSコードに追加して、アンダースコアの一部であるかのように呼び出すことができ_.zipShortest([1,2,3], ['a'])ます[[1, 'a']]

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

内部配列のインデックスの結果を取得することにより、配列の配列を減らし、新しい配列をマップできます。

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

レイジージェネレーターソリューションのバリエーション:

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

そして、これはpythonの古典的な「nグループ」イディオムzip(*[iter(a)]*n)です。

triples = [...zip(...Array(3).fill(iter(a)))]

これは何が悪いのかな、まったく同じものを書いた。私には他のすべての方法よりもずっと良い感じですが、おそらく両方が間違っています...フロータイプを追加する方法を探していましたが、苦労しています:D。
cglacet

0

Mochikitライブラリは、これと他の多くのPythonのような機能を提供します。Mochikitの開発者はPythonファンでもあるため、Pythonの一般的なスタイルがあり、非同期呼び出しをねじれたようなフレームワークにラップします。


0

私は純粋なJSでこれを実行して、上に投稿されたプラグインがどのように仕事を完了したのか疑問に思いました。これが私の結果です。IEなどでこれがどれほど安定するか私にはわからないと言って、これを前置きします。それはただのモックアップです。

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

完全ではありませんが、それでも興味深いものです。


jsfiddleはよさそうだ。TidyUpボタンがあります![実行]ボタンの[結果]パネルにconsole.log出力が表示されませんでした。どうして?
pq。

それ(console.log)を実行するには、Firebugのようなものが必要です。単なるスイッチconsole.logalert

そのときの結果ペインとは何ですか?
pq。

フィドルのHTMLを表示します。この場合、私はまっすぐなJSをやっています。document.write() jsfiddle.net/PyTWw/5

-1

これは、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);
  }
}

-1

ES6で問題がない場合:

const zip = (arr,...arrs) =>(
                            arr.map(
                              (v,i) => arrs.reduce((a,arr)=>[...a, arr[i]], [v])))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.