Najbardziej efektywny sposób na utworzenie tablicy JavaScript wypełnionej zerą?

602

Jaki jest najskuteczniejszy sposób na utworzenie dowolnej liczby pól wypełnionych zerami w JavaScript?

dil
źródło
7
Niektóre aktualne dane na ten temat: jsperf.com/zeroarrayjs
Web_Designer
7
Wypełnienie ES6 pozwala to zrobić natywnie.
Salvador Dali,
1
arr = new Array (length + 1) .joint (character) .split ('');
Jordan Stefanelli
4
AKTUALIZACJA 2016 : Kolejny niestandardowy test tutaj: jsfiddle.net/basickarl/md5z0Lqq
Karl Morrison
1
let i = 0; Array.from(Array(10), ()=>i++);
Bart Hoekstra

Odpowiedzi:

543

ES6 wprowadza Array.prototype.fill. Można go użyć w następujący sposób:

new Array(len).fill(0);

Nie jestem pewien, czy jest szybki, ale podoba mi się, ponieważ jest krótki i samoopisujący się.

Nadal nie ma go w IE ( sprawdź kompatybilność ), ale dostępny jest polypełniacz .

Oriol
źródło
15
wypełnienie jest szybkie. new Array(len)jest boleśnie powolny. (arr = []).length = len; arr.fill(0);dotyczy najszybszego rozwiązania, jakie kiedykolwiek widziałem ... lub przynajmniej wiąże
Pimp Trizkit
7
@PimpTrizkit arr = Array(n)i (arr = []).length = nzachowuj się identycznie zgodnie ze specyfikacją. W niektórych implementacjach może być szybsze, ale nie sądzę, że jest duża różnica.
Oriol
2
Cóż, zacząłem testować z użyciem tablic wielowymiarowych i wydawało się, że znacznie przyspieszyło to moje przypadki testowe. Właśnie teraz wykonałem kilka testów na FF41 i Chrome45.0.2454.99 m. Tak, chyba potrzebowałem więcej miejsca, żeby się wytłumaczyć. Przyznaję, że większość moich testów była stronnicza. Ale sprawdź to. Predefine var i używając tylko tej linii (arr = []).length = 1000; przed arr = new Array(1000);testem prędkości je zarówno w Chrome i FF na ... newjest strasznie powolny. Teraz, dla tablic o mniejszej długości ... powiedzmy <50 lub mniej więcej ... wtedy new Array()wydaje się, że działa lepiej. Ale ..
Pimp Trizkit
4
... Przyznaję, że tęskniłem za tą częścią ... kiedy dodałem drugą linię do testu ... arr.fill(0) ... wszystko się zmienia. Teraz użycie new Array()jest szybsze w większości przypadków, z wyjątkiem sytuacji, gdy dojdziesz do rozmiarów tablic> 100000 ... Wtedy możesz zacząć widzieć, jak ponownie rośnie szybkość. Ale jeśli tak naprawdę nie musisz wypełniać go zerami i możesz użyć standardowej fali pustych tablic. W (arr = []).length = xmoich testowych przypadkach przez większość czasu jest szalony szybko.
Pimp Trizkit,
4
Zauważ, że aby wykonać iterację po tablicy (np. Map lub ForEach), wartości muszą być ustawione , w przeciwnym razie pominie te indeksy. Ustawione wartości mogą być dowolne - nawet niezdefiniowane. Przykład: spróbuj new Array(5).forEach(val => console.log('hi'));kontra new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo
387

Chociaż jest to stary wątek, chciałem dodać do niego moje 2 centy. Nie jestem pewien, jak wolno / szybko to jest, ale jest to szybki jeden linijka. Oto co robię:

Jeśli chcę wstępnie wypełnić liczbą:

Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]

Jeśli chcę wstępnie wypełnić ciąg:

Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi")
// ["hi", "hi", "hi"]

Inne odpowiedzi sugerują:

new Array(5+1).join('0').split('')
// ["0", "0", "0", "0", "0"]

ale jeśli chcesz 0 (liczbę), a nie „0” (zero w ciągu), możesz:

