Самый эффективный способ создать массив JavaScript с нулевым заполнением?

602

Каков наиболее эффективный способ создания массива произвольной длины с нулевым заполнением в JavaScript?

разб
источник
7
Некоторые фактические данные об этом: jsperf.com/zeroarrayjs
Web_Designer
7
Заливка ES6 позволяет сделать это изначально.
Сальвадор Дали
1
arr = new Array (длина + 1) .joint (символ) .split ('');
Джордан Стефанелли
4
ОБНОВЛЕНИЕ 2016 : Еще один пользовательский тест здесь: jsfiddle.net/basickarl/md5z0Lqq
K - Токсичность в SO растет.
1
let i = 0; Array.from(Array(10), ()=>i++);
Барт Хукстра

Ответы:

543

ES6 вводит Array.prototype.fill. Это можно использовать так:

new Array(len).fill(0);

Не уверен, что это быстро, но мне это нравится, потому что это коротко и самоописательно.

Это все еще не в IE ( проверьте совместимость ), но есть доступный polyfill .

Ориоль
источник
15
заполнить быстро. new Array(len)мучительно медленно (arr = []).length = len; arr.fill(0);о самом быстром решении, которое я когда-либо видел ... или, по крайней мере, связано
Pimp Trizkit
7
@PimpTrizkit arr = Array(n)и (arr = []).length = nвести себя одинаково в соответствии со спецификацией. В некоторых реализациях это может быть быстрее, но я не думаю, что есть большая разница.
Oriol
2
Ну, я начал тестировать его с многомерными массивами, и это, казалось, значительно ускорило мои тестовые случаи. Только что провел еще несколько тестов на FF41 и Chrome45.0.2454.99 m. Да, я думаю, мне действительно нужно больше места, чтобы объяснить себя. Большая часть моих испытаний была предвзятостью. Но проверьте это. Создайте с собой вар и используя только эту линию (arr = []).length = 1000; против arr = new Array(1000);теста скорости его в обоих Chrome и FF ... newужасно медленно. Теперь, если длина массива меньше ... скажем, <50 или около того ... тогда new Array(), кажется, работает лучше. Но ..
Сутенер Тризкит
4
... Я признаю, что пропустил эту часть ... когда я добавляю вторую строку в тест ... arr.fill(0) ... все вроде как меняется. Теперь использование new Array()быстрее в большинстве случаев, за исключением случаев, когда вы достигаете размеров массива> 100000 ... Затем вы снова можете увидеть увеличение скорости. Но если вам на самом деле не нужно предварительно заполнять его нулями и вы можете использовать стандартную группу пустых массивов. Тогда (arr = []).length = xв моих тестовых случаях большую часть времени я схожу с ума.
Сутенер Тризкит
4
Обратите внимание, что для итерации по массиву (например, map или forEach) должны быть установлены значения , в противном случае эти индексы будут пропущены. Значения, которые вы устанавливаете, могут быть любыми, даже неопределенными. Пример: попробуй new Array(5).forEach(val => console.log('hi'));против new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo
387

Хотя это старая ветка, я хотел добавить к ней свои 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]
zertosh
источник
6
Отличный ответ! Можете ли вы объяснить трюк с Array.apply(null, new Array(5)).map(...)? Потому что просто делать (новый Array (5)). Map (...) не будет работать, как говорит спецификация
Дмитрий Пашкевич
36
(кстати, нам на самом деле не нужно 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это делает.
Зертош
4
Первый параметр для .apply- это то, что вы хотите this. Для этих целей это thisне имеет значения - нас действительно волнует только «особенность» распространения параметров, .applyпоэтому она может иметь любое значение. Мне нравится, nullпотому что это дешево, вы, вероятно, не хотите использовать, {}или, []поскольку вы будете создавать экземпляр объекта без причины.
Зертош
2
Также инициализация с помощью size + assign намного быстрее, чем push. См. Контрольный
Колин
2
как насчет Array.apply (null, Array (5)). map (x => 0)? Это немного короче!
Arch Linux Tux
97

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

Вот еще один способ сделать это с помощью 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]

