Poh Zi How - 4 months ago 10
Javascript Question

# Generating 3D array from 2D array

I'm trying to create a music game where I have to generate a 3D array from a basic 2D array. The plan was to copy and paste the 2D array 4 times into the 3D array before modifying it, as shown:

``````var note3base = [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];

var note3 = new Array(4);

for (h=0;h<note3.length;h++){
note3[h] = note3base;
} //creates 4 copies of note3base in a 3d-array to be modified

for (i=0;i<note3[0].length;i++){
note3[1][i][1] = flat(note3[1][i][1]); //minor
note3[2][i][1] = flat(note3[2][i][1]);
note3[2][i][2] = flat(note3[2][i][2]); //dim
note3[3][i][2] = sharp(note3[3][i][2]); //aug
} //how did everything become the same?
``````

The problem now seems to be that the
`for`
loop seems to apply the method to every single array (0 through 3).

The desired output for note3[0][1] would be C E G, note3[1][1] would be C Eb G, note[2][1] would be C Eb Gb, note[3][1] would be C E G#.

Any help is greatly appreciated!

I've included the (working) sharp and flat methods below for reference:

``````function sharp(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "x");
} else if (note.charAt(1) == "b"){
newnote = note.replace("b", "");
} else {
newnote = note + "#";
}
return newnote;
}

function flat(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "");
} else {
newnote = note + "b";
}
return newnote;
}
``````

The problem is that when you assign a variable equal to an array like this:

``````someVar = someArray;
``````

...it doesn't make a copy of the array, it creates a second reference to the same array. (This applies to all objects, and arrays are a type of object.) So after your loop, where you've said:

``````for (h=0;h<note3.length;h++){
note3[h] = note3base;
``````

...all of the elements in `note3` refer to the same underlying array.

To make an actual copy, you can manually copy all of the elements across using a loop, or you can use the `.slice()` method to make a copy for you:

``````for (h=0;h<note3.length;h++){
note3[h] = note3base.slice();
}
``````

But that will only solve half of the problem, because `note3base` itself contains references to other arrays, and `.slice()` will just copy these references. That is, although `note3[0]` and `note3[1]` (and 2 and 3) will refer to different arrays, `note3[0][0]` and `note3[1][0]` and `note3[2][0]` and `note3[3][0]` will refer to the same `["C", "E", "G"]` array. (And so forth.)

You need what's called a "deep copy". You could do it with a nested loop:

``````for (h=0;h<note3.length;h++){
// create this element as a new empty array:
note3[h] = [];
// for each 3-note array in note3base
for (var k = 0; k < note3base.length; k++) {
// make a copy with slice
note3[h][k] = note3base[k].slice();
}
}
``````

Having said all that, I think an easier way to do the whole thing would be instead of having a `note3base` variable that refers to an array, make it a function that returns a new array:

``````function makeNote3Array() {
return [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
}
``````

Because the function uses an array literal it will create a brand new array of arrays every time it is called. So then you can do the following, with no need for `.slice()` or nested loops:

``````var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = makeNote3Array();
}
``````
Source (Stackoverflow)