从二维数组生成三维数组

4

我正在尝试创建一个音乐游戏,需要从基本的2D数组生成一个3D数组。计划是在修改前将2D数组复制粘贴4次到3D数组中,如下所示:

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?

现在问题似乎是for循环将方法应用于每个数组(0到3)。
note3[0][1]的期望输出是C E G,note3[1][1]应该是C Eb G,note[2][1]应该是C Eb Gb,note[3][1]应该是C E G#。
非常感谢您的帮助!
下面是(可行的)升降调方法供参考:
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;
}

请注意,您的 flat() 函数需要像 sharp() 函数一样有一个 else if 情况,以覆盖双降。 - nnnnnn
2个回答

3

简而言之,请执行以下操作:

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

解释:

问题出在Javascript中传递参数时以“按值传递”和“按引用传递”之间的差异。

当你将一个基本类型的值赋给一个变量,比如a = "string";,然后将其赋给另一个变量,比如b = a;,这个值就是“按值传递”给了b:它的值被赋给了b,但是b引用了内存的不同部分。现在内存中有两个“string”值,一个是a的,一个是b的。

a = "string";
b = a;
a = "gnirts";
console.log(b);   // "string" 

这种方式不适用于非原始类型,如数组。在这里,值被“按引用”传递给b,这意味着内存中仍然只有一个[1, 2, 3]数组,并且ab都指向它。这意味着如果您更改a中的元素,则b中的元素也会更改,因为它们引用内存中相同的数组。所以你得到了这个:
a = [1, 2, 3];
b = a;
a[0] = "hello";
console.log(b);   // ["hello", 2, 3]

b[0] 的值已经发生变化,因为它和 a[0] 引用的是内存中同一个位置。为了解决这个问题,在将 note3base 传递给另一个变量时,我们需要明确地复制它,而不仅仅是通过引用传递。我们可以像上面那样使用 note3base.slice(0) 来实现。

编辑:在此处阅读更多信息。


3
问题在于当你将一个变量赋值为一个数组时,代码会是这样的:
someVar = someArray;

它不会复制数组,而是创建对同一数组的第二个引用。这适用于所有对象,而数组是对象的一种类型。因此,在循环之后,您可能会说:

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

所有note3中的元素都指向同一个底层数组。

如果要实际复制,您可以手动使用循环将所有元素复制到另一个数组中,或者您可以使用.slice() 方法为您创建副本:

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

但这只能解决问题的一半,因为note3base本身包含对其他数组的引用,而.slice()只会复制这些引用。也就是说,虽然note3[0]note3[1](还有2和3)将引用不同的数组,但note3[0][0]note3[1][0]note3[2][0]note3[3][0]将引用相同的["C", "E", "G"]数组。(等等。)
你需要使用所谓的“深度复制”。您可以使用嵌套循环来实现:
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();
  }
}

说了这么多,我认为一个更简单的方法是,不要使用一个指向数组的note3base变量,而是将其作为一个返回新数组的函数:

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

由于该函数使用了数组字面量,每次调用它都会创建一个全新的数组。因此,您可以执行以下操作,无需使用 .slice() 或嵌套循环:

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

这是一个非常清晰的解释!而且还有更好的解决方案!非常感谢! - Poh Zi How

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接