简体   繁体   中英

What is the most efficient way to populate an array based on other information in Javascript?

I have an array called NoteArray of size 16 in Javascript. I also have a static 2D array called NoteToNumberMapping of size 11x12 that looks like this . Now, as input, I let the user select any combination of the 12 standard notes (CC#-DD#-EFF#-GG#-AA#-B), or none at all. eg "CGG#", or "DD# GAB", or blank etc. PS: The notes could be "out of order" if necessary (eg "DACC#")

For the "CGG#" example, I would like to refer to the 2D array and see that in octave 0, the corresponding numbers for C, G and G# are 0, 7 and 8. Likewise for octave 1, the numbers are 12, 19 and 20. Continuing in this fashion, the first 16 numbers are: 0, 7, 8, 12, 19, 20, 24, 31, 32, 36, 43, 44, 48, 55, 56, 60.

I would like to assign these 16 numbers to my initial NoteArray.

My approach

I implemented the 2D array as a 1D array of 128 items, containing the letters. So item 0 was C0, item 1 was C#0 all the way till item 127 (G10). I wanted to use the fact that the values in the 2D table were a linear progression from 1-127. But I ended up with 3 nested for loops and still couldn't get it to work. What would be an efficient way to achieve this in Javascript?

You could first make a lookup table from note name to relative note number:

var relativeNoteNumberTable = {
    'C': 0, 'C#': 1, 'D': 2, 'D#':  3, 'E':  4, 'F': 5, 'F#': 6,
    'G': 7, 'G#': 8, 'A': 9, 'A#': 10, 'B': 11
};

Then you can get the relative note numbers of the selected notes relatively easily:

var selectedNotes = 'C G G#';
var relativeNoteNumbers = selectedNotes.split(' ')
    .filter(function(note) { return note })  // remove blanks
    .map(function(note) { return relativeNoteNumberTable[note] });
console.log(relativeNoteNumbers);  // [0, 7, 8]

Then observe that for each octave, the MIDI note number increases by 12. If you want those notes for each octave until you have 16 notes, you could do something like this:

var notes = [];
while(notes.length < 16) {
    notes.push(Math.floor(notes.length / relativeNoteNumbers.length) * 12 +
               relativeNoteNumbers[notes.length % relativeNoteNumbers.length]);
}

You should be able to do this in linear time complexity. You should use an object to store your 2D array instead like this:

my2dArray = {
  "0C": 0,
  "0C#": 1,
  "0D": 2,
  ...
}

Then, you will not have to iterate through the 2D array to get the map value you want. You simply do my2dArray[myKey] and this has a constant lookup time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM