Как создать массив, содержащий 1 ... N - PullRequest
849 голосов
/ 19 сентября 2010

Я ищу любые альтернативы ниже для создания массива JavaScript, содержащего от 1 до N, где N известно только во время выполнения.

var foo = [];

for (var i = 1; i <= N; i++) {
   foo.push(i);
}

Мне кажется, что должен быть способ сделать это без цикла.

Ответы [ 56 ]

816 голосов
/ 19 ноября 2013

Вы можете сделать это:

var N = 10; 
Array.apply(null, {length: N}).map(Number.call, Number)

результат: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

или со случайными значениями:

Array.apply(null, {length: N}).map(Function.call, Math.random)

результат: [0,7082694901619107, 0,9572225909214467, 0,8586748542729765, 0,8653848143294454, 0,008339877473190427, 0,9911756622605026, 0,8133423360995948, 0,8377588465809822, 0,5577575915958732, 0,16363654541783035]

Объяснение

Во-первых, обратите внимание, что Number.call(undefined, N) эквивалентно Number(N), который просто возвращает N. Мы будем использовать этот факт позже.

Array.apply(null, [undefined, undefined, undefined]) эквивалентно Array(undefined, undefined, undefined), который создает массив из трех элементов и присваивает undefined каждому элементу.

Как можно обобщить это до N элементов? Рассмотрим, как работает Array(), что выглядит примерно так:

function Array() {
    if ( arguments.length == 1 &&
         'number' === typeof arguments[0] &&
         arguments[0] >= 0 && arguments &&
         arguments[0] < 1 << 32 ) {
        return [ … ];  // array of length arguments[0], generated by native code
    }
    var a = [];
    for (var i = 0; i < arguments.length; i++) {
        a.push(arguments[i]);
    }
    return a;
}

Поскольку ECMAScript 5 , Function.prototype.apply(thisArg, argsArray) также принимает массивоподобный объект типа утки в качестве второго параметра. Если мы вызовем Array.apply(null, { length: N }), то он выполнит

function Array() {
    var a = [];
    for (var i = 0; i < /* arguments.length = */ N; i++) {
        a.push(/* arguments[i] = */ undefined);
    }
    return a;
}

Теперь у нас есть массив N -элементов, каждый элемент которого установлен на undefined. Когда мы вызываем .map(callback, thisArg), каждый элемент будет установлен в результат callback.call(thisArg, element, index, array). Следовательно, [undefined, undefined, …, undefined].map(Number.call, Number) будет отображать каждый элемент в (Number.call).call(Number, undefined, index, array), что совпадает с Number.call(undefined, index, array), который, как мы наблюдали ранее, оценивается в index. Это завершает массив, элементы которого совпадают с их индексом.

Зачем переживать проблему Array.apply(null, {length: N}) вместо просто Array(N)? В конце концов, оба выражения приведут к массиву неопределенных элементов N . Разница в том, что в первом выражении каждый элемент явно устанавливает в неопределенное значение, тогда как в последнем каждый элемент никогда не устанавливался. Согласно документации .map():

callback вызывается только для индексов массива, которым присвоены значения; он не вызывается для индексов, которые были удалены или которым никогда не назначались значения.

Следовательно, Array(N) недостаточно; Array(N).map(Number.call, Number) приведет к неинициализированному массиву длиной N .

Совместимость

Поскольку этот метод основан на поведении Function.prototype.apply(), указанном в ECMAScript 5, он будет не работать в браузерах до ECMAScript 5, таких как Chrome 14 и Internet Explorer 9.

810 голосов
/ 26 октября 2015

В ES6 с использованием Array из () и keys () методы.

Array.from(Array(10).keys())
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Укороченная версия с использованием оператора распространения .

[...Array(10).keys()]
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
322 голосов
/ 06 июля 2016

Простой и краткий путь в ES6 :

Array.from({length: 5}, (v, k) => k+1); 
// [1,2,3,4,5]

