Python의 zip 함수와 동등한 Javascript


216

파이썬의 zip 함수와 동등한 자바 스크립트가 있습니까? 즉, 길이가 같은 여러 배열이 주어지면 쌍 배열을 만듭니다.

예를 들어, 다음과 같은 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
우리 파이썬 프로그래머가 루프를 포함하는 멍청한 메소드가 느리기 때문에 '무서워'라고 말하는 것이 공정한 일이므로 항상 내장 된 메소드를 찾습니다. 그러나 Javascript에서는 특별히 느리지 않기 때문에 루프를 작성해야합니다.
LondonRob

3
@LondonRob 루프는 '빠른'메소드 뒤에 숨겨져 있는지 여부입니다. 자바 스크립트를 확실히 배열의 도입과 함께, 고차 기능에 대한 더 많은 지원을 받고있다 forEach, reduce, map, every, 그것은 단지의 경우와 등 그 zip(a는 "컷을"하지 않았다 flatMap또한 결석)이 아닌 성능 고려 사항 -하지만 공정하게 말하면 .NET (3.5)에는 몇 년 동안 Enumerable에 Zip이 없었습니다! 밑줄 / lodash (lodash 3.x는 느린 시퀀스 평가를 가짐)와 같은 '기능적'라이브러리는 동등한 zip 기능을 제공합니다.
user2864740

@ user2864740 해석 된 루프 (예 : Python)는 항상 기계 코드 루프보다 훨씬 느립니다. JIT 컴파일 루프 (예 : 최신 JS 엔진과 같은)는 기본 CPU 속도에 근접 할 수 있으므로 기계 코드 루프를 사용하여 발생하는 게인은 익명 함수 호출의 오버 헤드로 상쇄 될 수 있습니다. 그럼에도 불구하고 이러한 내장 함수를 갖고 여러 JS 엔진으로 여러 "내부 루프"변형을 프로파일 링하는 것이 좋습니다. 결과가 명확하지 않을 수 있습니다.
Tobia

답변:


178

2016 년 업데이트 :

snazzier Ecmascript 6 버전은 다음과 같습니다.

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

그림 equiv. 에 파이썬 { zip(*args)} :

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

(그리고 FizzyTea 다음 함수 정의 파이썬처럼 행동하지만, 고지 아래 참조 ...이 때문에 자신의 역 없습니다 있도록 ES6는 가변 인수 구문 지적 zip(zip(x))것 같지 x맷 크레이머 지적한대로했지만, zip(...zip(...x))==x추천 ( 일반 파이썬에서 zip(*zip(*x))==x))

대체 정의 equiv. 에 파이썬 { 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

(현재 ...구문에 성능 문제가있을 수 있으며 나중에는 가변적 인수와 함께 두 번째 답변을 사용하는 경우 구문 테스트를 수행 할 수 있습니다.)


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

위의 배열은 배열의 크기가 동일해야한다고 가정합니다. 또한 인수 목록이 가변적 인 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()
// []

이 마지막 두 버전 (다양한 여러 인수 버전)을 사용하는 경우 zip은 더 이상 고유 한 역이 아닙니다. zip(*[...])파이썬 의 관용구 를 모방하려면 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(...)이미 파이썬 스타일 함수를 작성했다면 사용할 수 있습니다 . 결국 ECMAScript 배열 이해 또는 생성기를 사용할 수 있습니다.)


1
이것은 나를 위해 작동하지 않습니다 : TypeError : Object 1에는 'map'메소드가 없습니다
Emanuele Paolini

7
variadic args 및 iterable을위한 ES6 :zip = (...rows) => [...rows[0]].map((_,c) => rows.map(row => row[c]));
1983

"객체 1에는 'map'방법이 없습니다"는 아마도이 게시물의 부록에서 다루는 맵 방법 (예 : 노드 목록 또는 문자열)이없는 객체에서 이것을 사용하려는 경우 일 것입니다
ninjagecko

variadic ES6 버전이 보존되지 않는 것은 사실이지만 zip(zip(x)) = x, 여전히 확신을 가질 수 zip(...zip(...x)) = x있습니다.
매트 Kramer

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

34

Underscore 라이브러리를 확인하십시오 .

Underscore는 맵, 필터링, 호출 등 자주 사용하는 기능 지원 도우미 (함수 바인딩, 자바 스크립트 템플릿, 빠른 인덱스 생성, 심도 평등 테스트 등)를 지원하는 100 가지가 넘는 기능을 제공합니다.

– 만든 사람들에게

나는 최근에 그 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

12
대신 밑줄로,이 시도 : lodash.com - 드롭 인 (drop-in) 교체, 같은 좋은 맛, 더 많은 기능, 더 크로스 브라우저의 일관성, 더 나은 반환 한. 설명 은 kitcambridge.be/blog/say-hello-to-lo-dash 를 참조하십시오 .
멀린 모건-그레이엄

16

ninjagecko의 우수하고 포괄적 인 답변 외에도 두 개의 JS 배열을 "tuple-mimic"로 압축하는 데 필요한 모든 것은 다음과 같습니다.

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

