2013-02-09 7 views
4

массив alltones содержит все возможные примечания в музыкальной шкале.Javascript looping поведение

var alltones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ]; 

Я хочу, чтобы пользователь вводил тон, а затем построил список, содержащий определенные элементы массива.

Say Я начинаю в alltones[5] или "F" и хочет взять каждый второй элемент массива с этой точкой и поместить его в свой список, пока я не вернусь вокруг "F". Массив больше похож на круг, чем на прямой. Я немного не знаю, как работает массив, когда цикл достигает последнего элемента.

Я подхожу к проблеме, создавая новый массив на основе ввода пользователей. Или есть способ, который JavaScript знает, чтобы вернуться к началу массива, как только он достигнет конца, так что он рассматривает массив как круг?

Пример: Пользовательский ввод = F
Выход, что я ищу, чтобы подсчитать (каждые два элемента) из F, пока мы не вернемся вокруг массива F
поэтому желаемый результат будет => FGABC# D #

+8

Может быть по модулю индексации, что вы хотите - что-то вроде 'alltones [х% alltones.length]' – zch

+1

Похоже, вы можете создать новый массив, используя кусочек и массив. indexOf и concat перед записью – mplungjan

+0

Можете ли вы показать нам несколько пробных прогонов (ввод и ожидаемый результат), которые вы ищете? Это необходимо для определения того, что вы действительно хотите сделать. –

ответ

2

Существует не existing Array method делать то, что вы хотите, но один легко определяется с помощью Array.prototype.slice и 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); 
}; 

И тогда вы можете использовать lcycle и rcycle как обычные методы массива:

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

Обратите внимание, что оба этих метода возвращают новый массив. Если вы хотите изменить исходный массив, вы можете определить похожие методы, используя 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); 
}; 

И снова, вы можете можете использовать lcycle_m и rcycle_m как обычные методы массива:

>>> 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" ] 
+0

Старый пост ... но это очень хороший ответ. Я никогда не думал о расширении прототипа массива таким образом. – moonman

1

Вы можете сделать это в двух циклах, поскольку JS не имеет встроенной поддержки кругов.

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 
} 

или что-то подобное. Нет необходимости создавать новый массив.

+0

Это звучит слишком сложно. Представьте себе очень сложное тело цикла – Bergi

+2

или один цикл 'for (var i = 0; i rampion

+1

для сложного тела цикла, вы можете переместить логин в функцию и вызвать с помощью аргумента. однострочный звук хорош, я скопирую это в свой ящик для инструментов – kms

1

Как об этом:

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#"] 

Что это делает реорганизовать массив первого так, что выбранный индекс сдвигается к началу и все до того, что перемещается в конец, а затем отфильтровывает все другие заметки , Если вы хотите, чтобы функция getNotes использовала строку, вы можете использовать метод indexOf. indexOf и filter (используются выше) являются частью ECMAScript 5, поэтому вам могут потребоваться полиполки в зависимости от того, какие браузеры вы планируете поддерживать.

Вот Fiddle with some examples.

+2

+1 аккуратное решение, но я удивлен, что вы не поместили второй 'return' в ту же строку;) – phant0m

+0

Просто личные предпочтения, я полагаю. Я считаю его более читаемым; Я всегда добавляю новую строку и отступ для тела функции. – Cecchi

+1

Мой комментарий был явно неудачной попыткой иронии;) – phant0m

0

делает это в двух контурах один для элементов массива, начиная после того, как в начальной точке, а другой с самого начала является лучший путь; ключ здесь - начать с начала в зависимости от того, где мы остановились в конце массива.

вы можете создать обобщенную функцию как

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); 
1

Вот путь к петле обратно к началу массива, как только он достигает конца:

arr[index%arr.length], если index>arr.length, это будет «loop» до начала.

Этот метод эффективен и работает практически на каждом языке.

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

Результат:

console.log(generateMusic(3)); //["D#", "F", "G", "A", "B", "C#"] 
console.log(generateMusic(5)); //["F", "G", "A", "B", "C#", "D#"]