Javascript эквивалент функции zip в Python - PullRequest
180 голосов
/ 01 февраля 2011

Существует ли javascript-эквивалент функции zip в Python? То есть, учитывая, что несколько массивов одинаковой длины создают массив пар.

Например, если у меня есть три массива, которые выглядят так:

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

Ответы [ 16 ]

154 голосов
/ 23 апреля 2012

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 указывает, что ES6 имеет синтаксис с переменным аргументом, поэтому следующее определение функции будет действовать как python, но см. ниже для отказа от ответственности ... это не будет его собственным обратным, поэтому zip(zip(x)) не будет равен x, хотя, как указывает Мэтт Крамер zip(...zip(...x))==x (как в обычном питоне 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

(Обратите внимание, что синтаксис ... может иметь проблемы с производительностью в настоящее время и, возможно, вв будущем, поэтому, если вы используете второй ответ с переменными аргументами, возможно, вы захотите его протестировать.)


Вот один из вариантов:

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

Выше предполагается, чточто массивы имеют одинаковый размер, как и должно быть.Он также предполагает, что вы передаете один аргумент list списков, в отличие от версии Python, где список аргументов является вариативным. Если вам нужны все эти «функции», см. Ниже.Это займет всего около 2 дополнительных строк кода.

Следующее будет имитировать поведение Python zip в крайних случаях, когда массивы не имеют одинаковый размер, молча притворяясь, что более длинные части массивов не существуют:

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

Это будет имитировать поведение Python itertools.zip_longest, вставляя undefined, где массивы не определены:

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

Если вы используете эти две последние версии (variadic aka. Множественный аргумент)версий), тогда zip больше не является собственным обратным.Чтобы имитировать идиому zip(*[...]) из Python, вам нужно будет сделать zip.apply(this, [...]), если вы хотите инвертировать функцию zip или если вы хотите аналогичным образом иметь переменное количество списков в качестве входных данных.


addendum :

Чтобы сделать этот дескриптор произвольным (например, в 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.)

30 голосов
/ 31 августа 2011

Ознакомьтесь с библиотекой Underscore .

Underscore предоставляет более 100 функций, которые поддерживают как ваших любимых функциональных помощников: отображение, фильтрация, вызов и другие специализированные вкусности: привязка функций, шаблоны javascript, создание быстрых индексов, глубокое тестирование на равенство и т. д.

- скажем, люди, которые сделали это

Я недавно начал использовать его специально для zip() функция, и это оставило большое первое впечатление.Я использую JQuery и CoffeeScript, и это просто отлично с ними.Подчеркивание начинается там, где они заканчиваются, и до сих пор меня это не подводило.Да, кстати, это всего лишь 3 КБ.

Проверьте это.

14 голосов
/ 28 июля 2013

В дополнение к превосходному и исчерпывающему ответу ninjagecko, все, что нужно, чтобы заархивировать два 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 движках, несмотря на то, что не указано).
Основное отличие от zip, izip, ... Python - результат mapфункциональный стиль, поскольку map требует аргумента-функции.Кроме того, это функция экземпляра Array.Вместо этого можно использовать 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 -loops:

См .: Что является наиболееэффективный способ объединения [1,2] и [7,8] в [[1,7], [2,8]]

zip tests

Примечание: базовые типы, такие как false и undefined, не обладают прототипной иерархией объектов и, следовательно, не предоставляют функцию toString.Следовательно, они выводятся как пустые в выходных данных.
Поскольку вторым аргументом parseInt является основание / число, в которое необходимо преобразовать число, и поскольку map передает индекс в качестве второго аргумента егоАргумент-функция, используется функция-обертка.

5 голосов
/ 07 апреля 2018

Наряду с другими функциями, подобными Python, pythonic предлагает функцию zip, с дополнительным преимуществом возврата вычисленных с отложенным вычислением 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

4 голосов
/ 17 января 2018

Современный пример 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.Обычно это происходит прозрачно, но здесь мы делаем это явно, поскольку мы уступаем шаг за шагом, пока один из них не будет исчерпан.Мы проверяем, исчерпан ли какой-либо из результатов (используя метод .some()) в данном массиве, и если это так, мы прерываем цикл while.

4 голосов
/ 30 декабря 2016

Python имеет две функции: zip и itertools.zip_longest.Реализация на JS / ES6 выглядит следующим образом:

Реализация почтового индекса Python на 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])));
};

Результаты:

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],
    []
));

[]

Реализация Python`szip_longest в 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
        )
    );
};

Результаты:

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

[[1, 667, 111, не определено], [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, ноль], [2, ложь, ноль, ноль], [3, -378, ноль, ноль], ['a', '337', ноль, ноль]]

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

[[1, 667, 111, «Нет»], [2, ложно, «Нет», «Нет»],
[3, -378, «Нет», «Нет»], [«a», «337», «Нет», «Нет»]]

3 голосов
/ 12 апреля 2019

Вы можете использовать служебную функцию с помощью 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 ]]
3 голосов
/ 06 марта 2019

1. Модуль Npm: zip-array

Я нашел модуль npm, который можно использовать как версию javascript для python zip:

zip-array - Javascript-эквивалент функции zip в Python. Объединяет значения каждого из массивов.

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

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

Другой альтернативный выбор - для пользователей Tensorflow.js: если вам нужна функция zip в python для работы с наборами данных tenorflow в Javascript, вы можете использовать tf.data.zip() в Tensorflow.js.

tf.data.zip () в Tensorflow.js, задокументированном в здесь

3 голосов
/ 29 ноября 2012

Как и @Brandon, я рекомендую Подчеркнуть zip . Тем не менее, он действует как zip_longest, добавляя значения undefined по мере необходимости, чтобы вернуть что-то длину самого длинного ввода.

Я использовал mixin метод для расширения подчеркивания с помощью zipShortest, который действует как zip Python, основанный на собственном источнике библиотеки для zip ,

Вы можете добавить следующее к общему 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;
}});
3 голосов
/ 01 февраля 2011

Не встроен в сам Javascript.Некоторые из распространенных структур Javascript (например, Prototype) предоставляют реализацию, или вы можете написать свою собственную.

...