Jaki jest najskuteczniejszy sposób na utworzenie dowolnej liczby pól wypełnionych zerami w JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Jaki jest najskuteczniejszy sposób na utworzenie dowolnej liczby pól wypełnionych zerami w JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Odpowiedzi:
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 .
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
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.
(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 ..
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.
new Array(5).forEach(val => console.log('hi'));kontra new Array(5).fill(undefined).forEach(val => console.log('hi'));.
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]
Array.apply(null, new Array(5)).map(...)? Bo po prostu robienie (nowa tablica (5)). Mapa (...) nie będzie działać, jak mówi specyfikacja
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.
.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.
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]
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]
repeatsztuczka zdecydowanie nie jest pożądana w produkcji, Array.from()jest całkowicie w porządku :-)
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)
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
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ądareknew 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ądarkachfor (L, M, N, O) są szybkie dla małych tablicfill (B, C) są szybkie w Chrome i Safari, ale zaskakująco najwolniejsze w Firefox dla dużych tablic. Są średnio szybkie dla małych tablicArray.apply(P) generuje błąd dla dużych tablic
Poniższy kod przedstawia rozwiązania stosowane w pomiarach
Przykładowe wyniki dla Chrome
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ą.
a = new Array(n); for (let i = 0; i < n; ++i) a[i] = 0;. Test
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.
a = Array(10).fill(null).map(() => { return []; });
a = Array(10).fill(0).map( _ => [] );
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>
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.
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
0jako domyślną wartość.
Array.from(new Uint8Array(10))zapewni normalną tablicę.
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ć. : /
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]
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
izmienna lokalna nie jest obca? lengthjest przekazywany przez wartość, więc powinieneś być w stanie bezpośrednio go zmniejszyć.
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ę.
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.
nullw tej tablicy -var x = new Array(7);
new Array(7)czy nie utworzyć tablicę „wypełnioną nieokreślone”. Tworzy pustą tablicę o długości 7.
(new Array(10)).fill(0).
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;
}
var array = []deklarację do pierwszej części pętli for, oddzielonej przecinkiem.
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.
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)
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.
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]
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
Firefox 20.0
Brakuje najważniejszego testu (przynajmniej dla mnie): Node.js. Podejrzewam, że jest zbliżony do testu porównawczego Chrome.
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));
_.range(0, length, 0), wierzę. Lodash nie ma wartości końcowej
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.
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)]
//**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)]);
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.
const arr = Array.from({ length: 10 }).fill(0)
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('');
}
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).
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;
};
}
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;
nbyłaby ona krótsza:for(var a=[];n--;a[n]=0);
let filled = [];
filled.length = 10;
filled.fill(0);
console.log(filled);
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? :-)
while (len--).. zabrało mi czas przetwarzania od około 60 ms do około 54 ms
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).
b = []...) jest 10-15% szybsza niż pierwsza, ale jest ponad 10 razy wolniejsza niż odpowiedź Jozuego.
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.
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']