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

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

var foo = [];

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

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

80 ответов

Решение

Если я получу то, что вы после, вы хотите массив чисел 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/

В ES6 используются методы Array from () и 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]

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

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,5577575915958745, 0,55775815308745, 0,17517830358732, 0,17217830308732, 0,11717830308732, 0,11717830308745

объяснение

Во-первых, обратите внимание, что 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.

Простой и краткий путь в 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]

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

console.log(
  range(5)
)

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

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

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

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

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

Array.from

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

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

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

Используйте очень популярный метод Underscore _.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); // => []
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;
}

Спектакль

Сегодня 2020.12.11 я провожу тесты на MacOs HighSierra 10.13.6 на Chrome v87, Safari v13.1.2 и Firefox v83 для выбранных решений.

Полученные результаты

Для всех браузеров

  • решение O (на основе while) самый быстрый (кроме Firefox для большого N - но там он быстрый)
  • решение T является самым быстрым в Firefox для большого N
  • решения M,P быстрые при малых N
  • решение V (lodash) быстро для больших N
  • решения W,X медленные при малых N
  • решение F медленное

Подробности

Я выполняю 2 тестовых случая:

  • для малого N = 10 - запустить его можно ЗДЕСЬ
  • для большого N = 1000000 - запустить его можно ЗДЕСЬ

Во фрагменте ниже представлены все протестированные решения A B C D E F G H I J K L M N O P Q R S T U V W X

А вот примеры результатов для хрома

✅ Просто это сработало для меня:

      [...Array(5)].map(...)

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

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

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

На этот вопрос много сложных ответов, но простая однострочная:

[...Array(255).keys()].map(x => x + 1)

Кроме того, хотя вышеприведенный текст является коротким (и аккуратным) для написания, я думаю, что следующий текст немного быстрее (для максимальной длины:

127, Int8,

255, Uint8,

32,767, Int16,

65 535, Uint16,

2,147,483,647, Int32,

4,294,967,295, Uint32.

(основано на максимальных целочисленных значениях), также здесь больше о Typed Arrays):

(new Uint8Array(255)).map(($,i) => i + 1);

Хотя это решение также не является идеальным, поскольку оно создает два массива и использует дополнительное объявление переменной "$" (не уверен, что это можно обойти, используя этот метод). Я думаю, что следующее решение - абсолютно быстрый способ сделать это:

for(var i = 0, arr = new Uint8Array(255); i < arr.length; i++) arr[i] = i + 1;

В любое время после выполнения этого оператора вы можете просто использовать переменную "arr" в текущей области видимости;

Если вы хотите сделать из него простую функцию (с некоторой базовой проверкой):

function range(min, max) {
    min = min && min.constructor == Number ? min : 0;
    !(max && max.constructor == Number && max > min) && // boolean statements can also be used with void return types, like a one-line if statement.
        ((max = min) & (min = 0));  //if there is a "max" argument specified, then first check if its a number and if its graeter than min: if so, stay the same; if not, then consider it as if there is no "max" in the first place, and "max" becomes "min" (and min becomes 0 by default)

    for(var i = 0, arr = new (
        max < 128 ? Int8Array : 
        max < 256 ? Uint8Array :
        max < 32768 ? Int16Array : 
        max < 65536 ? Uint16Array :
        max < 2147483648 ? Int32Array :
        max < 4294967296 ? Uint32Array : 
        Array
    )(max - min); i < arr.length; i++) arr[i] = i + min;
    return arr;
}



//and you can loop through it easily using array methods if you want
range(1,11).forEach(x => console.log(x));

//or if you're used to pythons `for...in` you can do a similar thing with `for...of` if you want the individual values:
for(i of range(2020,2025)) console.log(i);

//or if you really want to use `for..in`, you can, but then you will only be accessing the keys:

for(k in range(25,30)) console.log(k);

console.log(
    range(1,128).constructor.name,
    range(200).constructor.name,
    range(400,900).constructor.name,
    range(33333).constructor.name,
    range(823, 100000).constructor.name,
    range(10,4) // when the "min" argument is greater than the "max", then it just considers it as if there is no "max", and the new max becomes "min", and "min" becomes 0, as if "max" was never even written
);

Использование оператора спреда ES2015/ES6

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

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

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

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]

Это, вероятно, самый быстрый способ создания массива чисел

самый короткий

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

В соответствии

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]

Если вы используете 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]

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

Решение для пустого массива и только числа в массиве

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

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

Создает массив чисел (положительных и / или отрицательных), начиная от начала до, но не включая конец. Шаг -1 используется, если указано отрицательное начало без конца или шага. Если end не указан, он запускается с start, затем устанавливается на 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);
// ➜ []

Новый способ наполнения Array является:

const array = [...Array(5).keys()]
console.log(array)

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

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

// `n` is the size you want to initialize your array
// `null` is what the array will be filled with (can be any other value)
Array(n).fill(null)

ES6 это сделает свое дело:

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

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

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

Очень просто и легко сгенерировать ровно 1 - N

/questions/12527766/kak-generirovat-diapazon-chisel-ot-0-do-n-tolko-v-es2015/12527783#12527783

С Дельтой

Для javascript

самый маленький и однострочный
[...Array(N)].map((v, i) => from + i * step);

Примеры и другие альтернативы

