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

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

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

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

Ответ 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

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


Вот онелинер:

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

Ответ 2

Откройте библиотеку Underscore.

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

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

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

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

Ответ 3

В дополнение к превосходному и исчерпывающему ответу 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 -instance. Array.prototype.map этого можно использовать Array.prototype.map, если возникает 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 передает индекс в качестве второго аргумента своей функции-аргумента, используется функция-обертка.

Ответ 4

Наряду с другими функциями, подобными 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

Ответ 5

Python имеет две функции: 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#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, "None" ], [2, false, "None" , "None" ],
[3, -378, "Нет", "Нет никого" ], ['a', '337', 'None,' Is Is None ']]

Ответ 6

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

Ответ 7

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

Ответ 8

Как @Brandon, я рекомендую Underscore zip. Тем не менее, он действует как 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;
}});

Ответ 9

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 задокументирован здесь

Ответ 10

Вы можете сделать функцию полезности с помощью 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]]

Ответ 11

Вы можете уменьшить массив массивов и отобразить новый массив, взяв результат индекса внутреннего массива.

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

Ответ 12

Вариант решения ленивого генератора:

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

Ответ 13

Библиотека Mochikit предоставляет эту и многие другие подобные Python функции. разработчик Mochikit также является поклонником Python, поэтому он имеет общий стиль Python, а также обматывает асинхронные вызовы в скрученной структуре.

Ответ 14

Я попробовал это в чистом 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;
}

Ответ 15

Это приведет к отключению 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);
  }
}

Ответ 16

Если вы в порядке с ES6:

const zip = (arr,...arrs) =>(
                            arr.map(
                              (v,i) => arrs.reduce((a,arr)=>[...a, arr[i]], [v])))