설명 :
Javascript에는 tuples유형이 없으므로 언어 ​​스펙에서 튜플, 목록 및 세트에 대한 기능이 우선 순위가 높지 않았습니다.
그렇지 않으면, JS> 1.6의 배열 맵을 통해 유사한 동작에 간단한 방식으로 액세스 할 수 있습니다 . ( map실제로 많은> JS 1.4 엔진에서 JS 엔진 제조업체가 실제로 구현하지는 않지만).
파이썬의 zip,, izip... 와의 주요 차이점 은 함수 인수를 필요로하기 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)

관련 성능 :

map오버 for루프 사용 :

참조 : [[1,7], [2,8]에 [1,2] 및 [7,8]을 병합하는 가장 효율적인 방법은 무엇입니까

우편 테스트

참고 : 기본 유형과 같은 falseundefined에 노출되지 않도록 따라서 프로토 타입 객체 계층 구조를 posess하지 않는 toString기능. 따라서 출력에서 ​​빈 것으로 표시됩니다.
마찬가지로 parseInt두번째 인수까지의 숫자를 변환되는베이스 / 기수 번호가 있으며,이 때문에 map인수 함수의 두번째 인수 인덱스를 통과 래퍼 함수가 사용된다.


첫 번째 예는 "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 )
    }
}

먼저, 우리는 iterables리스트를 얻는다 iterators. 이것은 일반적으로 투명하게 발생하지만 여기서는 하나가 소진 될 때까지 단계별로 산출하므로 명시 적으로 수행합니다. .some()주어진 배열에서 (메소드를 사용하여 ) 결과 가 소진되었는지 확인하고, 그렇다면 루프를 중단합니다.


이 답변은 더 많은 설명을 사용할 수 있습니다.
cmaher

1
이터 러블에서 이터레이터 목록을 얻습니다. 이것은 일반적으로 투명하게 발생합니다. 여기서 우리는 명시 적으로 수행합니다. 중 하나가 고갈 될 때까지 단계별로 산출합니다. 배열의 .some () 메소드 중 하나가 소진되었는지 확인하고 그렇다면 중단합니다.
Dimitris

11

함께 다른 파이썬과 같은 기능 pythonic제공하는 zip평가 게으른 반환의 추가 혜택과 기능, Iterator그것의 동작에 유사한, 파이썬의 대응 :

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

파이썬에는 zip과 itertools.zip_longest의 두 가지 기능이 있습니다. 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 ','없음 ','없음 ']]


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

자바 스크립트 버전의 파이썬으로 사용할 수있는 npm 모듈을 찾았습니다 zip.

zip-array- Python의 zip 함수와 동등한 Javascript입니다. 각 배열의 값을 병합합니다.

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

2. tf.data.zip()Tensorflow.js에서

또 다른 대안은 Tensorflow.js 사용자를위한 것 zip입니다. Javascript에서 tensorflow 데이터 세트로 작업하기 위해 Python 의 함수 가 필요한 경우 Tensorflow.js에서 사용할 수 있습니다 tf.data.zip().

여기에 문서화 된 Tensorflow.js의 tf.data.zip ()


3

Javascript 자체에는 내장되어 있지 않습니다. 일반적인 Javascript 프레임 워크 (예 : 프로토 타입) 중 일부는 구현을 제공하거나 직접 작성할 수 있습니다.


1
링크? 또한, jQuery가 그렇게했다면 더 관심이있을 것입니다. 왜냐하면 제가 사용하고있는 것이기 때문입니다
.


2
그러나 jQuery 하나는 Python이 아닌 객체를 반환하므로 배열이 아닌 객체를 반환하므로 두 목록을 함께 압축 할 수 없다는 점에서 Python과 약간 다르게 작동합니다.
Amber

저자는 jQuery를 동등한 것으로 호출해서는 안된다.
pq.

3

@Brandon과 마찬가지로 Underscorezip 기능을 권장 합니다. 그러나 가장 긴 입력 길이를 반환하는 데 필요한 값을 zip_longest추가 하는 것처럼 작동 undefined합니다.

mixin메소드를 사용하여 라이브러리의 자체 소스를 기반으로 zipShortestPython과 같은 역할 을하는 밑줄을 확장 했습니다 .zipzip

당신은 당신의 일반적인 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;
}});

의견없이 공감? 이 답변을 개선하게되어 기쁘지만 피드백 없이는 할 수 없습니다.
Pat

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

그리고 이것은 파이썬의 고전적인 "n-group"관용구입니다 zip(*[iter(a)]*n).

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

나는 이것에 어떤 문제가 있는지 궁금해한다. 나는 똑같은 것을 썼다. 나에게 그것은 다른 모든 것보다 기분이 좋지만 어쩌면 우리 둘 다 잘못되었을 수도 있습니다 ... 흐름 유형을 추가하는 방법을 찾고 있었지만 어려움을 겪고 있습니다 : D.
cglacet

0

Mochikit 도서관이 많은 다른 기능 파이썬처럼 제공한다. 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.log하십시오 alert.

결과 창은 무엇입니까?
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.