Reputation: 67
The alltones
array contains all the possible notes in music scale.
var alltones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ];
I want to take the user's input of a tone, and then construct a list that contains certain elements of the array.
Say I start at alltones[5]
or "F"
, and want to take every second element of the array from that point and put it into my list, until I get back around to "F"
. The array is more like a circle than straight list. I'm a little unsure of how the array operates once the loop reaches the last element.
Do I approach the problem by generating a new array, based on the users input. Or is there a way that JavaScript knows to loop back to the start of an array once it reaches the end such that it treats the array like a circle?
An example:
User input = F
The output that I am seeking is to count (every two items) from F until we get back around the array to F
so the desired output would be => F G A B C# D#
Upvotes: 4
Views: 1139
Reputation: 1535
How about this:
function getNotes(index) {
var list = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ];
return list.splice(index).concat(list).filter(function(note, i) {
return i % 2 == 0;
});
}
getNotes(3); // ["D#", "F", "G", "A", "B", "C#"]
What this does is reorganize the array first so that the selected index is shifted to the beginning and everything before that is moved to the end, and then filters out every other note. If you'd rather have the getNotes
function consume a string, you can use the indexOf
method. indexOf
and filter
(used above) are part of ECMAScript 5 so you may need polyfills depending on what browsers you plan on supporting.
Here's a Fiddle with some examples.
Upvotes: 1
Reputation: 926
Here is the way to loop back to the start of an array once it reaches the end:
arr[index%arr.length]
, if index
> arr.length
, it will "loop" to the start.
this tech is efficient, and works for almost every language.
var alltones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ];
function generateMusic(startIndex) {
var i = 0, music = [],
tonesCount = alltones.length
do {
music.push(alltones[(startIndex+i)%tonesCount]);
} while ((i+=2) < tonesCount);
return music;
}
Result:
console.log(generateMusic(3)); //["D#", "F", "G", "A", "B", "C#"]
console.log(generateMusic(5)); //["F", "G", "A", "B", "C#", "D#"]
Upvotes: 1
Reputation: 7642
doing it in two loops one for array members starting after the initial point and another from the beginning is a better way; the key here is to start from the beginning depending where we left off at the end of the array.
you can create a generic function like
var alltones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ];
function build_tones(arr, start, jump_by){
var new_list = [];
//finish first iteration and get the last index so that we can offset accordingly from the beginning
var get_last = (function(){
var last;
for(i = start; i <= arr.length; i+=jump_by){
new_list.push(arr[i]);
last = i;
}
return last;
})();
//do the second iteration by picking up from the index left and continue from first
for(i = 0 + arr.length%get_last; i < start; i+=jump_by){
new_list.push(arr[i]);
}
return new_list;
}
var result = build_tones(alltones, 5, 2);
console.log(result);
Upvotes: 0
Reputation: 89113
There isn't an existing Array method to do what you want, but one is easily defined using Array.prototype.slice
and Array.prototype.concat
:
// move i elements from the start of the array to the end of the array
Array.prototype.lcycle = function(i) {
var xs = this.slice(0,i);
var ys = this.slice(i);
return ys.concat(xs)
};
// move i elements from the end of the array to the start of the array
Array.prototype.rcycle = function(i) {
var xs = this.slice(0,-i);
var ys = this.slice(-i);
return ys.concat(xs);
};
And then you can use lcycle
and rcycle
as normal array methods:
>>> alltones.lcycle(3)
[ "D#" , "E" , "F" , "F#" , "G" , "G#" , "A" , "A#" , "B" , "C" , "C#" , "D" ]
>>> alltones.rcycle(4)
[ "G#" , "A" , "A#" , "B" , "C" , "C#" , "D" , "D#" , "E" , "F" , "F#" , "G" ]
Note that both of these methods return a new array. If you wanted to mutate the original array, you could define similar methods using Array.prototype.splice
.
// move i elements from the start of the array to the end of the array, mutating the original array
Array.prototype.lcycle_m = function(i) {
var args = this.splice(0,i);
args.unshift(0);
args.unshift(this.length);
this.splice.apply(this, args);
};
// move i elements from the end of the array to the start of the array, mutating the original array
Array.prototype.rcycle_m = function(i) {
var args = this.splice(-i);
args.unshift(0);
args.unshift(0);
this.splice.apply(this, args);
};
And again, you can can use lcycle_m
and rcycle_m
as normal array methods:
>>> alltones.lcycle_m(3)
>>> alltones
[ "D#" , "E" , "F" , "F#" , "G" , "G#" , "A" , "A#" , "B" , "C" , "C#" , "D" ]
>>> alltones.rcycle_m(3)
>>> alltones
[ "C" , "C#" , "D" , "D#" , "E" , "F" , "F#" , "G" , "G#" , "A" , "A#" , "B" ]
Upvotes: 2
Reputation: 389
You can do it in two loops, as JS has no built in support for circles.
for (var i = users_input; i < alltones.length; i += 1) {
... use alltones[i] for calc
}
for (var i = 0; i < users_input; i += 1) {
... use alltones[i] for calc
}
or something along that line. There really is no need for creating a new array.
Upvotes: 1