new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]
zertosh
źródło
6
Świetna odpowiedź! Czy możesz wyjaśnić sztuczkę Array.apply(null, new Array(5)).map(...)? Bo po prostu robienie (nowa tablica (5)). Mapa (...) nie będzie działać, jak mówi specyfikacja
Dmitry Pashkevich
36
(przy okazji, tak naprawdę nie potrzebujemy new). Kiedy to robisz Array(5), tworzysz obiekt, który wygląda tak: { length: 5, __proto__: Array.prototype }- spróbuj console.dir( Array(5) ). Zauważ, że nie ma żadnych właściwości 0, 1, 2, itd. Ale kiedy applyto aż do Arraykonstruktora, to jak mówią Array(undefined, undefined, undefined, undefined, undefined). I dostajesz przedmiot, który wygląda trochę { length: 5, 0: undefined, 1: undefined...}. mapDziała na właściwości 0, 1itp, dlatego Twój przykład nie działa, ale podczas korzystania applyrobi.
zertosh
4
Pierwszym parametrem .applyjest właściwie to, czym chcesz thisbyć. Dla tych celów thisnie ma to znaczenia - zależy nam tylko na rozszerzeniu parametru „cechy” .apply- więc może to być dowolna wartość. Podoba mi się, nullponieważ jest tani, prawdopodobnie nie chcesz go używać {}lub []ponieważ tworzysz obiekt bez powodu.
zertosh
2
Również inicjalizacja za pomocą rozmiaru + przypisanie jest znacznie szybsza niż wypychanie. Zobacz przypadek testowy jsperf.com/zero-fill-2d-array
Colin
2
co z Array.apply (null, Array (5)). map (x => 0)? Jest trochę krótszy!
Arch Linux Tux,
97

Elegancki sposób na wypełnienie tablicy wstępnie obliczonymi wartościami

Oto inny sposób, aby to zrobić za pomocą ES6, o którym nikt dotąd nie wspominał:

> Array.from(Array(3), () => 0)
< [0, 0, 0]

Działa, przekazując funkcję mapy jako drugi parametr parametru Array.from.

W powyższym przykładzie pierwszy parametr przydziela tablicę 3 pozycji wypełnionych wartością, undefineda następnie funkcja lambda odwzorowuje każdą z nich na wartość 0.

Chociaż Array(len).fill(0)jest krótszy, nie działa, jeśli musisz najpierw wypełnić tablicę, wykonując obliczenia (wiem, że pytanie nie wymagało tego, ale wiele osób kończy tutaj, szukając tego) .

Na przykład, jeśli potrzebujesz tablicy z 10 liczbami losowymi:

> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]

Jest bardziej zwięzły (i elegancki) niż odpowiednik:

const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
    numbers[i] = Math.round(10 * Math.random());
}

Tej metody można również użyć do wygenerowania sekwencji liczb, korzystając z parametru indeksu podanego w wywołaniu zwrotnym:

> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Dodatkowa odpowiedź: wypełnij tablicę używając String repeat()

Ponieważ ta odpowiedź cieszy się dużym zainteresowaniem, chciałem też pokazać tę fajną sztuczkę. Chociaż nie tak przydatna jak moja główna odpowiedź, wprowadzę wciąż mało znaną, ale bardzo przydatną repeat()metodę String . Oto sztuczka:

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

Fajne hę? repeat()to bardzo przydatna metoda tworzenia ciągu znaków, który jest powtórzeniem ciągu oryginalnego kilka razy. Następnie split()tworzy dla nas tablicę, która następnie map()dostosowuje się do pożądanych wartości. Podział na etapy:

> "?".repeat(10)
< "??????????"

> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
Lucio Paiva
źródło
W tym poście jest wiele sztuczek salonowych, ale mam nadzieję, że żaden nie osiągnie kodu produkcyjnego :)
Eric Grange,
Chociaż repeatsztuczka zdecydowanie nie jest pożądana w produkcji, Array.from()jest całkowicie w porządku :-)
Lucio Paiva
Nie bardzo, Array.from () tutaj po prostu tworzy tablicę, iteruje ją za pomocą map (), wywołuje funkcję na każdym elemencie, aby utworzyć nową tablicę, a następnie odrzuca pierwszą tablicę ... W przypadku małych tablic może to być nieszkodliwy, w przypadku większych tablic, jest to taki wzorzec, który powoduje, że ludzie nazywają przeglądarki „świniami pamięci” :)
Eric Grange
Osoby zajmujące się dużymi tablicami powinny zdecydowanie lepiej to wiedzieć. Jednak w przypadku typowych aplikacji utworzenie tablicy pomocniczej o regularnych rozmiarach (do 10 000 elementów), która zostanie natychmiast usunięta, jest całkowicie w porządku (zajmuje tyle samo czasu, jakbyś uniknął tworzenia dodatkowej tablicy - testowany z najnowszym Chrome). W takich przypadkach czytelność staje się ważniejsza niż drobne optymalizacje wydajności. Jeśli chodzi o czas O (n), jest to konieczne, jeśli musisz obliczyć coś innego dla każdego elementu (główny temat mojej odpowiedzi). Ta dyskusja jest bardzo interesująca, cieszę się, że ją podniosłeś!
Lucio Paiva
88

W skrócie

Najszybsze rozwiązanie

let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;

Najkrótsze (przydatne) rozwiązanie (3x wolniejsze dla małych tablic, nieco wolniejsze dla dużych (najwolniejsze w Firefox))

