Создайте массив JavaScript, содержащий 1...N

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

var foo = [];

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

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

30 ответов


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

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

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

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

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

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

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


можно сделать так:

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) также принимает массив типа duck объект как его второй параметр. Если мы призовем 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)? В конце концов, оба выражения приведут к an N-массив элементов неопределенных элементов. Разница в том, что в первом выражении каждый элемент явно set в undefined, тогда как в последнем каждый элемент никогда не был установлен. Согласно документации .map():

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

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

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

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


в ES6 с использованием Array from () и ключи() методы.

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]

простой и краткий путь в ЕС6:

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]

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

console.log(
  range(5)
)

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

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

http://jsbin.com/molabiluwa/edit?js, консоль

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

console.log(
  Array(45).fill(0).map((e,i)=>i+1)
);

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

// _.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); // => []

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

массивы врожденно управлять их длины. По мере их прохождения их индексы могут храниться в памяти и ссылаться на эту точку. Если случайный индекс должен быть известен,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
*/


ЕС6

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

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

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

Заполнить/Карте

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

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

массив.от

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

Array.from({ length: N }, (v, i) => i)

function range(start, end) {
    var foo = [];
    for (var i = start; i <= end; i++) {
        foo.push(i);
    }
    return foo;
}

затем позвонил

var foo = range(1, 5);

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

Edit: на мой взгляд, следующее является лучшей функцией диапазона. Может быть, просто потому, что я предвзято отношусь к 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;
}

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

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]

Если вы используете d3.js в вашем приложении, как я, D3 предоставляет вспомогательную функцию, которая делает это для вас.

так, чтобы получить массив от 0 до 4, это так же просто, как:

d3.range(5)
[0, 1, 2, 3, 4]

и получить массив от 1 до 5, Как вы просили:

d3.range(1, 5+1)
[1, 2, 3, 4, 5]

проверить в этом уроке для получения дополнительной информации.


это, вероятно, самый быстрый способ генерировать массив чисел

короткий

var a=[],b=N;while(b--)a[b]=b+1;

Inline

var arr=(function(a,b){while(a--)b[a]=a;return b})(10,[]);
//arr=[0,1,2,3,4,5,6,7,8,9]

если вы хотите начать с 1

var arr=(function(a,b){while(a--)b[a]=a+1;return b})(10,[]);
//arr=[1,2,3,4,5,6,7,8,9,10]

нужна функция?

function range(a,b,c){c=[];while(a--)c[a]=a+b;return c}; //length,start,placeholder
var arr=range(10,5);
//arr=[5,6,7,8,9,10,11,12,13,14]

почему?

  1. while самый быстрый цикл

  2. прямая настройка быстрее, чем push

  3. [] быстрее, чем new Array(10)

  4. она короткая... посмотри первый код. тогда посмотрите на все остальные функции здесь.

если вы хотите не может жить без на

for(var a=[],b=7;b>0;a[--b]=b+1); //a=[1,2,3,4,5,6,7]

или

for(var a=[],b=7;b--;a[b]=b+1); //a=[1,2,3,4,5,6,7]

Примечание: apply, map, join-split .... срсли ??? это чертовски медленно!!!!!!а совместимость???


ES6 это сделает трюк:

[...Array(12).keys()]

проверить результат:

[...Array(12).keys()].map(number => console.log(number))

Если вы используете lodash, вы можете использовать _.диапазон:

_.range([start=0], end, [step=1])

создает массив чисел (положительный и/или отрицательный) прогрессирует от запуска до, но не в том числе, конец. Шаг 1 используется, если задано отрицательное начало без конца и шага. Если end не указан, он настроен на запуск с начнем значение 0.

примеры:

_.range(4);
// ➜ [0, 1, 2, 3]

_.range(-4);
// ➜ [0, -1, -2, -3]

_.range(1, 5);
// ➜ [1, 2, 3, 4]

_.range(0, 20, 5);
// ➜ [0, 5, 10, 15]

_.range(0, -4, -1);
// ➜ [0, -1, -2, -3]

_.range(1, 4, 0);
// ➜ [1, 1, 1]

_.range(0);
// ➜ []

используя ES2015/ЕС6 распространение оператор

[...Array(10)].map((_, i) => ++i)

console.log([...Array(10)].map((_, i) => ++i))

итоговый отчет .. Drrruummm Rolll -

Это короткий код для генерации массива размера N (здесь 10) без использования ES6. Coccoверсия выше близка, но не самая короткая.

(function(n){for(a=[];n--;a[n]=n+1);return a})(10)

но бесспорным победителем этого кода golf (конкуренция для решения конкретной проблемы в наименьшем количестве байтов исходного кода) является Нико Ruotsalainen . Используя конструктору Array ES6 оператор распространения . (Большая часть синтаксиса ES6 является допустимым typeScript, но ниже нет. Так что будьте благоразумны при его использовании)

[...Array(10).keys()]

в ES6 есть другой способ, используя массив.от который принимает 2 аргумента, первый-arrayLike (в этом случае объект с length свойство), а второй - функция отображения (в этом случае мы сопоставляем элемент с его индексом)

Array.from({length:10}, (v,i) => i)

Это короче и может использоваться для других последовательностей, таких как генерация четных чисел

Array.from({length:10}, (v,i) => i*2)

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

// open the dev console to see results

count = 100000

console.time("from object")
for (let i = 0; i<count; i++) {
  range = Array.from({length:10}, (v,i) => i )
}
console.timeEnd("from object")