Array.from(Array(10).keys()).map(i => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

[...Array(10).keys()].map(i => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]

Array(10).fill(0).map((v, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]

Array(10).fill().map((v, i) => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]

[...Array(10)].map((v, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
Диапазон Функция
const range = (from, to, step) =>
  [...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);

range(0, 9, 2);
//=> [0, 2, 4, 6, 8]

// can also assign range function as static method in Array class (but not recommended )
Array.range = (from, to, step) =>
  [...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);

Array.range(2, 10, 2);
//=> [2, 4, 6, 8, 10]

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

Array.range(2, 10, -1);
//=> []

Array.range(3, 0, -1);
//=> [3, 2, 1, 0]
Как итераторы
class Range {
  constructor(total = 0, step = 1, from = 0) {
    this[Symbol.iterator] = function* () {
      for (let i = 0; i < total; yield from + i++ * step) {}
    };
  }
}

[...new Range(5)]; // Five Elements
//=> [0, 1, 2, 3, 4]
[...new Range(5, 2)]; // Five Elements With Step 2
//=> [0, 2, 4, 6, 8]
[...new Range(5, -2, 10)]; // Five Elements With Step -2 From 10
//=>[10, 8, 6, 4, 2]
[...new Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]

// Also works with for..of loop
for (i of new Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2
Только как генераторы
const Range = function* (total = 0, step = 1, from = 0) {
  for (let i = 0; i < total; yield from + i++ * step) {}
};

Array.from(Range(5, -2, -10));
//=> [-10, -12, -14, -16, -18]

[...Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]

// Also works with for..of loop
for (i of Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2

// Lazy loaded way
const number0toInf = Range(Infinity);
number0toInf.next().value;
//=> 0
number0toInf.next().value;
//=> 1
// ...

От-К с шагом / дельтой

с использованием итераторов
class Range2 {
  constructor(to = 0, step = 1, from = 0) {
    this[Symbol.iterator] = function* () {
      let i = 0,
        length = Math.floor((to - from) / step) + 1;
      while (i < length) yield from + i++ * step;
    };
  }
}
[...new Range2(5)]; // First 5 Whole Numbers
//=> [0, 1, 2, 3, 4, 5]

[...new Range2(5, 2)]; // From 0 to 5 with step 2
//=> [0, 2, 4]

[...new Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]
с помощью генераторов
const Range2 = function* (to = 0, step = 1, from = 0) {
  let i = 0,
    length = Math.floor((to - from) / step) + 1;
  while (i < length) yield from + i++ * step;
};

[...Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]

let even4to10 = Range2(10, 2, 4);
even4to10.next().value;
//=> 4
even4to10.next().value;
//=> 6
even4to10.next().value;
//=> 8
even4to10.next().value;
//=> 10
even4to10.next().value;
//=> undefined

Для машинописного текста

class _Array<T> extends Array<T> {
  static range(from: number, to: number, step: number): number[] {
    return Array.from(Array(Math.floor((to - from) / step) + 1)).map(
      (v, k) => from + k * step
    );
  }
}
_Array.range(0, 9, 1);

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

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

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

Но бесспорным победителем этого Кодекса гольфа (соревнования по решению конкретной проблемы в наименьшем количестве байтов исходного кода) является Niko Ruotsalainen. Использование Array Constructor и оператора распространения ES6. (Большая часть синтаксиса ES6 является допустимым typeScript, но следующее - нет. Поэтому будьте осторожны при его использовании)

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

Быстрый

Это решение, вероятно, самое быстрое, оно вдохновлено функцией lodash _.range (но мое проще и быстрее)

Преимущества в производительности по сравнению с текущими (2020.12.11) существующими ответами на основе while/for

  • память выделяется один раз в начале a=Array(N)
  • увеличивающийся индекс i++ используется - что выглядит примерно на 30% быстрее, чем убывающий индекс i-- (возможно, потому что кеш-память процессора быстрее в прямом направлении)

В этом ответе были проведены тесты скорости с более чем 20 другими решениями.

В ES6 есть еще один способ, использующий Array.from, который принимает 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")

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

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

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

Array.from превращает "дыры" в undefined так Array.map работает как положено:

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

В ES6:

Array.from({length: 1000}, (_, i) => i).slice(1);

или еще лучше (без дополнительной переменной _ и без дополнительных slice вызов):

Array.from({length:1000}, Number.call, i => i + 1)

Или для немного более быстрых результатов вы можете использовать Uint8Array, если ваш список короче 256 результатов (или вы можете использовать другие списки Uint в зависимости от того, насколько короткий список, например Uint16 для максимального числа 65535 или Uint32 для максимального из 4294967295 и т. д. Официально эти типизированные массивы были добавлены только в ES6). Например:

Uint8Array.from({length:10}, Number.call, i => i + 1)

ES5:

Array.apply(0, {length: 1000}).map(function(){return arguments[1]+1});

В качестве альтернативы в ES5 для функции карты (например, второй параметр для Array.from функция в ES6 выше), вы можете использовать Number.call

Array.apply(0,{length:1000}).map(Number.call,Number).slice(1)

Или, если вы против .slice здесь также вы можете сделать ES5-эквивалент вышеупомянутого (из ES6), например:

Array.apply(0,{length:1000}).map(Number.call, Function("i","return i+1"))

Array(...Array(9)).map((_, i) => i);

console.log(Array(...Array(9)).map((_, i) => i))

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

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

Другие вопросы по тегам