Array(n).fill(0)


Detale

Dzisiaj 2020.06.09 Przeprowadzam testy na macOS High Sierra 10.13.6 w przeglądarkach Chrome 83.0, Firefox 77.0 i Safari 13.1. Testuję wybrane rozwiązania dla dwóch przypadków testowych

  • mała tablica - z 10 elementami - możesz wykonać test TUTAJ
  • duże tablice - z elementami 1M - możesz wykonać test TUTAJ

Wnioski

  • rozwiązanie oparte na new Array(n)+for (N) jest najszybszym rozwiązaniem dla małych tablic i dużych tablic (oprócz Chrome, ale wciąż tam bardzo szybko) i jest zalecane jako szybkie rozwiązanie dla różnych przeglądarek
  • rozwiązanie oparte na new Float32Array(n)(I) zwraca nietypową tablicę (np. nie można wywołać)push(..) ), więc nie porównuję jej wyników z innymi rozwiązaniami - jednak to rozwiązanie jest około 10-20 razy szybsze niż inne rozwiązania dla dużych tablic we wszystkich przeglądarkach
  • rozwiązania oparte na for (L, M, N, O) są szybkie dla małych tablic
  • rozwiązania oparte na fill (B, C) są szybkie w Chrome i Safari, ale zaskakująco najwolniejsze w Firefox dla dużych tablic. Są średnio szybkie dla małych tablic
  • rozwiązanie oparte na Array.apply(P) generuje błąd dla dużych tablic

wprowadź opis zdjęcia tutaj

Kod i przykład

Poniższy kod przedstawia rozwiązania stosowane w pomiarach

Przykładowe wyniki dla Chrome

wprowadź opis zdjęcia tutaj

Kamil Kiełczewski
źródło
Właśnie uruchomiłem kilka testów na Chrome 77 i prosta pętla z push () jest dwa razy szybsza niż fill () ... Zastanawiam się, jakie subtelne skutki uboczne fill () uniemożliwiają bardziej wydajną implementację?
Eric Grange,
@EricGrange Aktualizuję odpowiedź - na dole aktualizuję link do benchamrk z twoją propozycją: przypadek P let a=[]; for(i=n;i--;) a.push(0);- ale jest 4x wolniejszy niż fill(0)- więc nawet nie zaktualizuję obrazu z tą sprawą.
Kamil Kiełczewski
2
Niezłe pomiary. Analiza: G działa powoli z powodu zmiany rozmiaru tablicy przy każdej iteracji, a zmiana rozmiaru oznacza wykonanie nowej alokacji pamięci. A, B, M szybko, ponieważ zmiana rozmiaru odbywa się tylko raz. +1
Roland
Jest to jedna z niewielu odpowiedzi, która faktycznie próbuje odpowiedzieć na zadane pytanie. Bezskutecznie zawodzi, wpisując prawdopodobnie brak odpowiedzi i pomijając najszybszą prawdziwą odpowiedź, przynajmniej w czerwcu 2020 r. We wszystkich przeglądarkach a = new Array(n); for (let i = 0; i < n; ++i) a[i] = 0;. Test
gman
@gman właściwie masz rację - nie rozumiem, jak mogłem to przegapić - wykonuję nowe testy i aktualizuję odpowiedź o więcej szczegółów - dziękuję :)
Kamil Kiełczewski
63

Wspomniana już metoda napełniania ES 6 ładnie się tym zajmuje. Większość współczesnych przeglądarek stacjonarnych obsługuje obecnie wymagane metody prototypów Array (Chromium, FF, Edge i Safari) [ 1 ]. Możesz sprawdzić szczegóły w MDN . Prostym przykładem użycia jest

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

Biorąc pod uwagę obecne wsparcie przeglądarki, powinieneś być ostrożny z jej użyciem, chyba że masz pewność, że Twoi odbiorcy korzystają z nowoczesnych przeglądarek Desktop.

Gerald Senarclens de Grancy
źródło
4
Jeśli wypełnisz typ odniesienia, będzie on taki sam dla wszystkich. nowy Array (10) .fill (null) .map (() => []) byłby zwięzłym sposobem na obejście tego (spalił mnie początkowo haha)
John Culviner
4
AKTUALIZACJA 2016 : Ta metoda wysadza wszystko inne z wody, kliknij tutaj, aby zobaczyć wyniki: jsfiddle.net/basickarl/md5z0Lqq
Karl Morrison
to zadziała dla tablic. a = Array(10).fill(null).map(() => { return []; });
Andy,
2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Phrogz
50

