Javascript эквивалент функции zip в Python
Есть ли 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]]
24 ответа
Обновление 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, то есть версии с несколькими аргументами), то 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.)
Проверьте библиотеку Underscore.
Underscore предоставляет более 100 функций, которые поддерживают как ваши любимые функциональные помощники: отображение, фильтрация, вызов, а также более специализированные функции: привязка функций, создание шаблонов javascript, создание быстрых индексов, тестирование на глубокое равенство и так далее.
- Скажи людям, которые сделали это
Я недавно начал использовать его специально для zip()
функция, и это оставило большое первое впечатление. Я использую JQuery и CoffeeScript, и это просто отлично с ними. Подчеркивание начинается там, где они заканчиваются, и до сих пор меня это не подводило. О, кстати, это только 3kb минимизировано.
Проверьте это.
Современный пример 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.
В дополнение к превосходному и исчерпывающему ответу 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
над for
-loops:
См.: Какой самый эффективный способ объединения [1,2] и [7,8] в [[1,7], [2,8]]
Примечание: базовые типы, такие как false
а также undefined
не владеть прототипом объектной иерархии и, следовательно, не выставлять toString
функция. Следовательно, они показаны как пустые в выходных данных.
Как parseInt
Второй аргумент - основание / число основание, в которое нужно преобразовать число, и так как map
передает индекс в качестве второго аргумента своей функции-аргумента, используется функция-обертка.
pythonic
предложения zip
наряду с некоторыми другими Python-подобными функциями:
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
Питон имеет две функции: zip и itertools.zip_longest. Реализация на JS/ES6 выглядит следующим образом:
Реализация Python`s Zip на 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's zip_longest на JS/ES6
( https://docs.python.org/3.5/library/itertools.html?highlight=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, не определено, не определено], ['a', '337', не определено, не определено]]
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", "Нет", "Нет"]]
Вы можете сделать функцию полезности с помощью 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 ]]
1. Модуль Npm: zip-array
Я нашел модуль npm, который можно использовать как версию Python для JavaScript 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 задокументирован здесь
Не встроен в сам Javascript. Некоторые из распространенных структур Javascript (например, Prototype) предоставляют реализацию, или вы можете написать свою собственную.
ES2020 самый короткий вариант:
function * zip(arr1, arr2, i = 0) {
while(arr1[i]) yield [ arr1[i], arr2[i++] ];
}
[ ...zip(arr1, arr2) ] // result
Как и @Brandon, я рекомендую функцию почтового индекса Underscore. Тем не менее, он действует как zip_longest
добавляя undefined
значения по мере необходимости, чтобы вернуть что-то длина самого длинного ввода.
Я использовал mixin
метод расширения подчеркивания с zipShortest
, который действует как Python zip
на основе собственного источника библиотеки для 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;
}});
Вариант решения ленивого генератора:
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)))]
Вы можете уменьшить массив массивов и отобразить новый массив, взяв результат индекса внутреннего массива.
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);
Я не парень, использующий javascript, но мне кажется, что многие из этих ответов пытаются найти самое красивое и умное решение, используя которое нормально, но для кого-то вроде меня, который не использует javascript каждый день, есть несколько альтернатив, которые, возможно, могут быть немного более читабельным.
Возможно, один из способов избежать милого и умного кода:
function zip(a,b){
// pre-allocate an array to hold the results
rval=Array(Math.max(a.length, b.length));
for(i=0; i<rval.length; i++){
rval[i]=[a[i],b[i]]
}
return rval
}
Если вам нравятся генераторы:
function* _zip(a,b){
len = Math.max(a.length, b.length) // handle different sized arrays
for(i=0; i<len; i++) { yield [a[i],b[i]] }
}
Или если вы действительно хотите использовать
Array.map
:
function map(a,b){
x = a.length > b.length ? a : b // call map on the biggest array
return x.map((_,i)=>[a[i],b[i]])
}
Как я уже сказал, я не обычный специалист по javascript, так что это не самые элегантные решения, но они читабельны для меня.
Я попробовал это на чистом 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;
}
Это не пуленепробиваемый, но все же интересно.
Генераторный подход к функции zip pythons.
function* zip(...arrs){
for(let i = 0; i < arrs[0].length; i++){
a = arrs.map(e=>e[i])
if(a.indexOf(undefined) == -1 ){yield a }else{return undefined;}
}
}
// use as multiple iterators
for( let [a,b,c] of zip([1, 2, 3, 4], ['a', 'b', 'c', 'd'], ['hi', 'hello', 'howdy', 'how are you']) )
console.log(a,b,c)
// creating new array with the combined arrays
let outputArr = []
for( let arr of zip([1, 2, 3, 4], ['a', 'b', 'c', 'd'], ['hi', 'hello', 'howdy', 'how are you']) )
outputArr.push(arr)
Вот мое решение
let zip = (a, b) => (a.length < b.length
? a.map((e, i) => [e, b[i]])
: b.map((e, i) => [a[i], e]))
Я создал простую функцию для этого с возможностью предоставления функции застежки-молнии.
function zip(zipper, ...arrays) {
if (zipper instanceof Array) {
arrays.unshift(zipper)
zipper = (...elements) => elements
}
const length = Math.min(...arrays.map(array => array.length))
const zipped = []
for (let i = 0; i < length; i++) {
zipped.push(zipper(...arrays.map(array => array[i])))
}
return zipped
}
https://gist.github.com/AmrIKhudair/4b740149c29c492859e00f451832975b
Ниже приведен быстрый и эффективный способ сделать это, используя библиотеку , оператор zip:
const {pipe, zip} = require('iter-ops');
const i = pipe(array1, zip(array2, array3));
console.log(...i); //=> [ 1, 'a', 4 ] [ 2, 'b', 5 ] [ 3, 'c', 6 ]
Библиотека обрабатывает все входные данные как итерации, поэтому они повторяются только один раз. И таким же образом он может обрабатывать все типы итерируемых объектов —
Iterable
,
AsyncIterable
,
Iterator
,
AsyncIterator
.
PS Я автор iter-opsiter-ops .
Библиотека Mochikit предоставляет эту и многие другие функции, подобные Python. Разработчик Mochikit также является поклонником Python, поэтому он имеет общий стиль Python, а также обертывает асинхронные вызовы в скрученной среде.
Эквивалентной функции нет. Если у вас всего несколько массивов, вы должны использовать
for
цикл, чтобы получить индекс, а затем использовать индекс для доступа к массивам:
var array1 = [1, 2, 3];
var array2 = ['a','b','c'];
for (let i = 0; i < Math.min(array1.length, array2.length); i++) {
doStuff(array1[i], array2[i]);
}
У вас может быть внутренний цикл над массивами, если у вас их больше.
Если вы в порядке с ES6:
const zip = (arr,...arrs) =>(
arr.map(
(v,i) => arrs.reduce((a,arr)=>[...a, arr[i]], [v])))
Это Dimitris строку с Dimitris на основе итератора:
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);
}
}