Вопрос-Ответ

Javascript equivalent of Python's zip function

Javascript-эквивалент zip-функции Python

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

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

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

Выходной массив должен быть:

var outputArray = [[1,'a',4], [2,'b',5], [3,'c',6]]
Переведено автоматически
Ответ 1

обновление 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 (как в обычном 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

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

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


Вот 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(...)

Вышесказанное предполагает, что массивы имеют одинаковый размер, как и должно быть. Также предполагается, что вы передаете один аргумент list of lists , в отличие от версии 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()
// []

Это будет имитировать itertools.zip_longest поведение Python, вставляя 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. версии с несколькими аргументами), то 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 или генераторы.)

Ответ 2

Ознакомьтесь с библиотекой подчеркивания.


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


– Скажите, кто это сделал

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

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

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

Современный пример 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

В дополнение к превосходному и всеобъемлющему ответу 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, несмотря на то, что это не указано).
Основное отличие от Python zip, izip, ... вытекает из 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 over for-циклов:

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

zip-тесты

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

2023-07-16 19:58 python