Uwaga dodana w sierpniu 2013 r., Zaktualizowana w lutym 2015 r .: Poniższa odpowiedź z 2009 r. Dotyczy ogólnego Arraytypu JavaScript . To nie dotyczy nowszych pisanych tablic zdefiniowanych w ES2015 [a teraz dostępne w wielu przeglądarkach], jak Int32Arrayi takie. Należy również pamiętać, że ES2015 dodaje fillmetodę zarówno do tablic, jak i tablic maszynowych , co może być najbardziej wydajnym sposobem ich wypełnienia ...

Może to mieć duże znaczenie dla niektórych implementacji sposobu tworzenia tablicy. W szczególności silnik V8 Chrome próbuje użyć wysoce wydajnej, ciągłej tablicy pamięci, jeśli uważa, że ​​jest to możliwe, przechodząc na tablicę obiektową tylko w razie potrzeby.


W większości języków byłoby to wstępnie alokować, a następnie wypełniać zerowo, jak poniżej:

function newFilledArray(len, val) {
    var rv = new Array(len);
    while (--len >= 0) {
        rv[len] = val;
    }
    return rv;
}

Ale tablice JavaScript nie są tak naprawdę tablicami , są mapami klucz / wartość, tak jak wszystkie inne obiekty JavaScript, więc nie ma potrzeby „wstępnego przydzielania” (ustawienie długości nie przydziela tylu miejsc do wypełnienia), ani czy istnieje jakikolwiek powód, by sądzić, że korzyść z odliczenia do zera (co jest po prostu szybkie porównanie w pętli) nie jest ważniejsza przez dodanie kluczy w odwrotnej kolejności, gdy implementacja mogła zoptymalizować ich obsługę kluczy związane z tablicami teorii, generalnie wykonasz je w kolejności.

W rzeczywistości Matthew Crumley zwrócił uwagę, że odliczanie w Firefox jest znacznie wolniejsze niż liczenie w górę, co mogę potwierdzić - jest to jego część tablicowa (zapętlanie do zera jest wciąż szybsze niż zapętlenie do limitu w var). Najwyraźniej dodawanie elementów do tablicy w odwrotnej kolejności jest powolnym działaniem przeglądarki Firefox. W rzeczywistości wyniki różnią się nieco w zależności od implementacji JavaScript (co nie jest wcale takie zaskakujące). Oto szybka i brudna strona testowa (poniżej) dla implementacji przeglądarki (bardzo brudna, nie ustępuje podczas testów, więc zapewnia minimalną informację zwrotną i będzie działać z ograniczeniami czasowymi skryptów). Polecam odświeżenie między testami; FF (przynajmniej) spowalnia powtarzane testy, jeśli nie.

Dość skomplikowana wersja, która używa Array # concat, jest szybsza niż prosty init na FF, gdzieś pomiędzy 1000 a 2000 tablic elementów. Jednak w silniku V8 Chrome prosty init wygrywa za każdym razem ...

Oto strona testowa ( kopia na żywo ):

<!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
źródło
Nie jestem pewien, czy wypełnianie wstecz będzie miało tutaj znaczenie, biorąc pod uwagę, że uzyskujesz dostęp tylko do elementów (nie usuwając ich) i masz już wstępnie przydzielone. Czy się mylę?
Tryptyk
punkt zapełnienia do tyłu nie dotyczy w szczególności tablicy, ale na chwilę dotyczy warunku ucieczki - falsey 0 bardzo skutecznie kończy pętlę
annakata 18.08.2009
(chociaż właśnie zauważyłem, że ten kod nie korzysta z tego)
annakata,
@annakata, nie możesz z tego skorzystać tutaj, ponieważ 0 jest prawidłowym indeksem.
Tryptyk
@triptych: nieprawda, wystarczy właściwa kolejność - zobacz mój post
annakata,
34

Domyślnie Uint8Array, Uint16Arrayoraz Uint32Arrayzajęcia zachować jej wartości jako zera, więc nie potrzeba żadnych skomplikowanych technik napełniania, po prostu zrobić:

var ary = new Uint8Array(10);

wszystkie elementy tablicy arybędą domyślnie zerami.

martwy
źródło
5
To jest ładne, ale umysł uwaga ta nie może być traktowane tak samo jak zwykła tablica, na przykład Array.isArray(ary)jest false. Długość jest również tylko do odczytu, więc nie można wcisnąć do niej nowych elementów, jak w przypadkuary.push
MusikAnimal
Wszystkie typy tablic Fwiw zachowują 0jako domyślną wartość.
jfunk
2
@MusikAnimal, Array.from(new Uint8Array(10))zapewni normalną tablicę.
Tomas Langkaas
@TomasLangkaas: Tak, ale inna odpowiedź pokazuje, że jest to około 5 razy wolniej niż Array(n).fill(0)w Chrome, jeśli naprawdę potrzebujesz tablicy JS. Jeśli możesz użyć tablicy TypedArray, jest to o wiele szybsze nawet niż .fill(0), zwłaszcza jeśli możesz użyć domyślnej wartości inicjalizującej 0. Wydaje się, że nie ma konstruktora, który przyjmuje wartość wypełnienia i długość, tak jak std::vectorma to C ++ . Wydaje się, że dla każdej niezerowej wartości musisz zbudować wyzerowany TypedArray, a następnie go wypełnić. : /
Peter Cordes,
29

