Найефективніший спосіб створити нульовий масив JavaScript?


602

Який найефективніший спосіб створити масив довільної довжини, заповненої нулем у JavaScript?


7
Деякі фактичні дані щодо цього: jsperf.com/zeroarrayjs
Web_Designer

7
ES6 заповнення дозволяє робити це на самому собі.
Сальвадор Далі

1
arr = новий масив (довжина + 1) .joint (символ) .split ('');
Йордан Стефанеллі

4
ОНОВЛЕННЯ 2016 : Ще один спеціальний орієнтир тут: jsfiddle.net/basickarl/md5z0Lqq
Karl Morrison

1
let i = 0; Array.from(Array(10), ()=>i++);
Барт Хоекстра

Відповіді:


543

ES6 вводить Array.prototype.fill. Його можна використовувати так:

new Array(len).fill(0);

Не впевнений, чи швидко це, але мені це подобається, оскільки він короткий і самоописується.

Це все ще не в IE ( перевірити сумісність ), але є поліфіл .


15
заповнення швидко. new Array(len)болісно повільний. (arr = []).length = len; arr.fill(0);йдеться про найшвидше рішення, яке я бачив десь ... або принаймні зв'язали
Сутенер Тризкіт

7
@PimpTrizkit arr = Array(n)і (arr = []).length = nповодитися однаково відповідно до специфікації. У деяких реалізаціях це може бути швидшим, але я не думаю, що є велика різниця.
Оріол

2
Ну, я почав тестувати його за допомогою багатовимірних масивів, і, здавалося, це значно пришвидшило мої тестові справи. Щойно провів ще кілька тестів на FF41 та Chrome45.0.2454.99 м. Так, я думаю, мені справді було потрібно більше місця, щоб пояснити себе. Більшість моїх тестувань були упередженими. Але, перевірте це. Заздалегідь визначте вар і використовуючи саме цю лінію (arr = []).length = 1000; на arr = new Array(1000);швидкість тестуйте її в Chrome і FF ... newце дуже повільно. Тепер, якщо довжина масиву менша .. скажімо, <50 або там abouts ... тоді 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]

6
Чудова відповідь! Чи можете ви поясніть, будь ласка, трюк Array.apply(null, new Array(5)).map(...)? Тому що просто робити (новий масив (5)). Карта (...) не працюватиме, як розповідає специфікація
Дмитро Пашкевич

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це робить.
zertosh

4
Перший параметр для .applyнасправді є тим, яким ви хочете thisбути. Для цих цілей це thisне має значення - нас дійсно дбає лише про "особливість", що поширює параметр, .apply- це може бути будь-яке значення. Мені це подобається, nullтому що це дешево, ви, мабуть, не хочете користуватися {}або []оскільки ви будете створювати об'єкт без будь-якої причини.
zertosh

2
Також ініціалізація з розміром + призначити набагато швидше, ніж натискання. Дивіться тестовий випадок jsperf.com/zero-fill-2d-array
Колін

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]

У цій посаді багато хитрощів, але, сподіваємось, жоден, який не дійде до виробничого коду :)
Eric Grange

Хоча repeatтрюк напевно не хочеться у виробництві, Array.from()це зовсім чудово :-)
Лусіо Пайва

Насправді, Array.from () тут в основному створює масив, повторюючи його за допомогою map (), викликає функцію на кожному елементі для створення нового масиву, а потім відміняє перший масив ... Для невеликих масивів це може бути нешкідливий, для великих масивів - це така картина, яка призводить до того, що люди називають браузери «
Eric Grange

Люди, які мають великі масиви, безумовно, повинні знати це краще. Для звичайних додатків, однак, створення звичайного розміру допоміжного розміру (до 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 елементами - ви можете виконати тест ТУТ
  • великі масиви - з елементами 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

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


Просто запустив кілька тестів на Chrome 77 і простий цикл із push () вдвічі швидший, ніж fill () ... Цікаво, які тонкі побічні ефекти fill () заважають більш ефективному впровадженню?
Ерік

@EricGrange Я оновлюю відповідь - внизу я поновлюю посилання на benchamrk з вашою пропозицією: випадок P let a=[]; for(i=n;i--;) a.push(0);- але це в 4 рази повільніше, fill(0)- тому я навіть не буду оновлювати картину відьом у цьому випадку.
Kamil Kiełczewski

2
Приємні вимірювання. Аналіз: G повільний через зміну масиву при кожній ітерації, а зміна розміру означає новий розподіл пам'яті. A, B, M швидко, тому що розмір виконується лише один раз. +1
Роланд

Це одна з небагатьох відповідей, яка насправді намагається відповісти на поставлене питання. Невдало це не вдається, дотримуючись аргументовано невідповіді та пропускаючи найшвидший реальний відповідь, принаймні у червні 2020 року у всіх браузерах, що є a = new Array(n); for (let i = 0; i < n; ++i) a[i] = 0;. Тест
gman

@gman насправді ти маєш рацію - я не розумію, як я міг це пропустити - я виконую нові тести та оновлюю відповідь більш детальною інформацією - дякую :)
оновлюю Kamil Kiełczewski

