【问题标题】:Generating 3D array from 2D array从 2D 数组生成 3D 数组
【发布时间】:2016-06-14 08:27:29
【问题描述】:

我正在尝试创建一个音乐游戏,我必须从基本的 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 案例,以覆盖双平面。

标签: javascript multidimensional-array


【解决方案1】:

问题是当你分配一个等于这样的数组的变量时:

someVar = someArray;

...它不会复制数组,它会创建对 same 数组的第二个引用。 (这适用于所有对象,数组是一种对象。)所以在你的循环之后,你说过:

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

...note3 中的所有元素都引用同一个底层数组。

要制作实际副本,您可以使用循环手动复制所有元素,也可以使用.slice() method 为您制作副本:

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

【讨论】:

  • 这个解释很清楚!有一个更好的启动解决方案!非常感谢!
【解决方案2】:

TL;DR,这样做:

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

说明:

问题在于在 Javascript 中传递“按值”和“按引用”之间的差异。

当您将原始值分配给变量时,例如a = "string";,然后将其分配给另一个变量,例如b = a;,该值将传递给b'按值':它的值 分配给b,但b 引用了内存的不同部分。现在内存中有两个“字符串”值,一个用于a,一个用于b。

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

这不适用于非原始类型,例如数组。这里将值传递给b'by reference',意味着内存中仍然只有一个[1,2,3]数组,a和b都指向它。这意味着您更改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) 来做到这一点。

编辑:阅读更多here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 2011-11-14
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多