Jeśli używasz ES6, możesz użyć Array.from () w następujący sposób:

Array.from({ length: 3 }, () => 0);
//[0, 0, 0]

Ma taki sam wynik jak

Array.from({ length: 3 }).map(() => 0)
//[0, 0, 0]

Ponieważ

Array.from({ length: 3 })
//[undefined, undefined, undefined]
Foxiris
źródło
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']

Należy pamiętać, że whilejest zwykle bardziej wydajne niż for-in, forEachitp

kangax
źródło
3
Czy izmienna lokalna nie jest obca? lengthjest przekazywany przez wartość, więc powinieneś być w stanie bezpośrednio go zmniejszyć.
Sean Bright,
3
Chociaż na początku wygląda to świetnie, niestety bardzo wolno przypisuje się wartości w dowolnym punkcie w dowolnym momencie (np arr[i] = value.). O wiele szybciej jest przechodzić od początku do końca i używać arr.push(value). To denerwujące, ponieważ wolę twoją metodę.
Nick Brunt
19

za pomocą notacji obiektowej

var x = [];

zero wypełnione? lubić...

var x = [0,0,0,0,0,0];

wypełnione „niezdefiniowanym” ...

var x = new Array(7);

notacja obj z zerami

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

Na marginesie, jeśli zmodyfikujesz prototyp Array, oba

var x = new Array();

i

var y = [];

będą miały te prototypowe modyfikacje

W każdym razie nie przejmowałbym się zbytnio wydajnością ani szybkością tej operacji, istnieje wiele innych rzeczy, które prawdopodobnie będziesz robić, które są o wiele bardziej marnotrawne i kosztowne niż wprowadzanie tablicy o dowolnej długości zawierającej zera.

Allen Rice
źródło
5
Err ... nie ma nullw tej tablicy -var x = new Array(7);
kangax
5
W rzeczywistości tablica nie jest wypełniona niczym nowym Array (n), nawet „niezdefiniowanymi”, po prostu ustawia wartość długości tablic na n. Możesz to sprawdzić, wywołując (new Array (1)). ForEach (...). forEach nigdy się nie uruchamia, inaczej niż w przypadku wywołania [niezdefiniowany].
JussiR,
4
new Array(7)czy nie utworzyć tablicę „wypełnioną nieokreślone”. Tworzy pustą tablicę o długości 7.
RobG
1
Być może zechcesz ponownie rozważyć części odpowiedzi, ponieważ to, co mówi @RobG, ma kluczowe znaczenie (jeśli to, co mówisz, jest prawdą, mapowanie byłoby znacznie łatwiejsze)
Abdo
1
Te dni, które możesz zrobić (new Array(10)).fill(0).
Javier de la Rosa
18

Testowałem wszystkie kombinacje wstępnego przydzielania / nieprzydzielania wstępnego, liczenia w górę / w dół oraz pętli for / while w IE 6/7/8, Firefox 3.5, Chrome i Opera.

Poniższe funkcje były konsekwentnie najszybsze lub bardzo zbliżone w Firefoksie, Chrome i IE8 i niewiele wolniejsze niż najszybsze w Operze i IE 6. Jest to również najprostsze i najczystsze moim zdaniem. Znalazłem kilka przeglądarek, w których wersja pętli while jest nieco szybsza, więc dołączam ją również w celach informacyjnych.

function newFilledArray(length, val) {
    var array = [];
    for (var i = 0; i < length; i++) {
        array[i] = val;
    }
    return array;
}

lub