Бонусный ответ: заполнить массив с помощью String 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()он отлично подходит :-)
Lucio Paiva
Не совсем, Array.from () здесь в основном создает массив, перебирает его с помощью map (), вызывает функцию для каждого элемента для создания нового массива, затем отбрасывает первый массив ... Для небольших массивов это может быть безобидный, для больших массивов, это тот тип паттерна, который приводит к тому, что люди называют браузеры «
Eric Grange
Люди, работающие с большими массивами, должны знать лучше, чем это, безусловно. Однако для обычных приложений создание массива aux обычного размера (до 10 тыс. Элементов), который будет сразу же размещен, вполне подходит (занимает столько же времени, сколько если бы вы избегали создания дополнительного массива - протестировано в последней версии Chrome). В таких случаях удобочитаемость становится более важной, чем незначительная оптимизация производительности. Что касается времени O (n), это необходимо, если вам нужно вычислить что-то свое для каждого элемента (основная тема моего ответа). Это обсуждение очень интересно, рад, что вы подняли его!
Лусио Пайва
88

Короче говоря

Самое быстрое решение

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. Я тестирую выбранные решения для двух тестовых случаев

  • маленький массив - с 10 элементами - тест можно выполнить ЗДЕСЬ
  • большие массивы - с элементами 1M - тест можно выполнить ЗДЕСЬ

Выводы

  • Решение, основанное на 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

введите описание изображения здесь

Камил Келчевски
источник
Просто запустил несколько тестов на Chrome 77 и простой цикл с push () в два раза быстрее, чем fill () ... Интересно, какие тонкие побочные эффекты fill () мешают более эффективной реализации?
Эрик Грандж
@EricGrange Я обновляю ответ - внизу я обновляю ссылку на benchamrk с вашим предложением: случай P let a=[]; for(i=n;i--;) a.push(0);- но это в 4 раза медленнее, чем fill(0)- поэтому я даже не буду обновлять картинку в этом случае.
Камиль Келчевски
2
Хорошие измерения. Анализ: G медленный из-за изменения размера массива на каждой итерации, а изменение размера означает выполнение нового выделения памяти. A, B, M быстро, потому что калибровка производится только один раз. +1
Роланд
63

Уже упомянутый метод заполнения ES 6 прекрасно справляется с этой задачей. Большинство современных настольных браузеров уже поддерживают требуемые методы-прототипы Array (Chromium, FF, Edge и Safari) [ 1 ]. Вы можете посмотреть детали на MDN . Простой пример использования

a = new Array(10).fill(0);

Учитывая текущую поддержку браузера, вы должны быть осторожны, если не уверены, что ваша аудитория использует современные настольные браузеры.

Джеральд Сенаркленс де Гранси
источник
4
Если вы заполните ссылочный тип, он будет одинаковым для всех. new Array (10) .fill (null) .map (() => []) был бы кратким способом обойти это (сожгли меня изначально, ха-ха)
Джон Калвинер
4
ОБНОВЛЕНИЕ 2016 : Этот метод выбрасывает все остальное из воды, нажмите здесь для ознакомления
K - Токсичность в SO растет.
это будет работать для массивов. a = Array(10).fill(null).map(() => { return []; });
Энди
2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Фрогз
50

Примечание добавлено в августе 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>
TJ Crowder
источник
Не уверен, что заполнение в обратном направлении будет иметь здесь значение, поскольку вы только получаете доступ к элементам (не удаляя их) и уже распределились заранее. Я ошибаюсь?
Триптих
точка обратной заливки не имеет ничего общего с массивом, это связано с условием escape на некоторое время - фальси 0 очень эффективно завершает цикл
annakata
(хотя я только что заметил, что этот код фактически не использует это)
annakata
@annakata, вы не можете использовать это здесь, потому что 0 является допустимым индексом.
Триптих
@triptych: не правда, все, что нужно, это правильный порядок - см. мой пост
annakata
34

По умолчанию Uint8Array, Uint16Arrayи Uint32Arrayклассы держать нули ее ценности, так что вам не нужны никакие сложные методы наполнения, просто сделать:

var ary = new Uint8Array(10);

все элементы массива aryбудут нулями по умолчанию.

deadrunk
источник
5
Это хорошо, но помните, что это нельзя рассматривать так же, как обычный массив, например, Array.isArray(ary)is false. Длина также только для чтения , так что вы не можете нажать новые детали к нему , как сary.push
MusikAnimal
Все типизированные массивы сохраняют 0значение по умолчанию.
jfunk,
2
@MusikAnimal, Array.from(new Uint8Array(10))предоставит обычный массив.
Томас Лангкаас
@TomasLangkaas: Да, но другой ответ показывает, что это примерно в 5 раз медленнее, чем Array(n).fill(0)в Chrome, если вам действительно нужен массив JS. Если вы можете использовать TypedArray, это намного быстрее, чем .fill(0), тем не менее, особенно если вы можете использовать значение инициализатора по умолчанию 0. Кажется, не существует конструктора, который принимает значение заполнения и длину, как в C ++ std::vector. Кажется, что для любого ненулевого значения вы должны создать обнуленный TypedArray и затем заполнить его. : /
Питер Кордес
29

