2016-10-02 2 views
3

Я хочу сделать следующий массив на основе еще 2 массивов:Confused о массивах на JavaScript

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) 
 
     \t 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]] 

Почему это происходит?

ответ

5

Каждый другой ответ на вопрос о длине массива, и подобные вещи не являются правильными. Единственная причина, по которой вы получаете 3 (или независимо от последнего значения/длины) по всему месту, - это то, что массивы являются ссылочными, а functions, а не for-loops создают лексическую область. Это одна из причин, по которой вы слышите, что «функции являются первоклассными гражданами в javascript». Это классический пример, и часто в интервью также используется для отключения разработчиков, которые не привыкли к тому, как работает определение области видимости в 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]; 
 
    }); \t 
 
    }); 
 

 
//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%20%26%20closures/ch1.md

И еще одна вещь: , когда вы думаете, что вы хотите петлю в JS, вы обычно хотите Array.map(), особенно если вы переопределяете значения.

+1

приятный! должно ли оно быть 'return [item1, item2]' для создания массива в виде запроса OP? –

+1

Позвольте мне дважды проверить это, похоже, да, у меня это было в моей скрипке, редактирование. Теперь он обновляется. Похоже, я их оборвал. Рабочая демонстрация и ответ должны быть правильными. –

+0

К бенчмаркам! –

-1

ваш подход откусил, то лучше взять этот подход (который довольно просто, я думаю): 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); 
+0

Не могли бы вы по крайней мере, объяснить, почему это 'выключено'? Вот что задает вопрос. – Li357

0

Это происходит потому, что вы появляетесь элементами от 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));

-1

Я немного модифицировал ваш код (но не слишком, я пытался придерживаться того же подхода). Решением было создание нового массива (arr1) во внутреннем цикле. В противном случае первая правильная пара ['a', 1] была бы переопределена сразу после того, как она будет перенесена на 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); 
    } 
} 
0

Проблема возникает во втором цикле. потому что у вас есть единственная ссылка на ary1, над которой вы переписываете значение с каждым циклом.

так первый раз петлями ваш массив будет иметь

[['a',1]] 

, но потому, что вы у вас есть только одна ссылка на ary1 вы просто редактируя значение, которое вы только что толкнул в ary2.

так, то вы получите это:

[['a',2],['a',2]] 

вы можете думать, что вы толкаете новый массив в России, но это на самом деле точно такой же массив! поэтому шаблон продолжается, и вы получаете результат, который видите.

-1

Да, это сложнее, чем нужно. Результат вы хотите называется Cartesian product, и может быть получен следующим образом:

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])); 
+0

Пройдите меня через синтаксис =>. –

+0

@ J.D.Pace Это [функция стрелки] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) в ES6. – Li357

+0

Это просто использование карты в качестве итератора по внешнему массиву. Не очень элегантный. – Redu

0

Помимо решений для фиксированного стиля для два массива, вы можете использовать другой подход для получения результата. вы можете использовать итеративный и рекурсивный подход для переменной длины частей и их длины.

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

+0

Один крошечный вопрос здесь. Если ваши элементы массива являются массивами, то '.concat()' разворачивает их. т.е. try '[['a', 'b', 'c'], ['1', '2', '3', '4'], [[A '], [' B ']]] '. У меня такая же проблема в [этой] (https://repl.it/DmXN) вы могли бы дать руку ..? – Redu

+0

@redu, в приведенном выше случае вы можете обернуть элемент в скобки, прежде чем конкатенировать их, например, 'var p = part.concat ([a]); возможно, это также помогает в вашей проблеме (concar уменьшает только один уровень массива). –

+0

Вы разбираете все предметы там? Да .. Это то, что я подумал, но показалось мне грязным хаком. Хм, нет ... на секунду подумал, что в твоем случае это выполнимо, я полагаю. – Redu

1

Я думаю, правильный функциональный подход будет сделан одним вкладышем уменьшить и вложенная карту дуэт.

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