#javascript #arrays
#javascript #массивы
Вопрос:
Я хочу создать следующий массив на основе еще 2 массивов:
array1 = ['a', 'b']
array2 = [1,2,3]
Я хочу создать следующий массив
newArray = [['a',1], ['a',2], ['a',3], ['b',1], ['b',2], ['b',3]]
Вот мой код:
var test1 = ['a', 'b'];
var test2 = [1,2,3], arr1 = [], arr2 = [];
for(var i = 0; i < test1.length; i ){
arr1 = [];
arr1.push(test1[i]);
for(var x = 0; x < test2.length; x ){
if(arr1.length > 1)
arr1.pop();
arr1.push(test2[x])
arr2.push(arr1);
}
}
console.log("arr1:",JSON.stringify(arr1), "arr2:" ,JSON.stringify(arr2));
Но он возвращает последний элемент второго массива.
[['a',3], ['a',3], ['a',3], ['b',3], ['b',3], ['b',3]]
Почему это происходит?
Ответ №1:
Любой другой ответ о длинах массивов и тому подобных вещах неверен. Единственная причина, по которой вы получаете 3
(или независимо от последнего значения / длины) повсюду, заключается в том, что массивы по ссылке, а functions
не for-loops
создают лексическую область. Это одна из причин, по которой вы слышите, что «функции являются первоклассными гражданами в javascript». Это классический пример, который часто используется в интервью, чтобы сбить с толку разработчиков, которые не привыкли к тому, как на самом деле работает scoping в javascript. Есть несколько способов исправить это, которые включают в себя обертывание внутренних элементов циклов в функциональных областях и передачу индекса, но я хотел бы предложить более «ориентированный на javascript» подход, который заключается в решении проблемы с функциями.
Посмотрите этот пример (который, кстати, также является четким способом реализации вашей цели.)
var test1 = ['a', 'b'];
var test2 = [1,2,3];
// this will iterate over array 1 and return
// [[ [a,1],[a,2],[a,3] ],[ [b,1],[b,2],[b,3] ]]
var merged = test1.map(function(item1){
return test2.map(function(item2){
return [item1, item2];
});
});
//this is a slick way to 'flatten' the result set
var answer = [].concat.apply([],merged )
console.log(answer) //this is it.
Функции ()
создают область видимости, а не «скобки» {}
. Обычно самое простое решение — использовать функции для решения ваших проблем, поскольку они создают лексическую область видимости. Например, самая надежная библиотека в npm, Lodash, основана на этом. Я думаю, что вы будете писать все меньше и меньше циклов изо дня в день js по мере продвижения и использовать больше функций.
Рабочий пример на скрипке js: https://jsfiddle.net/3z0hh12y/1 /
Вы можете прочитать больше о областях и замыканиях здесь https://github.com/getify/You-Dont-Know-JS/blob/master/scope & closures/ch1.md
И еще одна вещь: когда вы думаете, что хотите цикл в js, вы обычно хотите Array.map()
, особенно если вы переназначаете значения.
Комментарии:
1. хороший! должно ли это быть
return [item1, item2]
для создания массива, как просил OP?2. позвольте мне дважды проверить это, похоже, да, у меня это было в моей скрипке, редактируя. Теперь он обновлен. Похоже, я их перевернул. Рабочая демонстрация и ответ должны быть правильными.
3. @J.D.Pace интересно, что Lodash часто превосходит native .map, если вам интересно. Отличная библиотека и даже имеет функцию .flatten(), чтобы избежать необходимости использовать этот хитрый трюк с выравниванием — гораздо легче читать. Я рекомендую Lodash для такого рода операций
4. @the_5imian Я посмотрел на Lodash для других проектов. Возможно, пришло время вернуться к нему. Проверьте синтаксис в ответе, описывающем декартово произведение.
5. @J.D.Pace в основном, как Lodash справится с этим. Кроме того, функциональные манипуляции
rx.js
с ,most.js
,highland.js
и многими другими библиотеками также делают это. Этот ответ похож на то, как это делают многие серьезные библиотеки — единственная причина, по которой я не написал именно это, заключается в том, что я хотел объяснить «почему» этого и ответить на родном JS (для всех, кто приходит). В настоящее время для наблюдаемых и функционального преобразования, most.js это самое быстрое, что мне известно на данный момент. Взгляните на motorcyle.js для примера в контексте FRP.
Ответ №2:
Ваша проблема заключается во втором цикле. поскольку у вас есть единственная ссылка на ary1, вы переписываете значение с каждым циклом.
итак, при первом цикле ваш массив будет иметь
[['a',1]]
но поскольку у вас есть только одна ссылка на ary1, вы просто редактируете значение, которое вы только что ввели в ary2.
итак, тогда вы получаете это:
[['a',2],['a',2]]
вы можете подумать, что вводите новый массив, но на самом деле это тот же самый массив! итак, шаблон продолжается, и вы получаете результат, который вы видите.
Ответ №3:
Это происходит потому, что вы удаляете элементы из arr1, как только его длина превышает 1, поэтому сохраняется только последний элемент.
Попробуйте это:
var test1 = ['a', 'b'];
var test2 = [1,2,3];
var arr1 = [], arr2 = [];
for(var i = 0; i < test1.length; i ) {
for(var x = 0; x < test2.length; x ) {
arr1[arr1.length] = [test1[i], test2[x]];
}
}
console.log(JSON.stringify(arr1));
Ответ №4:
Помимо решений для фиксированного стиля для двух массивов, вы можете использовать другой подход для получения результата. вы могли бы использовать итеративный и рекурсивный подход для переменной длины частей и их длины.
function combine(array) {
function c(part, index) {
array[index].forEach(function (a) {
var p = part.concat([a]);
if (p.length === array.length) {
r.push(p);
return;
}
c(p, index 1);
});
}
var r = [];
c([], 0);
return r;
}
console.log(combine([['a', 'b'], [1, 2, 3]]));
console.log(combine([['a', 'b', 'c'], ['1', '2', '3', '4'], ['A', 'B']]));
console.log(combine([['a', 'b', 'c'], ['1', '2', '3', '4'], [['A'], ['B']]]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Комментарии:
1. Здесь одна небольшая проблема. Если ваши элементы массива являются массивами, то
.concat()
они будут разворачиваться. ie попробуйте[['a', 'b', 'c'], ['1', '2', '3', '4'], [['A'], ['B']]]
. У меня такая же проблема в этом , не могли бы вы помочь ..?2. @redu, в приведенном выше случае вы могли бы заключить элемент в квадратные скобки, прежде чем объединять их, например
var p = part.concat([a]);
, возможно, это также поможет в вашей проблеме (concar уменьшает только один уровень массива).3. Вы упорядочиваете все элементы там? ДА.. Это то, о чем я думал, но мне показалось грязным взломом. Хм, нет .. если подумать, в вашем случае это выполнимо, я полагаю.
4. это просто обходной путь для автоматического оператора rest of
concat
. да, это выглядит немного банально.
Ответ №5:
Я думаю, что правильный функциональный подход был бы выполнен с помощью одного liner reduce и nested map duo.
var arr = ['a', 'b'],
brr = [1,2,3],
result = arr.reduce((p,c) => p.concat(brr.map(e => [c,e])),[]);
console.log(JSON.stringify(result));
Ответ №6:
ваш подход немного отличается, вам лучше воспользоваться этим подходом (который, я думаю, довольно прост): DEMO
var array1 = ['a', 'b'],
array2 = [1,2,3],
nextArray=[];
for(var i=0, array1Length=array1.length; i < array1Length; i ){
for(var x=0, array2Length=array2.length; x < array2Length; x ){
nextArray.push([array1[i],array2[x]]);
}
}
console.log(nextArray);
Комментарии:
1. Не могли бы вы хотя бы объяснить, почему это «выключено»? Вот что на самом деле задает вопрос.
Ответ №7:
Да, это намного сложнее, чем должно быть. Результат, который вы хотите, называется декартовым произведением и может быть сгенерирован следующим образом:
const cartesianProduct = (a, b) => {
const rv = [];
a.map(ae => b.map(be => rv.push([ae, be])));
return rv;
};
const array1 = ['a', 'b']
const array2 = [1,2,3]
console.log(JSON.stringify(cartesianProduct(array1, array2)));
Если бы Javascript6 имел flatMap
, это было бы еще проще:
const cartesianProduct = (a, b) =>
a.flatMap(ae => b.map(be => [ae, be]));
Комментарии:
1. Пожалуйста, объясните мне синтаксис =>.
2. @J.D.Pace Это функция со стрелкой в ES6.
3. Это просто использование map в качестве итератора по внешнему массиву. Не очень элегантно.
4. @Redu — как я уже сказал, было бы лучше, если бы в языке была функция flatMap() или если вы добавите ее (используя подчеркивание или тому подобное).
Ответ №8:
Я немного изменил ваш код (но не слишком сильно, я старался придерживаться того же подхода). Решением было создать новый array ( arr1
) во внутреннем цикле. В противном случае первая правильная пара ['a', 1]
будет переопределена сразу после того, как begin нажал arr2
на следующий проход цикла.
var test1 = ['a', 'b'];
var test2 = [1,2,3];
var arr2 = [];
for(var i = 0; i < test1.length; i ){
for(var x = 0; x < test2.length; x ){
var arr1 = [];
arr1.push(test1[i]);
if(arr1.length > 1){
arr1.pop();
}
arr1.push(test2[x]);
arr2.push(arr1);
}
}