Если вы используете 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]
foxiris
источник
23
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и т. Д.

kangax
источник
3
Разве iлокальная переменная не является посторонней? lengthпередается по значению, так что вы должны иметь возможность уменьшить его напрямую.
Шон Брайт
3
Хотя поначалу это выглядит великолепно, к сожалению, очень медленно присваивать значения в произвольной точке в арке (например arr[i] = value). Это намного быстрее, чтобы пройтись от начала до конца и использовать arr.push(value). Это раздражает, потому что я предпочитаю твой метод.
Ник Брант,
19

используя обозначение объекта

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 = [];

будет иметь эти модификации прототипа

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

Аллен Райс
источник
5
Э-э ... nullв этом массиве нет s -var x = new Array(7);
kangax
5
На самом деле, массив не заполняется чем-либо новым Array (n), даже 'undefined', он просто устанавливает значение длины массива в n. Вы можете проверить это, вызвав (new Array (1)). ForEach (...). forEach никогда не выполняется, в отличие от того, если вы вызываете его на [undefined].
JussiR
4
new Array(7)это не создает массив «наполненный неопределенным». Он создает пустой массив длиной 7.
RobG
1
Возможно, вы захотите пересмотреть части своего ответа, так как то, что говорит @RobG, является критическим (если бы вы сказали, что это правда, картирование было бы намного проще)
Абдо
1
В эти дни вы могли бы сделать (new Array(10)).fill(0).
Хавьер де ла Роса
18