63

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

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

З огляду на поточну підтримку веб-переглядача, ви повинні з обережністю використовувати це, якщо ви не впевнені, що ваша аудиторія використовує сучасні настільні браузери.


4
Якщо ви заповнили тип посилання, він буде однаковим для всіх. новий масив (10) .fill (null) .map (() => []) був би стислим способом обійти це (спалив мене спочатку ха-ха)
John Culviner

4
ОНОВЛЕННЯ 2016 : Цей метод вимиває все інше з води, натисніть тут для орієнтирів: jsfiddle.net/basickarl/md5z0Lqq
Karl Morrison

це буде працювати для масивів. a = Array(10).fill(null).map(() => { return []; });
Енді

2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Phrogz

50

Примітка додано в серпні 2013 року, оновлено лютий 2015 року: Відповідь нижче від 2009 року стосується загального Arrayтипу JavaScript . Це не стосується новіших набраних масивів, визначених у ES2015 [та доступних зараз у багатьох браузерах], таких як тощо Int32Array. Також зауважте, що ES2015 додає fillметод як для масивів, так і для набраних масивів , що, ймовірно, буде найефективнішим способом їх заповнення ...

Крім того, це може мати велике значення для деяких реалізацій того, як ви створюєте масив. Зокрема, двигун V8 Chrome намагається використовувати високоефективний масив пам’яті безперервної пам’яті, якщо він вважає, що може, переходячи на об’єктний масив лише за необхідності.


Для більшості мов було б попередньо виділити, а потім заповнити нуль, як це:

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, швидша, ніж прямий init на FF, приблизно десь між 1000 та 2000 масивами елементів. На двигуні V8 Chrome, однак, прямо-таки 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>

Не впевнені, що заповнення назад має значення тут, якщо ви отримуєте доступ лише до елементів (не видаляючи їх) і ви вже попередньо виділили їх. Я помиляюся?
Триптих

точка зворотного заповнення не має особливого стосунку до масиву, це стосується умови втечі на деякий час - falsey 0 завершує цикл дуже ефективно
annakata

(хоча я щойно помітив, що цей код насправді не використовує цього)
annakata

@annakata, ви не можете використовувати це тут, оскільки 0 - це дійсний індекс.
Триптих

@triptych: неправда, все потрібне - це правильний порядок - дивіться мій пост
annakata

34

За замовчуванням Uint8Array, Uint16Arrayі Uint32Arrayкласи тримати нулі її цінності, так що вам не потрібні ніякі складні методи наповнення, просто зробити:

var ary = new Uint8Array(10);

всі елементи масиву aryза замовчуванням будуть нулями.


5
Це добре, але зауважте, що це не може трактуватися так само, як звичайний масив, наприклад, Array.isArray(ary)є false. Довжина також доступна лише для читання, тому ви не можете натискати на неї нові елементи, як це ary.push
стосується

Fwiw всі набрані масиви зберігають 0як їх значення за замовчуванням.
jfunk

2
@MusikAnimal, Array.from(new Uint8Array(10))забезпечить нормальний масив.
Томаш Лангкаас

@TomasLangkaas: Так, але інша відповідь показує, що це приблизно в 5 разів повільніше, ніж Array(n).fill(0)у Chrome, якщо те, що вам потрібно, - це масив JS. Якщо ви можете використовувати TypedArray, це набагато швидше, ніж .fill(0), хоча, особливо якщо ви можете використовувати значення ініціалізатора за замовчуванням 0. Здається, не існує конструктора, який приймає значення заповнення та довжину, як це std::vectorмає C ++ . Здається, для будь-якого ненульового значення потрібно побудувати нульовий 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]

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і т.д.


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

