Sebastian Chavez
Sebastian Chavez

Reputation: 117

Confused about arrays on javascript

I want to make the next array based on another 2 arrays:

array1 = ['a', 'b']
array2 = [1,2,3]

I want to create the next array

newArray = [['a',1], ['a',2], ['a',3], ['b',1], ['b',2], ['b',3]]

Here is my code:

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

But it returns the last element of the second array.

[['a',3], ['a',3], ['a',3], ['b',3], ['b',3], ['b',3]]

Why is this happening?

Upvotes: 2

Views: 256

Answers (8)

Redu
Redu

Reputation: 26161

I guess the proper functional approach would be done by a single liner reduce and 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));

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386550

Beside the solutions for fixed style for two array, you might use another approach to get the result. you could use an iterative and recursive approach for variable length of parts an their length.

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

Upvotes: 0

J.D. Pace
J.D. Pace

Reputation: 616

It's occurring because you're popping elements off arr1 once it's length exceeds 1, so the last element is all that persists.

Try this:

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

Upvotes: 0

the_5imian
the_5imian

Reputation: 896

Every other answer about array lengths, and similar things are not right. The only reason you get 3 (or whatever the last value/length is) all over the place is because Arrays are by reference, and functions, not for-loops create lexical scope. This is one of the reasons you hear that 'functions are first-class citizens in javascript'. This is a classical example, and frequently in interviews too, used to trip up devs who are not used to how scoping in javascript really behaves. There are some ways to fix it that involve wrapping the innards of loops in functional scopes, and passing in the index, but I'd like to suggest a more 'javascript centric' approach, and that is to solve the problem with functions.

See this example (which by the way is also a clear way to implement your goal.)

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.

Functions () make scope - not 'brackets' {}. The easiest fix is usually to use functions to solve your problems as they create lexical scope. The most trusted library on npm, lodash, for instance is based on this. I think you'll write less and less loops from day to day js as you progress, and use more functions.

Working example on js fiddle: https://jsfiddle.net/3z0hh12y/1/

You can read more about scopes and closures here https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch1.md

And one more thing: when you think you want a loop in js, you usually want Array.map(), especially if you're remapping values.

Upvotes: 5

antoineMoPa
antoineMoPa

Reputation: 1024

I modified your code a little (but not too much, I tried to keep the same approach). The solution was to create a new array (arr1) in the inner loop. Otherwise, the first correct pair ['a', 1] would get overridden just after begin pushed to arr2 at the next loop pass.

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

Upvotes: -1

Michael Lorton
Michael Lorton

Reputation: 44376

Yeah, that's way more complicated than it needs to be. The result you want is called the Cartesian product, and can be generated like this:

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

If Javascript6 had flatMap it would be even simpler:

 const cartesianProduct = (a, b) => 
    a.flatMap(ae => b.map(be => [ae, be]));

Upvotes: -1

Niles Tanner
Niles Tanner

Reputation: 4021

You problem lies in the second loop. because you have a single reference to ary1 you are over writing the value with each loop.

so first time it loops your array will have

[['a',1]]

but because you you only have one reference to ary1 you are just editing the value you just pushed into ary2.

so then you get this:

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

you may think that you are pushing a new array in but it is in fact the same exact array! so the pattern continues and you get the result that you are seeing.

Upvotes: 0

Amin Jafari
Amin Jafari

Reputation: 7207

your approach is a bit off, you better take this approach (which is pretty simple I think): 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);

Upvotes: -1

Related Questions