Я протестировал все комбинации предварительного выделения / отсутствия предварительного выделения, счета вверх / вниз и циклов 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;
}
Мэтью Крамли
источник
1
Вы также можете добавить var array = []объявление в первую часть цикла for, разделенного только запятой.
Дамианб
Мне нравится это предложение от damianb, но не забудьте поставить назначение и запятую перед приращением! `for (var i = 0; i <длина; массив [i] = val, i ++);
каштановая
Сделайте то, что всем остальным не хватает со вторым, и установите для длины массива lengthуже заданное значение, чтобы оно не менялось постоянно. На моей машине я получил массив нулей длиной в 1 миллион от 40 мс до 8.
Джонатан Грей,
Кажется, я получаю увеличение скорости на 10-15%, когда я реорганизую это решение в один слой. 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, похоже, в этой версии он работает быстрее)
Pimp Trizkit
Я также отмечу это в моем тестировании. В приличном количестве моих тестовых случаев последняя версия, кажется, работает в 3–5 раз быстрее, чем в 10 раз ... Я не уверен, почему ... (различные размеры массива проверены между chrome и FF)
Pimp Trizkit
13

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

Например (используя функцию из выбранного выше ответа для инициализации массива), создайте заполненный нулями массив длины maxLength как переменную, видимую для кода, который требует нулевых массивов:

var zero = newFilledArray(maxLength, 0);

Теперь нарезайте этот массив каждый раз, когда вам нужен заполненный нулями массив длины requiredLength < maxLength :

zero.slice(0, requiredLength);

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

Ненад Вукичевич
источник
13
function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}
Eli
источник
3
Вы также можете использовать, new Array(size+1).join("x").split("x").map(function() { return 0; })чтобы получить реальные цифры
Юваль
6
@Yuval Или простоnew Array(size+1).join('0').split('').map(Number)
Пол
11

Я не имею ничего против:

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]

Сальвадор Дали
источник
10

То, как я обычно это делаю (и удивительно быстро), использует 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

  1. Фредерик Готлиб - 6,43
  2. Сэм Барнум - 4,83
  3. Эли - 3,68
  4. Иисус Навин 2,91
  5. Мэтью Крамли - 2,67
  6. бдуран - 2,55
  7. Аллен Райс - 2.11
  8. Кангакс - 0,68
  9. Tj. Краудер - 0,67
  10. Зертош - ОШИБКА

Firefox 20.0

  1. Аллен Райс - 1,85
  2. Джошуа - 1,82
  3. Мэтью Крамли - 1,79
  4. бдуран - 1,37
  5. Фредерик Готлиб - 0,67
  6. Сэм Барнум - 0,63
  7. Эли - 0,59
  8. кагакс - 0,13
  9. Tj. Краудер - 0,13
  10. Зертош - ОШИБКА

Отсутствует самый важный тест (по крайней мере, для меня): Node.js. Я подозреваю, что это близко к тесту Chrome.

дурум
источник
Это самый эффективный способ для моих пальцев и для моих глаз. Но это очень и очень медленно для Chrome (согласно этому jsperf. 99% медленнее).
Орвеллофил
1
Интересно, была ли проблема на Mac вашего друга связана со: stackoverflow.com/questions/39129200/… или, возможно, Array.slice не обрабатывал UInt8Array и не просачивался неинициализированной памяти? (проблема безопасности!).
Робокат
@robocat Хороший улов! Если я хорошо помню, мы использовали Node.js 0.6 или 0.8. Мы подумали о какой-то утечке, но мы не смогли воспроизвести ее с производственным стеком, поэтому решили просто ее проигнорировать.
дурум
10

Используя lodash или подчеркивание

_.range(0, length - 1, 0);

Или, если у вас есть массив, и вы хотите массив такой же длины

array.map(_.constant(0));
djechlin
источник
Я так рада, что вы добавили этот ответ, поскольку я использую подчеркивание, и я знала, что что-то есть для этого ... но пока не смогла найти его. Я просто хотел бы создать массивы объектов, используя это
PandaWood
@PandaWood _.range (0, длина -1, 0) .map (Object.new), я думаю.
Джечлин
Должно быть _.range(0, length, 0), я считаю. Lodash не имеет конечного значения
user4815162342
9

Решение ES6:

[...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0]
Vic
источник
8

Начиная с 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, знали об этих новых стандартах при поиске среди множества старых и новых ответов.

Исаак Б
источник
8

Создать новый массив

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

Хуанма Менендес
источник
6

Не видел этот метод в ответах, поэтому вот он:

"0".repeat( 200 ).split("").map( parseFloat )

В результате вы получите нулевой массив длиной 200:

[ 0, 0, 0, 0, ... 0 ]

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

Евгений Тюрин
источник
5
Ни самый быстрый, ни самый короткий, но приятный вклад в разнообразие решений.
7vujy0f0hy
5

const arr = Array.from({ length: 10 }).fill(0)

Алекс Дики
источник
1
Это не заполняет массив нулями. Это заполняет это неопределенным.
JLH
@jlh спасибо исправлено
алекс дыкіі
4

Эта 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('');
}
Сэм Барнум
источник
2
Ок, дикий Это намного быстрее, чем использование нового массива (len). НО! Я вижу в Chrome, что последующее чтение этих данных занимает значительно больше времени. Вот некоторые временные метки, чтобы показать, что я имею в виду: (Использование нового массива (len)) 0.365: Создание массива 4.526: Выполнение свертки 10.75: Завершение свертки (использование concat) 0.339: Создание массива 0.591: Выполнение свертки // OMG, WAY быстрее 18.056: Свертка завершена
Брукс
4

Я тестировал отличный ответ 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).

Фредерик Готтлиб
источник
4

Как насчет new Array(51).join('0').split('')?

Кори Моухортер
источник
1
тогда .map(function(a){return +a})?
lonewarrior556
4

Возможно, стоит отметить, что 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;
  };
}
Иво
источник
4

Самый короткий для кода цикла

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;
nathnolt
источник
2
Учитывая, что длина является определенной переменной, nэто будет короче:for(var a=[];n--;a[n]=0);
Томас Лангкаас
3

Моя самая быстрая функция была бы:

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
мс
Ответ Мэтью Крамбли все еще на самом деле превосходит это (30 мс)!
Ник
3

Я знал, что у меня где-то был этот прото :)

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, который вообще отстой (в целом удивление).

annakata
источник
Я только что проверил это: второй метод ( b = []...) на 10-15% быстрее первого, но он более чем в 10 раз медленнее, чем ответ Джошуа.
Ник
Я знаю, что это древний пост . Но, возможно, это все еще интересует других (таких как я). Поэтому я хотел бы предложить добавить функцию-прототип: включить else {this.length=n;}после this.length-check. Это сократит уже существующий массив, если это необходимо, при повторной initего настройке на другую длину n.
cars10m
2

Анонимная функция:

(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

Матео Джанолио
источник