Каков наиболее эффективный способ создания массива произвольной длины с нулевым заполнением в JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Каков наиболее эффективный способ создания массива произвольной длины с нулевым заполнением в JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Ответы:
ES6 вводит Array.prototype.fill
. Это можно использовать так:
new Array(len).fill(0);
Не уверен, что это быстро, но мне это нравится, потому что это коротко и самоописательно.
Это все еще не в IE ( проверьте совместимость ), но есть доступный polyfill .
new Array(len)
мучительно медленно (arr = []).length = len; arr.fill(0);
о самом быстром решении, которое я когда-либо видел ... или, по крайней мере, связано
arr = Array(n)
и (arr = []).length = n
вести себя одинаково в соответствии со спецификацией. В некоторых реализациях это может быть быстрее, но я не думаю, что есть большая разница.
(arr = []).length = 1000;
против arr = new Array(1000);
теста скорости его в обоих Chrome и FF ... new
ужасно медленно. Теперь, если длина массива меньше ... скажем, <50 или около того ... тогда new Array()
, кажется, работает лучше. Но ..
arr.fill(0)
... все вроде как меняется. Теперь использование new Array()
быстрее в большинстве случаев, за исключением случаев, когда вы достигаете размеров массива> 100000 ... Затем вы снова можете увидеть увеличение скорости. Но если вам на самом деле не нужно предварительно заполнять его нулями и вы можете использовать стандартную группу пустых массивов. Тогда (arr = []).length = x
в моих тестовых случаях большую часть времени я схожу с ума.
new Array(5).forEach(val => console.log('hi'));
против new Array(5).fill(undefined).forEach(val => console.log('hi'));
.
Хотя это старая ветка, я хотел добавить к ней свои 2 цента. Не уверен, насколько это медленно / быстро, но это быстрый лайнер. Вот что я делаю:
Если я хочу предварительно заполнить номер:
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]
Если я хочу предварительно заполнить строкой:
Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi")
// ["hi", "hi", "hi"]
Другие ответы предложили:
new Array(5+1).join('0').split('')
// ["0", "0", "0", "0", "0"]
но если вы хотите 0 (число), а не «0» (ноль внутри строки), вы можете сделать:
new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]
Array.apply(null, new Array(5)).map(...)
? Потому что просто делать (новый Array (5)). Map (...) не будет работать, как говорит спецификация
new
) Когда Array(5)
вы создаете объект, который выглядит примерно так: { length: 5, __proto__: Array.prototype }
- попробуйте console.dir( Array(5) )
. Обратите внимание , что он не имеет каких - либо свойств 0
, 1
, 2
и т.д. Но когда вам , apply
что пред Array
конструктору, это , как говорят Array(undefined, undefined, undefined, undefined, undefined)
. И вы получаете объект, который вроде как выглядит { length: 5, 0: undefined, 1: undefined...}
. map
работ по свойствам 0
, 1
и т.д., поэтому ваш пример не работает, но когда вы используете apply
это делает.
.apply
- это то, что вы хотите this
. Для этих целей это this
не имеет значения - нас действительно волнует только «особенность» распространения параметров, .apply
поэтому она может иметь любое значение. Мне нравится, null
потому что это дешево, вы, вероятно, не хотите использовать, {}
или, []
поскольку вы будете создавать экземпляр объекта без причины.
Вот еще один способ сделать это с помощью ES6, о котором никто еще не упоминал:
> Array.from(Array(3), () => 0)
< [0, 0, 0]
Он работает, передавая функцию карты в качестве второго параметра Array.from
.
В приведенном выше примере первый параметр выделяет массив из 3 позиций, заполненных значением, undefined
а затем лямбда-функция отображает каждую из них на значение 0
.
Хотя Array(len).fill(0)
он короче, он не работает, если вам нужно сначала заполнить массив, выполнив какое-то вычисление (я знаю, что вопрос не задавался, но многие в конечном итоге ищут это) .
Например, если вам нужен массив с 10 случайными числами:
> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]
Это более кратко (и элегантно), чем эквивалент:
const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
numbers[i] = Math.round(10 * Math.random());
}
Этот метод также можно использовать для генерации последовательностей чисел, используя параметр индекса, предоставленный в обратном вызове:
> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
repeat()
Поскольку этот ответ привлекает большое внимание, я также хотел показать этот крутой трюк. Хотя и не такой полезный, как мой основной ответ, я представлю еще не очень известный, но очень полезный repeat()
метод String . Вот хитрость:
> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
Круто, да? repeat()
это очень полезный метод для создания строки, которая является повторением исходной строки определенное количество раз. После этого создаем split()
для нас массив, который затем map()
привязывается к нужным значениям. Разбивая его по шагам:
> "?".repeat(10)
< "??????????"
> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]
> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
repeat
трюк определенно не нужен в производстве, Array.from()
он отлично подходит :-)
Самое быстрое решение
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
Кратчайшее (удобное) решение (в 3 раза медленнее для маленьких массивов, немного медленнее для больших (медленнее в Firefox))
Array(n).fill(0)
Сегодня 2020.06.09 я выполняю тесты на macOS High Sierra 10.13.6 в браузерах Chrome 83.0, Firefox 77.0 и Safari 13.1. Я тестирую выбранные решения для двух тестовых случаев
new Array(n)+for
(N), является самым быстрым решением для небольших и больших массивов (кроме Chrome, но там все еще очень быстро) и рекомендуется в качестве быстрого кросс-браузерного решения.new Float32Array(n)
(I) возвращает нестандартный массив (например, вы не можете вызвать push(..)
его), поэтому я не сравниваю его результаты с другими решениями - однако это решение примерно в 10-20 раз быстрее, чем другие решения для больших массивов во всех браузерахfor
(L, M, N, O) являются быстрыми для небольших массивовfill
(B, C), бывают быстрыми в Chrome и Safari, но удивительно медленнее в Firefox для больших массивов. Они средние быстрые для небольших массивовArray.apply
(P) выбрасывает ошибку для больших массивов
Ниже код представляет решения, используемые в измерениях
Пример результатов для Chrome
let a=[]; for(i=n;i--;) a.push(0);
- но это в 4 раза медленнее, чем fill(0)
- поэтому я даже не буду обновлять картинку в этом случае.
Уже упомянутый метод заполнения ES 6 прекрасно справляется с этой задачей. Большинство современных настольных браузеров уже поддерживают требуемые методы-прототипы Array (Chromium, FF, Edge и Safari) [ 1 ]. Вы можете посмотреть детали на MDN . Простой пример использования
a = new Array(10).fill(0);
Учитывая текущую поддержку браузера, вы должны быть осторожны, если не уверены, что ваша аудитория использует современные настольные браузеры.
a = Array(10).fill(null).map(() => { return []; });
a = Array(10).fill(0).map( _ => [] );
Примечание добавлено в августе 2013 года, обновлено в феврале 2015 года. Нижеприведенный ответ от 2009 года относится к универсальному Array
типу JavaScript . Это не относится к новым типизированным массивам, определенным в ES2015 [и доступным сейчас во многих браузерах], Int32Array
и тому подобное. Также обратите внимание, что ES2015 добавляет fill
метод для массивов и типизированных массивов , который, вероятно, будет наиболее эффективным способом их заполнения ...
Кроме того, для некоторых реализаций это может иметь большое значение для создания массива. В частности, движок Chrome V8 пытается использовать высокоэффективный массив непрерывной памяти, если сочтет это возможным, переходя на массив на основе объектов только при необходимости.
В большинстве языков это будет предварительно выделено, а затем заполнено нулями, например так:
function newFilledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = val;
}
return rv;
}
Но JavaScript-массивы на самом деле не являются массивами , они представляют собой сопоставления ключ / значение, как и все другие объекты JavaScript, так что нет никакого «предварительного выделения» (установка длины не выделяет столько слотов для заполнения), ни есть ли основания полагать, что преимущество обратного отсчета (а именно быстрого выполнения сравнения в цикле) не перевешивается добавлением ключей в обратном порядке, когда реализация, возможно, хорошо оптимизировала их обработку ключей связанные с массивами по теории, вы обычно делаете их по порядку.
Фактически, Мэтью Крамли отметил, что обратный отсчет в Firefox заметно медленнее, чем подсчет, и я могу подтвердить этот результат - это часть массива (цикл до нуля все же быстрее, чем цикл до предела в переменной). Очевидно, что добавление элементов в массив в обратном порядке является медленной операцией в Firefox. На самом деле, результаты могут сильно отличаться от реализации JavaScript (что не так уж удивительно). Вот быстрая и грязная тестовая страница (ниже) для реализации браузера (очень грязная, не дает результатов во время тестов, поэтому обеспечивает минимальную обратную связь и будет работать вне временных рамок скрипта). Я рекомендую освежиться между тестами; FF (по крайней мере) замедляет повторные тесты, если вы этого не сделаете.
Довольно сложная версия, в которой используется Array # concat, работает быстрее, чем прямая инициализация в FF, где-то между 1000 и 2000 массивами элементов. На двигателе Chrome V8, однако, прямой init побеждает каждый раз ...
Вот тестовая страница ( живая копия ):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Zero Init Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
#log p {
margin: 0;
padding: 0;
}
.error {
color: red;
}
.winner {
color: green;
font-weight: bold;
}
</style>
<script type='text/javascript' src='prototype-1.6.0.3.js'></script>
<script type='text/javascript'>
var testdefs = {
'downpre': {
total: 0,
desc: "Count down, pre-decrement",
func: makeWithCountDownPre
},
'downpost': {
total: 0,
desc: "Count down, post-decrement",
func: makeWithCountDownPost
},
'up': {
total: 0,
desc: "Count up (normal)",
func: makeWithCountUp
},
'downandup': {
total: 0,
desc: "Count down (for loop) and up (for filling)",
func: makeWithCountDownArrayUp
},
'concat': {
total: 0,
desc: "Concat",
func: makeWithConcat
}
};
document.observe('dom:loaded', function() {
var markup, defname;
markup = "";
for (defname in testdefs) {
markup +=
"<div><input type='checkbox' id='chk_" + defname + "' checked>" +
"<label for='chk_" + defname + "'>" + testdefs[defname].desc + "</label></div>";
}
$('checkboxes').update(markup);
$('btnTest').observe('click', btnTestClick);
});
function epoch() {
return (new Date()).getTime();
}
function btnTestClick() {
// Clear log
$('log').update('Testing...');
// Show running
$('btnTest').disabled = true;
// Run after a pause while the browser updates display
btnTestClickPart2.defer();
}
function btnTestClickPart2() {
try {
runTests();
}
catch (e) {
log("Exception: " + e);
}
// Re-enable the button; we don't yheidl
$('btnTest').disabled = false;
}
function runTests() {
var start, time, counter, length, defname, def, results, a, invalid, lowest, s;
// Get loops and length
s = $F('txtLoops');
runcount = parseInt(s);
if (isNaN(runcount) || runcount <= 0) {
log("Invalid loops value '" + s + "'");
return;
}
s = $F('txtLength');
length = parseInt(s);
if (isNaN(length) || length <= 0) {
log("Invalid length value '" + s + "'");
return;
}
// Clear log
$('log').update('');
// Do it
for (counter = 0; counter <= runcount; ++counter) {
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
start = epoch();
a = def.func(length);
time = epoch() - start;
if (counter == 0) {
// Don't count (warm up), but do check the algorithm works
invalid = validateResult(a, length);
if (invalid) {
log("<span class='error'>FAILURE</span> with def " + defname + ": " + invalid);
return;
}
}
else {
// Count this one
log("#" + counter + ": " + def.desc + ": " + time + "ms");
def.total += time;
}
}
}
}
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
def.avg = def.total / runcount;
if (typeof lowest != 'number' || lowest > def.avg) {
lowest = def.avg;
}
}
}
results =
"<p>Results:" +
"<br>Length: " + length +
"<br>Loops: " + runcount +
"</p>";
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
results += "<p" + (lowest == def.avg ? " class='winner'" : "") + ">" + def.desc + ", average time: " + def.avg + "ms</p>";
}
}
results += "<hr>";
$('log').insert({top: results});
}
function validateResult(a, length) {
var n;
if (a.length != length) {
return "Length is wrong";
}
for (n = length - 1; n >= 0; --n) {
if (a[n] != 0) {
return "Index " + n + " is not zero";
}
}
return undefined;
}
function makeWithCountDownPre(len) {
var a;
a = new Array(len);
while (--len >= 0) {
a[len] = 0;
}
return a;
}
function makeWithCountDownPost(len) {
var a;
a = new Array(len);
while (len-- > 0) {
a[len] = 0;
}
return a;
}
function makeWithCountUp(len) {
var a, i;
a = new Array(len);
for (i = 0; i < len; ++i) {
a[i] = 0;
}
return a;
}
function makeWithCountDownArrayUp(len) {
var a, i;
a = new Array(len);
i = 0;
while (--len >= 0) {
a[i++] = 0;
}
return a;
}
function makeWithConcat(len) {
var a, rem, currlen;
if (len == 0) {
return [];
}
a = [0];
currlen = 1;
while (currlen < len) {
rem = len - currlen;
if (rem < currlen) {
a = a.concat(a.slice(0, rem));
}
else {
a = a.concat(a);
}
currlen = a.length;
}
return a;
}
function log(msg) {
$('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<label for='txtLength'>Length:</label><input type='text' id='txtLength' value='10000'>
<br><label for='txtLoops'>Loops:</label><input type='text' id='txtLoops' value='10'>
<div id='checkboxes'></div>
<br><input type='button' id='btnTest' value='Test'>
<hr>
<div id='log'></div>
</div></body>
</html>
По умолчанию Uint8Array
, Uint16Array
и Uint32Array
классы держать нули ее ценности, так что вам не нужны никакие сложные методы наполнения, просто сделать:
var ary = new Uint8Array(10);
все элементы массива ary
будут нулями по умолчанию.
Array.isArray(ary)
is false
. Длина также только для чтения , так что вы не можете нажать новые детали к нему , как сary.push
0
значение по умолчанию.
Array.from(new Uint8Array(10))
предоставит обычный массив.
Array(n).fill(0)
в Chrome, если вам действительно нужен массив JS. Если вы можете использовать TypedArray, это намного быстрее, чем .fill(0)
, тем не менее, особенно если вы можете использовать значение инициализатора по умолчанию 0
. Кажется, не существует конструктора, который принимает значение заполнения и длину, как в C ++ std::vector
. Кажется, что для любого ненулевого значения вы должны создать обнуленный TypedArray и затем заполнить его. : /
Если вы используете ES6, вы можете использовать Array.from () следующим образом:
Array.from({ length: 3 }, () => 0);
//[0, 0, 0]
Имеет тот же результат, что и
Array.from({ length: 3 }).map(() => 0)
//[0, 0, 0]
Потому что
Array.from({ length: 3 })
//[undefined, undefined, undefined]
function makeArrayOf(value, length) {
var arr = [], i = length;
while (i--) {
arr[i] = value;
}
return arr;
}
makeArrayOf(0, 5); // [0, 0, 0, 0, 0]
makeArrayOf('x', 3); // ['x', 'x', 'x']
Обратите внимание, что while
обычно более эффективно, чем for-in
, forEach
и т. Д.
i
локальная переменная не является посторонней? length
передается по значению, так что вы должны иметь возможность уменьшить его напрямую.
arr[i] = value
). Это намного быстрее, чтобы пройтись от начала до конца и использовать arr.push(value)
. Это раздражает, потому что я предпочитаю твой метод.
используя обозначение объекта
var x = [];
ноль заполнен? подобно...
var x = [0,0,0,0,0,0];
заполнено "неопределенным" ...
var x = new Array(7);
объективная запись с нулями
var x = [];
for (var i = 0; i < 10; i++) x[i] = 0;
В качестве примечания, если вы измените прототип Array, оба
var x = new Array();
а также
var y = [];
будет иметь эти модификации прототипа
В любом случае, я не был бы чрезмерно обеспокоен эффективностью или скоростью этой операции, есть много других вещей, которые вы, вероятно, будете делать, которые гораздо более расточительны и дороги, чем создание массива произвольной длины, содержащей нули.
null
в этом массиве нет s -var x = new Array(7);
new Array(7)
это не создает массив «наполненный неопределенным». Он создает пустой массив длиной 7.
(new Array(10)).fill(0)
.
Я протестировал все комбинации предварительного выделения / отсутствия предварительного выделения, счета вверх / вниз и циклов for / while в IE 6/7/8, Firefox 3.5, Chrome и Opera.
Приведенные ниже функции всегда были самыми быстрыми или чрезвычайно близкими в Firefox, Chrome и IE8, и не намного медленнее, чем самые быстрые в Opera и IE 6. Это также самое простое и понятное, на мой взгляд. Я нашел несколько браузеров, в которых версия цикла while немного быстрее, поэтому я включил ее и для справки.
function newFilledArray(length, val) {
var array = [];
for (var i = 0; i < length; i++) {
array[i] = val;
}
return array;
}
или
function newFilledArray(length, val) {
var array = [];
var i = 0;
while (i < length) {
array[i++] = val;
}
return array;
}
var array = []
объявление в первую часть цикла for, разделенного только запятой.
length
уже заданное значение, чтобы оно не менялось постоянно. На моей машине я получил массив нулей длиной в 1 миллион от 40 мс до 8.
for (i = 0, array = []; i < length; ++i) array[i] = val;
.. Меньше блоков? ... во всяком случае, также ... если я установлю array.length
длину нового массива в длину .. я, похоже, получу еще 10% -15% увеличение скорости FF ... в Chrome, кажется, удваивает скорость -> var i, array = []; array.length = length; while(i < length) array[i++] = val;
(было еще быстрее, если бы я оставил его как for
цикл ... но инициализация больше не нужна, так что while
, похоже, в этой версии он работает быстрее)
Если вам нужно создать много заполненных нулями массивов разной длины во время выполнения вашего кода, самый быстрый способ, который я нашел для этого, - это создать нулевой массив один раз , используя один из методов, упомянутых в этом разделе, длины который вы знаете, никогда не будет превышен, а затем нарезать этот массив по мере необходимости.
Например (используя функцию из выбранного выше ответа для инициализации массива), создайте заполненный нулями массив длины maxLength как переменную, видимую для кода, который требует нулевых массивов:
var zero = newFilledArray(maxLength, 0);
Теперь нарезайте этот массив каждый раз, когда вам нужен заполненный нулями массив длины requiredLength < maxLength :
zero.slice(0, requiredLength);
Во время выполнения моего кода я создавал массивы, заполненные нулями, тысячи раз, это значительно ускорило процесс.
Я не имею ничего против:
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
new Array(5+1).join('0').split('').map(parseFloat);
предложенный Zertosh, но в новых расширениях массива ES6 вы можете сделать это изначально с помощью fill
метода. Теперь IE edge, Chrome и FF его поддерживают, но проверьте таблицу совместимости
new Array(3).fill(0)
даст тебе [0, 0, 0]
. Вы можете заполнить массив любым значением, таким как new Array(5).fill('abc')
(даже объекты и другие массивы).
Кроме того, вы можете изменить предыдущие массивы с помощью fill:
arr = [1, 2, 3, 4, 5, 6]
arr.fill(9, 3, 5) # what to fill, start, end
что дает вам: [1, 2, 3, 9, 9, 6]
То, как я обычно это делаю (и удивительно быстро), использует Uint8Array
. Например, создание заполненного нулями вектора из 1M элементов:
var zeroFilled = [].slice.apply(new Uint8Array(1000000))
Я пользователь Linux и всегда работал на меня, но однажды у друга, использующего Mac, было несколько ненулевых элементов. Я думал, что его машина работает со сбоями, но все же вот самый безопасный способ, который мы нашли, это исправить:
var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000))
Edited
Chrome 25.0.1364.160
Firefox 20.0
Отсутствует самый важный тест (по крайней мере, для меня): Node.js. Я подозреваю, что это близко к тесту Chrome.
Используя lodash или подчеркивание
_.range(0, length - 1, 0);
Или, если у вас есть массив, и вы хотите массив такой же длины
array.map(_.constant(0));
_.range(0, length, 0)
, я считаю. Lodash не имеет конечного значения
Начиная с ECMAScript2016 , существует один четкий выбор для больших массивов.
Поскольку этот ответ по-прежнему отображается в верхней части результатов поиска Google, вот ответ на 2017 год.
Вот текущий jsbench с несколькими десятками популярных методов, включая многие, предложенные до сих пор по этому вопросу. Если вы найдете лучший способ, пожалуйста, добавьте, раскошелиться и поделиться.
Я хочу отметить, что не существует истинного наиболее эффективного способа создания массива произвольной длины с нулевым заполнением. Вы можете оптимизировать скорость, ясность и ремонтопригодность - либо можно считать более эффективным выбором в зависимости от потребностей проекта.
При оптимизации по скорости вы хотите: создать массив с использованием буквального синтаксиса; установите длину, инициализируйте переменную итерации и выполните итерацию по массиву, используя цикл while. Вот пример.
const arr = [];
arr.length = 120000;
let i = 0;
while (i < 120000) {
arr[i] = 0;
i++;
}
Другая возможная реализация будет:
(arr = []).length = n;
let i = 0;
while (i < n) {
arr[i] = 0;
i++;
}
Но я настоятельно не рекомендую использовать эту вторую имплантацию на практике, поскольку она менее понятна и не позволяет вам поддерживать область видимости для переменной массива.
Они значительно быстрее, чем заполнение цикла for, и примерно на 90% быстрее, чем стандартный метод
const arr = Array(n).fill(0);
Но этот метод заполнения по-прежнему является наиболее эффективным выбором для небольших массивов из-за его ясности, краткости и удобства обслуживания. Разница в производительности, скорее всего, не убьет вас, если вы не создадите много массивов с длинами порядка тысяч и более.
Несколько других важных замечаний. Большинство руководств по стилю рекомендуют вам больше не использовать var
без особой причины при использовании ES6 или новее. Используйте const
для переменных, которые не будут переопределены, и let
для переменных, которые будут. MDN и руководство Стиля AirBnB в больших местах , чтобы пойти для получения более подробной информации о передовой практике. Вопросы не касались синтаксиса, но важно, чтобы люди, плохо знакомые с JS, знали об этих новых стандартах при поиске среди множества старых и новых ответов.
Создать новый массив
new Array(arrayLength).fill(0);
Чтобы добавить некоторые значения в конце существующего массива
[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]
//**To create an all new Array**
console.log(new Array(5).fill(0));
//**To add some values at the end of an existing Array**
let existingArray = [1,2,3]
console.log([...existingArray, ...new Array(5).fill(0)]);
Не видел этот метод в ответах, поэтому вот он:
"0".repeat( 200 ).split("").map( parseFloat )
В результате вы получите нулевой массив длиной 200:
[ 0, 0, 0, 0, ... 0 ]
Я не уверен в производительности этого кода, но это не должно быть проблемой, если вы используете его для относительно небольших массивов.
const arr = Array.from({ length: 10 }).fill(0)
Эта concat
версия намного быстрее в моих тестах на Chrome (2013-03-21). Около 200 мс для 10 000 000 элементов против 675 для прямой инициализации.
function filledArray(len, value) {
if (len <= 0) return [];
var result = [value];
while (result.length < len/2) {
result = result.concat(result);
}
return result.concat(result.slice(0, len-result.length));
}
Бонус: если вы хотите заполнить ваш массив строками, это краткий способ сделать это (не так быстро, как concat
если бы):
function filledArrayString(len, value) {
return new Array(len+1).join(value).split('');
}
Я тестировал отличный ответ TJ Crowder и придумал рекурсивное слияние на основе решения concat, которое превосходит любое в его тестах в Chrome (я не тестировал другие браузеры).
function makeRec(len, acc) {
if (acc == null) acc = [];
if (len <= 1) return acc;
var b = makeRec(len >> 1, [0]);
b = b.concat(b);
if (len & 1) b = b.concat([0]);
return b;
},
вызовите метод с makeRec(29)
.
Возможно, стоит отметить, что Array.prototype.fill
это было добавлено как часть предложения ECMAScript 6 (Гармония) . Я предпочел бы использовать полифилл, написанный ниже, прежде чем рассматривать другие варианты, упомянутые в теме.
if (!Array.prototype.fill) {
Array.prototype.fill = function(value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;
// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
};
}
Самый короткий для кода цикла
a=i=[];for(;i<100;)a[i++]=0;
edit:
for(a=i=[];i<100;)a[i++]=0;
or
for(a=[],i=100;i--;)a[i]=0;
Безопасная версия вар
var a=[],i=0;for(;i<100;)a[i++]=0;
edit:
for(var i=100,a=[];i--;)a[i]=0;
n
это будет короче:for(var a=[];n--;a[n]=0);
let filled = [];
filled.length = 10;
filled.fill(0);
console.log(filled);
Моя самая быстрая функция была бы:
function newFilledArray(len, val) {
var a = [];
while(len--){
a.push(val);
}
return a;
}
var st = (new Date()).getTime();
newFilledArray(1000000, 0)
console.log((new Date()).getTime() - st); // returned 63, 65, 62 milliseconds
Использование встроенных функций push и shift для добавления элементов в массив выполняется намного быстрее (примерно в 10 раз), чем объявление области действия массива и обращение к каждому элементу для установки его значения.
К вашему сведению: я последовательно получаю более быстрые времена с первым циклом, который ведет обратный отсчет, при запуске этого в firebug (расширение firefox).
var a = [];
var len = 1000000;
var st = (new Date()).getTime();
while(len){
a.push(0);
len -= 1;
}
console.log((new Date()).getTime() - st); // returned 863, 894, 875 milliseconds
st = (new Date()).getTime();
len = 1000000;
a = [];
for(var i = 0; i < len; i++){
a.push(0);
}
console.log((new Date()).getTime() - st); // returned 1155, 1179, 1163 milliseconds
Мне интересно знать, что делает из этого TJ Crowder? :-)
while (len--)
... приняв мое время обработки примерно с 60
Я знал, что у меня где-то был этот прото :)
Array.prototype.init = function(x,n)
{
if(typeof(n)=='undefined') { n = this.length; }
while (n--) { this[n] = x; }
return this;
}
var a = (new Array(5)).init(0);
var b = [].init(0,4);
Редактировать: тесты
В ответ на Джошуа и другие методы я провел свой собственный сравнительный анализ и вижу результаты, совершенно отличные от тех, о которых сообщалось.
Вот что я проверял:
//my original method
Array.prototype.init = function(x,n)
{
if(typeof(n)=='undefined') { n = this.length; }
while (n--) { this[n] = x; }
return this;
}
//now using push which I had previously thought to be slower than direct assignment
Array.prototype.init2 = function(x,n)
{
if(typeof(n)=='undefined') { n = this.length; }
while (n--) { this.push(x); }
return this;
}
//joshua's method
function newFilledArray(len, val) {
var a = [];
while(len--){
a.push(val);
}
return a;
}
//test m1 and m2 with short arrays many times 10K * 10
var a = new Date();
for(var i=0; i<10000; i++)
{
var t1 = [].init(0,10);
}
var A = new Date();
var b = new Date();
for(var i=0; i<10000; i++)
{
var t2 = [].init2(0,10);
}
var B = new Date();
//test m1 and m2 with long array created once 100K
var c = new Date();
var t3 = [].init(0,100000);
var C = new Date();
var d = new Date();
var t4 = [].init2(0,100000);
var D = new Date();
//test m3 with short array many times 10K * 10
var e = new Date();
for(var i=0; i<10000; i++)
{
var t5 = newFilledArray(10,0);
}
var E = new Date();
//test m3 with long array created once 100K
var f = new Date();
var t6 = newFilledArray(100000, 0)
var F = new Date();
Результаты:
IE7 deltas:
dA=156
dB=359
dC=125
dD=375
dE=468
dF=412
FF3.5 deltas:
dA=6
dB=13
dC=63
dD=8
dE=12
dF=8
Так что, по моим расчетам, push действительно медленнее, но лучше работает с более длинными массивами в FF, но хуже в IE, который вообще отстой (в целом удивление).
b = []...
) на 10-15% быстрее первого, но он более чем в 10 раз медленнее, чем ответ Джошуа.
else {this.length=n;}
после this.length
-check. Это сократит уже существующий массив, если это необходимо, при повторной init
его настройке на другую длину n
.
Анонимная функция:
(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Немного короче с for-loop:
(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Работает с любым Object
, просто поменяй что внутри this.push()
.
Вы даже можете сохранить функцию:
function fill(size, content) {
for(;size--;this.push(content));
return this;
}
Позвоните, используя:
var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']
Добавление элементов в уже существующий массив:
var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']
Производительность: http://jsperf.com/zero-filled-array-creation/25