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

Я изменил flm "S отличный ответ взять произвольное количество массивов:

         function* zip(arrays, i = 0) {
  while (arrays[0][i]) {
    yield arrays.map((arr, j) => arr[j < arrays.length - 1 ? i : i++])
  }
 }

Не встроен в сам 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);
  }
}
Другие вопросы по тегам