function newFilledArray(length, val) {
    var array = [];
    var i = 0;
    while (i < length) {
        array[i++] = val;
    }
    return array;
}
Matthew Crumley
źródło
1
Możesz także wrzucić var array = []deklarację do pierwszej części pętli for, oddzielonej przecinkiem.
damianb,
Podoba mi się ta sugestia damianb, ale pamiętaj, aby umieścić przyrostek i przecinek przed inkrementacją! `for (var i = 0; i <length; array [i] = val, i ++);
punstress
Zrób to, czego brakuje wszystkim innym, i ustaw długość tablicy na lengthwartość już podaną, aby nie ulegała ciągłym zmianom. Na mojej maszynie przyniosłem tablicę 1 milion długości zer od 40 ms do 8.
Jonathan Gray
Wydaje mi się, że dostaję wzrost prędkości o 10-15%, kiedy przefakturuję to rozwiązanie na jedną warstwę. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Mniej bloków? ... w każdym razie także ... jeśli array.lengthustawię nową tablicę na długość .. wydaje mi się, że dostanę kolejne 10% -15% zwiększenie prędkości FF ... w Chrome wydaje się, że podwoi prędkość -> var i, array = []; array.length = length; while(i < length) array[i++] = val;(był jeszcze szybszy, gdybym zostawił go jako forpętlę ... ale init nie jest już potrzebny, więc whilewydaje się, że jest szybszy w tej wersji)
Pimp Trizkit
Zwrócę też uwagę na to podczas moich testów. W przyzwoitej liczbie moich przypadków testowych powyższa wersja wydaje się działać 3x do ponad 10 razy szybciej ... Nie jestem pewien, dlaczego ... (różne rozmiary macierzy testowane między chromem a FF)
Pimp Trizkit
13

Jeśli podczas wykonywania kodu musisz utworzyć wiele wypełnionych zerami tablic o różnych długościach, najszybszym sposobem, jaki udało mi się to osiągnąć, jest utworzenie tablicy zerowej jeden raz , przy użyciu jednej z metod wymienionych w tym temacie, o długości które, jak wiesz, nigdy nie zostanie przekroczone, a następnie pokrój tę tablicę w razie potrzeby.

Na przykład (używając funkcji z wybranej powyżej odpowiedzi do zainicjowania tablicy), utwórz tablicę wypełnioną zerami o długości maxLength , jako zmienną widoczną dla kodu, który potrzebuje tablic zerowych:

var zero = newFilledArray(maxLength, 0);

Teraz pokrój tę tablicę za każdym razem, gdy potrzebujesz wymaganej zerowej tablicy o wymaganej długości Długość < max Długość :

zero.slice(0, requiredLength);

Podczas wykonywania mojego kodu tysiące razy tworzyłem tablice wypełnione zerami, co ogromnie przyspieszyło ten proces.

Nenad Vukicevic
źródło
13
function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}
Eli
źródło
3
Możesz także użyć new Array(size+1).join("x").split("x").map(function() { return 0; })do uzyskania rzeczywistych liczb
Yuval,
6
@Yuval Lub po prostunew Array(size+1).join('0').split('').map(Number)
Paul
11

Nie mam nic przeciwko:

Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
new Array(5+1).join('0').split('').map(parseFloat);

sugerowane przez Zertosha, ale w nowych rozszerzeniach macierzy ES6 pozwala to robić natywnie za pomocą fillmetody. Teraz IE edge, Chrome i FF obsługuje to, ale sprawdź tabelę kompatybilności

new Array(3).fill(0)da ci [0, 0, 0]. Możesz wypełnić tablicę dowolną wartością, taką jak new Array(5).fill('abc')(nawet obiekty i inne tablice).

Ponadto możesz modyfikować poprzednie tablice za pomocą fill:

arr = [1, 2, 3, 4, 5, 6]
arr.fill(9, 3, 5)  # what to fill, start, end

co daje ci: [1, 2, 3, 9, 9, 6]

Salvador Dali
źródło
10

Sposób, w jaki zwykle to robię (i jest niesamowicie szybki), to używanie Uint8Array. Na przykład, tworzenie wektora wypełnionego zerami elementów 1M:

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

Jestem użytkownikiem Linuksa i zawsze dla mnie pracował, ale kiedyś znajomy korzystający z komputera Mac miał pewne niezerowe elementy. Myślałem, że jego maszyna działa nieprawidłowo, ale wciąż jest to najbezpieczniejszy sposób, aby go naprawić:

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

Edytowane

Chrome 25.0.1364.160

  1. Frederik Gottlieb - 6,43
  2. Sam Barnum - 4,83
  3. Eli - 3,68
  4. Jozuego 2.91
  5. Mathew Crumley - 2,67
  6. bduran - 2,55
  7. Ryż Allen - 2.11
  8. kangax - 0,68
  9. TJ Crowder - 0,67
  10. zertosh - BŁĄD

Firefox 20.0

  1. Ryż Allen - 1,85
  2. Jozuego - 1,82
  3. Mathew Crumley - 1,79
  4. bduran - 1,37
  5. Frederik Gottlieb - 0,67
  6. Sam Barnum - 0,63
  7. Eli - 0,59
  8. kagax - 0,13
  9. TJ Crowder - 0,13
  10. zertosh - BŁĄD

Brakuje najważniejszego testu (przynajmniej dla mnie): Node.js. Podejrzewam, że jest zbliżony do testu porównawczego Chrome.

durum
źródło
To najbardziej skuteczny sposób dla moich palców i dla moich oczu. Ale dla Chrome jest to bardzo powolne (zgodnie z tym jsperf. 99% wolniej).
Orwellophile
1
Zastanawiam się, czy problem na komputerze Mac twojego znajomego był związany z: stackoverflow.com/questions/39129200/... a może Array.slice nie obsługiwał tablicy UInt8Array i wyciekał niezainicjowaną pamięć? (problem bezpieczeństwa!).
robocat
@robocat Good catch! Jeśli dobrze pamiętam, używaliśmy Node.js 0.6 lub 0.8. Myśleliśmy o pewnym przecieku, ale nie mogliśmy go odtworzyć na stosie produkcyjnym, więc postanowiliśmy go zignorować.
durum
10

Za pomocą lodash lub podkreślenia

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

Lub jeśli masz tablicę i chcesz tablicę o tej samej długości

array.map(_.constant(0));
djechlin
źródło
Tak się cieszę, że dodałeś tę odpowiedź, gdy używam podkreślenia, i wiedziałem, że jest na to coś ... ale jeszcze nie byłem w stanie jej znaleźć. Chciałbym tylko móc tworzyć tablice obiektów za pomocą tego
PandaWood
@PandaWood _.range (0, długość -1, 0) .mapa (Object.new), tak myślę.
djechlin
Powinien być _.range(0, length, 0), wierzę. Lodash nie ma wartości końcowej
użytkownik4815162342,
9

Rozwiązanie ES6:

[...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0]
Vic
źródło
8

Od ECMAScript2016 istnieje jeden jasny wybór dla dużych tablic.

Ponieważ ta odpowiedź wciąż pojawia się u góry w wynikach wyszukiwania Google, oto odpowiedź na rok 2017.

Oto aktualny jsbench z kilkoma popularnymi metodami, w tym wieloma zaproponowanymi do tej pory na to pytanie. Jeśli znajdziesz lepszą metodę, dodaj, rozwidlaj i udostępniaj.

Chcę zauważyć, że nie ma naprawdę najbardziej efektywnego sposobu na utworzenie tablicy o dowolnej długości wypełnionej zerą. Możesz zoptymalizować szybkość, jasność i łatwość konserwacji - albo można uznać, że jest to bardziej wydajny wybór w zależności od potrzeb projektu.

Podczas optymalizacji prędkości chcesz: utworzyć tablicę przy użyciu składni literalnej; ustaw długość, zainicjuj zmienną iteracyjną i iteruj po tablicy za pomocą pętli while. Oto przykład.

const arr = [];
arr.length = 120000;
let i = 0;
while (i < 120000) {
  arr[i] = 0;
  i++;
}

Inną możliwą implementacją byłoby:

(arr = []).length = n;
let i = 0;
while (i < n) {
    arr[i] = 0;
    i++;
}

Ale zdecydowanie odradzam stosowanie tej drugiej implantacji w praktyce, ponieważ jest ona mniej przejrzysta i nie pozwala zachować zakresu bloków w zmiennej tablicowej.

Są one znacznie szybsze niż wypełnianie pętli for i około 90% szybciej niż standardowa metoda

const arr = Array(n).fill(0);

Jednak ta metoda wypełniania jest nadal najbardziej wydajnym wyborem dla mniejszych tablic ze względu na jej przejrzystość, zwięzłość i łatwość konserwacji. Różnica w wydajności prawdopodobnie Cię nie zabije, chyba że wykonasz wiele tablic o długości rzędu tysięcy lub więcej.

Kilka innych ważnych uwag. Większość przewodników po stylu zaleca, aby nie używać już varbez specjalnego powodu podczas używania ES6 lub nowszego. Użyj constdla zmiennych, które nie zostaną ponownie zdefiniowane, i letdla zmiennych, które będą. MDN i Airbnb za Style Guide to wspaniałe miejsca, aby znaleźć więcej informacji na temat najlepszych praktyk. Pytania nie dotyczyły składni, ale ważne jest, aby ludzie nowi w JS wiedzieli o tych nowych standardach podczas przeszukiwania tych rygli starych i nowych odpowiedzi.

Izaak B.
źródło
8

Aby utworzyć zupełnie nową macierz

new Array(arrayLength).fill(0);

Aby dodać niektóre wartości na końcu istniejącej tablicy

[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]

Przykład

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

Juanma Menendez
źródło
6

Nie widziałem tej metody w odpowiedziach, więc oto ona:

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

W rezultacie otrzymasz tablicę o wartości zerowej o długości 200:

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

Nie jestem pewien wydajności tego kodu, ale nie powinno to stanowić problemu, jeśli używasz go do stosunkowo małych tablic.

Eugene Tiurin
źródło
5
Ani najszybszy, ani najkrótszy, ale niezły wkład w różnorodność rozwiązań.
7vujy0f0hy 19.04.17
5

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

Alex dykyі
źródło
1
To nie wypełnia tablicy zerami. Wypełnia go niezdefiniowanym.
jlh
@jlh dzięki naprawiono
Alex dykyі
4

Ta concatwersja jest znacznie szybsza w moich testach na Chrome (2013-03-21). Około 200 ms dla 10 000 000 elementów w porównaniu z 675 dla prostego 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));
}

Bonus: jeśli chcesz wypełnić tablicę ciągami, jest to zwięzły sposób na zrobienie tego (nie tak szybko, jak concatgdyby):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}
Sam Barnum
źródło
2
Ok, dzika. To jest DROGA szybszy niż używanie nowej macierzy (len). ALE! Widzę w Chrome, że kolejne odczyty tych danych trwają znacznie dłużej. Oto kilka znaczników czasu, które pokazują, co mam na myśli: (Korzystanie z nowej macierzy (len)) 0.365: Tworzenie macierzy 4.526: Wykonywanie konwolucji 10.75: Wykonywanie konwolucji (używanie konkat) 0.339: Tworzenie tablicy 0.591: Wykonywanie konwolucji // OMG, WAY szybciej 18.056: Konwolucja zakończona
Brooks
4

Testowałem świetną odpowiedź TJ Crowdera i wymyśliłem rekurencyjne scalanie oparte na rozwiązaniu konkat, które przewyższa wszelkie w swoich testach w Chrome (nie testowałem innych przeglądarek).

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

wywołaj metodę za pomocą makeRec(29).

Frederik Gottlieb
źródło
4

Co new Array(51).join('0').split('')?

Cory Mawhorter
źródło
1
więc .map(function(a){return +a})?
lonewarrior556
4

Warto zauważyć, że Array.prototype.fillzostał dodany w ramach propozycji ECMAScript 6 (Harmonia) . Wolę pójść z polifillem napisanym poniżej, zanim rozważę inne opcje wymienione w wątku.

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;
  };
}
Ivo
źródło
4

Najkrótszy dla kodu pętli

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;

Bezpieczna wersja var

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

edit:
for(var i=100,a=[];i--;)a[i]=0;
nathnolt
źródło
2
Biorąc pod uwagę, że długość jest zdefiniowaną zmienną, nbyłaby ona krótsza:for(var a=[];n--;a[n]=0);
Tomas Langkaas
4

let filled = [];
filled.length = 10;
filled.fill(0);

console.log(filled);

Tomiwa Adefokun
źródło
3

Moją najszybszą funkcją byłoby:

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

Używanie natywnego push i shift do dodawania elementów do tablicy jest znacznie szybsze (około 10 razy) niż deklarowanie zakresu tablicy i odwoływanie się do każdego elementu w celu ustawienia jego wartości.

fyi: Konsekwentnie otrzymuję szybsze czasy dzięki pierwszej pętli, która się odlicza, gdy uruchamiam to w firebug (rozszerzenie 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

Chcę wiedzieć, co robi z tego TJ Crowder? :-)

Jozuego
źródło
Możesz go przyspieszyć, zmieniając go na while (len--).. zabrało mi czas przetwarzania od około 60 ms do około 54 ms
nickf
Odpowiedź Matthew Crumbly'ego wciąż tak naprawdę bije (30ms)!
nickf
3

Wiedziałem, że mam gdzieś ten proto :)

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

Edycja: testy

W odpowiedzi na Joshua i inne metody przeprowadziłem własne testy porównawcze i widzę zupełnie inne wyniki niż te zgłoszone.

Oto co przetestowałem:

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

Wyniki:

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

Według mojego obliczenia push jest generalnie wolniejszy, ale działa lepiej z dłuższymi tablicami w FF, ale gorzej w IE, który po prostu jest do kitu (niespodzianka quel).

annakata
źródło
Właśnie to przetestowałem: druga metoda ( b = []...) jest 10-15% szybsza niż pierwsza, ale jest ponad 10 razy wolniejsza niż odpowiedź Jozuego.
nickf
Wiem, że to starożytny post . Ale może nadal jest interesujący dla innych (takich jak ja). Dlatego chciałbym zaproponować adition do funkcji prototypowej: jak else {this.length=n;}po this.length-Check. W razie potrzeby skróci to już istniejącą tablicę podczas ponownej initinicjalizacji jej do innej długości n.
samochody 10
2

Funkcja anonimowa:

(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]

Nieco krótszy z pętlą for:

(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]

Działa z każdym Object, wystarczy zmienić to, co jest w środku this.push().

Możesz nawet zapisać funkcję:

function fill(size, content) {
  for(;size--;this.push(content));
  return this;
}

Zadzwoń, używając:

var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']

Dodawanie elementów do już istniejącej tablicy:

var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']

Wydajność: http://jsperf.com/zero-filled-array-creation/25

Mateo Gianolio
źródło