console.time("from keys")
for (let i =0; i<count; i++) {
  range = Array.from(Array(10).keys())
}
console.timeEnd("from keys")

console.time("apply")
for (let i = 0; i<count; i++) {
  range = Array.apply(null, { length: 10 }).map(function(element, index) { return index; })
}
console.timeEnd("apply")

использование новых методов массива и => синтаксис функции из стандарта ES6 (только Firefox на момент написания).

заполняя отверстия с undefined:

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

Array.from получается "дыр" в undefined так Array.map работает:

Array.from(Array(5)).map((_, i) => i + 1)

for(var i,a=[i=0];i<10;a[i++]=i);

в = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


попробуйте это:

var foo = [1, 2, 3, 4, 5];

Если вы используете CoffeeScript, вы можете создать диапазон, выполнив:

var foo = [1..5]; 

в противном случае, если вы используете vanilla JavaScript, вам придется использовать цикл, если вы хотите инициализировать массив до переменной длины.


следующая функция возвращает массив, заполненный числами:

var createArrayOfNumbers = function (n) {
    return Array.apply(null, new Array(n)).map(function (empty, index) {
        return index;
    });
};

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


немного проще, чем вариант строки:

// create range by N
Array(N).join(0).split(0);

// create a range starting with 0 as the value
Array(7).join(0).split(0).map(Number.call, Number); // [0, 1, 2, 3, 4, 5, 6]

Object.keys(Array.apply(0, Array(3))).map(Number)

возвращает [0, 1, 2]. Очень похоже на отличный ответ Игоря Шубина, но с немного меньшим обманом (и одним символом дольше).

объяснение:

  • Array(3) // [undefined × 3] создать массив длины n=3. К сожалению, этот массив почти бесполезен для нас, поэтому мы должны...
  • Array.apply(0,Array(3)) // [undefined, undefined, undefined] создать массив типа Iterable. Примечание: null более распространен как первый arg apply, но 0 укорачиваться.
  • Object.keys(Array.apply(0,Array(3))) // ['0', '1', '2'] затем получите ключи массива (работает, потому что массивы являются массивом typeof-это объект с индексами для ключей.
  • Object.keys(Array.apply(0,Array(3))).map(Number) // [0, 1, 2] и сопоставьте клавиши, Преобразуя строки в числа.

просто еще один ЕС6 версия.

при использовании Array.from второй необязательный аргумент:

массив.от (arrayLike [, mapFn [, thisArg]])

мы можем построить нумерованный массив из пустого Array(10) позиции:

Array.from(Array(10), (_, i) => i)

var arr = Array.from(Array(10), (_, i) => i);
document.write(arr);

кажется, что единственный аромат, который в настоящее время не находится в этом довольно полном списке ответов, - это генератор; так что исправить это:

const gen = N => [...(function*(){let i=0;while(i<N)yield i++})()]

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

gen(4) // [0,1,2,3]

хорошая вещь об этом-вам не просто нужно увеличивать... Чтобы вдохновиться ответом @igor-shubin, вы можете легко создать массив randoms:

const gen = N => [...(function*(){let i=0;
  while(i++<N) yield Math.random()
})()]

а то долгих оперативно дорого например:

const slow = N => new Array(N).join().split(',').map((e,i)=>i*5)
// [0,5,10,15,...]

вы могли бы вместо этого сделать:

const fast = N => [...(function*(){let i=0;while(i++<N)yield i*5})()]

самый быстрый способ наполнения Array в V8-это:

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

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


Я искал функциональное решение и я закончил с:

function numbers(min, max) {
  return Array(max-min+2).join().split(',').map(function(e, i) { return min+i; });
}

console.log(numbers(1, 9));

Примечание: join().split(',') преобразует разреженный массив в непрерывный.


Импровизация по вышеуказанному:

var range = function (n) {
  return Array(n).join().split(',').map(function(e, i) { return i; });
}  

можно получить следующие варианты:

1) массив.init для значения v

var arrayInitTo = function (n,v) {
  return Array(n).join().split(',').map(function() { return v; });
}; 

2) получить обратный диапазон:

var rangeRev = function (n) {
  return Array(n).join().split(',').map(function() { return n--; });
};

Я не видел никакого решения, основанного на рекурсивных функциях (и никогда не писал рекурсивные функции сам), поэтому вот моя попытка.

обратите внимание на этот массив.push (something) возвращает новую длину массива:

(a=[]).push(a.push(a.push(0))) //  a = [0, 1, 2]

и с рекурсивной функцией:

var a = (function f(s,e,a,n){return ((n?n:n=s)>e)?a:f(s,e,a?a:a=[],a.push(n)+s)})(start,end) // e.g., start = 1, end = 5

EDIT: два других решения

var a = Object.keys(new Int8Array(6)).map(Number).slice(1)

и

var a = []
var i=setInterval(function(){a.length===5?clearInterval(i):a.push(a.length+1)}) 

Array(8).fill(0).map(Number.call, Number)

Воровать Игоры Number.call трюк, но с использованием fill() немного укоротить. Работает только с ES6 и выше.


типа Iterable версия с помощью генератор функция, которая не изменяет Number.prototype.

function sequence(max, step = 1) {
  return {
    [Symbol.iterator]: function* () {
      for (let i = 1; i <= max; i += step) yield i
    }
  }
}

console.log([...sequence(10)])