it-roy-ru.com

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]]
167
pq.

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 в случаях Edge, когда массивы не имеют одинаковый размер, безмолвно притворяясь, что более длинные части массивов не существуют:

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 или если вы хотите, чтобы аналогичным образом было переменное число списков в качестве входных данных.


addendum:

Чтобы сделать этот дескриптор итеративным (например, в Python вы можете использовать Zip для строк, диапазонов, объектов карты и т.д.), Вы можете определить следующее:

function iterView(iterable) {
    // returns an array equivalent to the iterable
}

Однако, если вы напишите Zip следующим образом way , даже в этом нет необходимости:

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

148
ninjagecko

Проверьте библиотеку Подчеркни .

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

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

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

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

28
Brandon

В дополнение к превосходному и исчерпывающему ответу ninjagecko, все, что нужно для Zip двух JS-массивов в "Tuple-mimic", это:

//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- циклов:

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

Zip tests

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

12
Lorenz Lo Sauer

Питон имеет две функции: 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],
    []
));

[]

Реализация Zip_longest Python для 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, не определено, не определено], ['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', 'Нет', 'Есть Никто' ] ]

4
Seti Volkylany

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

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

3
Amber

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
3
Keyvan

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

Как и @Brandon, я рекомендую Underscore 's Zip function. Однако он действует как 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;
}});
2
Pat

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

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

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

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

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

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

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

1
Huan

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

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

1
Nina Scholz

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

0
Keith

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

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)))]
0
georg

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

Это не пуленепробиваемый, но все же интересно.

0
user1385191