Таким образом:

    Array.from({length: N}, (v, k) => k+1);  
   // [1,2,3,...,N]

Поскольку массив инициализируется с undefined в каждой позиции, значение v будет undefined

const range = (N) => Array.from({length: N}, (v, k) => k+1) ;

console.log(
  range(5)
)
277 голосов
/ 19 сентября 2010

Если я получу то, что вам нужно, вам понадобится массив чисел 1..n, который вы сможете позже просмотреть.

Если это все, что вам нужно, вы можете сделать это вместо этого?

var foo = new Array(45); // create an empty array with length 45

тогда, когда вы захотите его использовать ... (без оптимизации, например)

for(var i = 0; i < foo.length; i++){
  document.write('Item: ' + (i + 1) + ' of ' + foo.length + '<br/>'); 
}

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

Смотрите его в действии здесь: http://jsfiddle.net/3kcvm/

171 голосов
/ 21 декабря 2015

В ES6 вы можете сделать:

Array(N).fill().map((e,i)=>i+1);

http://jsbin.com/molabiluwa/edit?js,console

Редактировать: Изменено Array(45) на Array(N), так как вы обновили вопрос.

console.log(
  Array(45).fill(0).map((e,i)=>i+1)
);
145 голосов
/ 19 сентября 2010

Я знаю, что ваш вопрос требует численного заполнения массива, но я не уверен, почему вы хотите это сделать.

Массивы управляют своей длиной.По мере их прохождения их индексы могут храниться в памяти и ссылаться в этой точке.Если необходимо знать случайный индекс, можно использовать метод indexOf.


При этом для ваших нужд вы можете просто объявить массив определенного размера:

var foo = new Array(N);   // where N is a positive integer

/* this will create an array of size, N, primarily for memory allocation, 
   but does not create any defined values

   foo.length                                // size of Array
   foo[ Math.floor(foo.length/2) ] = 'value' // places value in the middle of the array
*/

ES6

Распространение

Использование операторов распространения (...) и keys позволяет создать временный массив размера N для полученияиндексы, а затем новый массив, который может быть назначен вашей переменной:

var foo = [ ...Array(N).keys() ];

Fill / Map

Вы можете сначала создать нужный размер массива, заполнить его неопределенным изатем создайте новый массив, используя map, который устанавливает каждый элемент в индекс.

var foo = Array(N).fill().map((v,i)=>i);

Array.from

Это должно быть инициализировано до длины размера N и заполнить массив водин проход.

Array.from({ length: N }, (v, i) => i)
102 голосов
/ 27 июня 2014

Используйте очень популярный метод подчеркивания _.range

// _.range([start], stop, [step])

_.range(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11); // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5); // => [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1); //  => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0); // => []
63 голосов
/ 19 сентября 2010
function range(start, end) {
    var foo = [];
    for (var i = start; i <= end; i++) {
        foo.push(i);
    }
    return foo;
}

Затем вызывается

var foo = range(1, 5);

Нет встроенного способа сделать это в Javascript, но это совершенно допустимая служебная функция, которую нужно создать, если вам нужно сделать это более одного раза.

Редактировать: По моему мнению, следующая функция является лучшей функцией диапазона. Может быть, только потому, что я предвзято относится к LINQ, но я думаю, что это более полезно в большинстве случаев. Ваш пробег может отличаться.

function range(start, count) {
    if(arguments.length == 1) {
        count = start;
        start = 0;
    }

    var foo = [];
    for (var i = 0; i < count; i++) {
        foo.push(start + i);
    }
    return foo;
}
52 голосов
/ 28 августа 2018

самый быстрый способ заполнить Array в v8:

[...Array(5)].map((_,i) => i);

результат будет: [0, 1, 2, 3, 4]

43 голосов
/ 19 марта 2014

Вы можете использовать это:

new Array(/*any number which you want*/)
    .join().split(',')
    .map(function(item, index){ return ++index;})

например

new Array(10)
    .join().split(',')
    .map(function(item, index){ return ++index;})

создаст следующий массив:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...