noj позначення з нулями

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
Насправді масив нічим не заповнюється новим масивом (n), навіть не визначеним, він просто встановлює значення довжини масивів у n. Ви можете перевірити це, зателефонувавши (новий масив (1)). ForEach (...). forEach ніколи не виконує, на відміну від того, якщо ви називаєте його [[undefined].
JussiR

4
new Array(7)це НЕ створює масив «наповнений невизначеним». Він створює порожній масив довжиною 7.
RobG

1
Ви можете переглянути деталі своєї відповіді, оскільки те, що говорить @RobG, є критичним (якби те, що ви говорили, було правдою, картографування було б набагато простіше)
Абдо

1
Ці дні ви могли зробити (new Array(10)).fill(0).
Хав'єр де ла Роза

18

Я перевірив усі комбінації попереднього розподілу / не попереднього розподілу, підрахунку вгору / вниз, та циклів / для циклів в 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 for, розділеної лише комою.
damianb

Мені подобається ця пропозиція від damianb, але не забудьте поставити завдання та кому перед збільшенням! `для (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 рази набагато більше 10 разів швидше ... Я не дуже впевнений, чому ... (різні розміри масивів тестовані між хромом та FF)
сутенер Тризкіт

13

Якщо вам потрібно створити безліч заповнених масивів різної довжини під час виконання вашого коду, я знайшов найшвидший спосіб досягти цього - створити нульовий масив один раз , використовуючи один із методів, зазначених у цій темі, довжиною який ви знаєте, ніколи не буде перевищено, а потім наріжте цей масив за необхідності.

Наприклад (використовуючи функцію з обраної відповіді вище для ініціалізації масиву), створіть заповнений нулем масив довжиною maxLength як змінну, видиму коду, який потребує нульових масивів:

var zero = newFilledArray(maxLength, 0);

Тепер відріжте цей масив щоразу, коли вам потрібен нульовий масив довжини, необхідноїLength < maxLength :

zero.slice(0, requiredLength);

Я створював масиви, заповнені нулями тисячі разів під час виконання мого коду, це надзвичайно прискорило процес.


13
function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}

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') (наприклад, об'єктами та іншими масивами).

Крім того, ви можете змінювати попередні масиви із заповненням:

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. Наприклад, створення нульового заповненого вектора елементів 1М:

  var zeroFilled = [].slice.apply(new Uint8Array(1000000))

Я користувач Linux і завжди працював для мене, але колись у друга, який використовував Mac, було кілька ненульових елементів. Я думав, що його машина несправна, але все-таки ось найбезпечніший спосіб, як ми її виправили:

  var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000)) 

Відредаговано

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. zertosh - ПОМИЛКА

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. zertosh - ПОМИЛКА

Не вистачає найважливішого тесту (принаймні для мене): Node.js. Я підозрюю, що це близько до еталону Chrome.


Це найефективніший спосіб як для моїх пальців, так і для моїх очей. Але це дуже повільно для Chrome (згідно з цим jsperf. На 99% повільніше).
Орвелофіл

1
Цікаво , якщо проблема на Mac вашого друга було пов'язано з: stackoverflow.com/questions/39129200 / ... або , може бути , Array.slice не обробки UInt8Array і витік неініцалізірованние пам'яті? (питання безпеки!).
robocat

@robocat Хороший улов! Якщо я добре пам'ятаю, ми використовували Node.js 0.6 або 0.8. Ми думали про якийсь витік, але не змогли відтворити це з виробничим стеком, тому просто вирішили проігнорувати це.
дурум

10

Використання лодашу або підкреслення

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

Або якщо у вас є масив, який існує, і ви хочете масив однакової довжини

array.map(_.constant(0));

Тож радий, що ви додали цю відповідь, оскільки я використовую підкреслення, і я знав, що для цього є щось ... але ще не зміг його знайти. Я просто хочу, щоб я міг створювати масиви об’єктів за допомогою цього
PandaWood

@PandaWood _.range (0, довжина -1, 0) .мапа (Object.new), я думаю.
djechlin

Має бути _.range(0, length, 0), я вважаю. Лодаш є ексклюзивним для кінцевого значення
user4815162342


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


4

Ця concatверсія набагато швидша в моїх тестах на Chrome (2013-03-21). Близько 200 мс для 10 000 000 елементів проти 675 для прямого init.

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

Бонус: якщо ви хочете заповнити свій масив Strings, це стислий спосіб зробити це (не так швидко, як 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, ШЛИШЕ 18.056: Завершення завершення
Брукс

4

Я випробовував чудову відповідь TJ Crowder і придумав рекурсивне злиття на основі лаконічного рішення, яке перевершує будь-які його тести в 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

Можливо, варто зазначити, що 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;

2
Зважаючи на те, що довжина є визначеною змінною, nце буде коротше:for(var a=[];n--;a[n]=0);
Tomas Langkaas


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

Використання нативного натискання та зсуву для додавання елементів до масиву набагато швидше (приблизно в 10 разів), ніж оголошення області масиву та посилання кожного елемента для встановлення його значення.

fyi: я постійно отримую швидші часи з першим циклом, який відлічується під час запуску цього в 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 мс до приблизно 54 мс
nickf

Відповідь Метью Ромблі все ще насправді перемагає це (30 мс)!
nickf

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

Так що, за моїм рахунком, поштовх дійсніше повільніше, але працює краще з довшими масивами у FF, але гірше в IE, який просто смокче загалом (quel сюрприз).


Я щойно це перевірив: другий метод ( b = []...) на 10-15% швидший, ніж перший, але в 10 разів повільніше, ніж відповідь Джошуа.
nickf

Я знаю, що це давній пост . Але, можливо, це все ж цікавить інших (як я). Тому я хотів би запропонувати доповнити функцію прототипу: включити else {this.length=n;}після this.length-вивірку. Це дозволить скоротити вже наявний масив, якщо необхідно, повторний initдіапазон його на іншу довжину n.
автомобілі10м

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.