Sıfır doldurulmuş bir JavaScript dizisi oluşturmanın en etkili yolu nedir?


602

JavaScript'te rasgele uzunluk sıfır dolu dizi oluşturmanın en etkili yolu nedir?


7
Bu konuda bazı gerçek veriler: jsperf.com/zeroarrayjs
Web_Designer

7
ES6 dolgusu bunu doğal olarak yapmayı sağlar.
Salvador Dali

1
arr = yeni Dizi (uzunluk + 1). eklem (karakter) .split ('');
Jordan Stefanelli

4
GÜNCELLEME 2016 : Burada bir başka özel kriter: jsfiddle.net/basickarl/md5z0Lqq
K -

1
let i = 0; Array.from(Array(10), ()=>i++);
Bart Hoekstra

Yanıtlar:


543

ES6 tanıtıyor Array.prototype.fill. Bu şekilde kullanılabilir:

new Array(len).fill(0);

Hızlı olup olmadığından emin değilim, ama hoşuma gidiyor çünkü kısa ve kendini tarif ediyor.

Hala IE'de değil ( uyumluluğu kontrol edin ), ancak kullanılabilir bir çoklu dolgu var .


15
dolgu hızlı. new Array(len)acı verici yavaştır. (arr = []).length = len; arr.fill(0);herhangi bir yerde görülen en hızlı çözüm hakkında ... ya da en azından bağlı
Pimp Trizkit

7
@PimpTrizkit arr = Array(n)ve (arr = []).length = nşartnameye göre aynı şekilde davranır. Bazı uygulamalarda daha hızlı olabilir, ancak büyük bir fark olduğunu düşünmüyorum.
Oriol

2
Çok boyutlu dizilerle test etmeye başladım ve test senaryolarımı büyük ölçüde hızlandırdı. Şimdi FF41 ve Chrome üzerinde biraz daha test yaptıktan sonra 45.0.2454.99 m. Evet, sanırım kendimi açıklamak için daha fazla alana ihtiyacım vardı. Testlerimin çoğu itiraf edeceğim önyargı oldu. Ama şuna bir bak. Bir varı önceden tanımlayın ve hem Chrome'da hem de FF'de hız testine (arr = []).length = 1000; karşı bu satırı kullanmak arr = new Array(1000);... newçok yavaş. Şimdi, daha küçük dizi uzunlukları için .. <50 ya da orada hakkında ... sonra new Array()daha iyi performans gibi görünüyor. But ..
Pimp Trizkit

4
... bu bölümü kaçırdığımı itiraf edeceğim ... teste ikinci satırı eklediğimde ... arr.fill(0) ... her şey değişti. Şimdi, new Array()100000'den büyük dizi boyutlarına erişmeniz dışında çoğu durumda kullanmak daha hızlıdır ... Sonra hız artışını tekrar görmeye başlayabilirsiniz. Ama aslında sıfırlarla doldurmak zorunda değilsiniz ve boş dizilerin standart yanlışını kullanabilirsiniz. Sonra (arr = []).length = xçoğu zaman test vakalarımda çılgınca.
Pezevenk Trizkit

4
Dizi üzerinde yineleme yapmak için (örneğin, map veya forEach) değerlerin ayarlanması gerektiğini , aksi takdirde bu dizinleri atlayacağını unutmayın. Belirlediğiniz değerler istediğiniz şekilde olabilir - tanımsız olsa bile. Örnek: deneme new Array(5).forEach(val => console.log('hi'));vs new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo

387

Bu eski bir iş parçacığı olmasına rağmen, 2 sent eklemek istedim. Bunun ne kadar yavaş / hızlı olduğundan emin değilim, ama hızlı bir astar. İşte yaptığım şey:

Bir numarayla önceden doldurmak istersem:

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

Bir dize ile önceden doldurmak isterseniz:

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

Diğer cevaplar önerdi:

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

ancak "0" (bir dize içinde sıfır) istemiyorsanız 0 (sayı) istiyorsanız şunları yapabilirsiniz:

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

6
Mükemmel cevap! Hile ile açıklayabilir misiniz Array.apply(null, new Array(5)).map(...)? Çünkü basitçe yapıyor (yeni Dizi (5)). Harita (...) spesifikasyonun söylediği gibi çalışmaz
Dmitry Pashkevich

36
(btw, gerçekten ihtiyacımız yok new) Bunu yaptığınızda Array(5)şöyle görünen bir nesne yaratıyorsunuz: { length: 5, __proto__: Array.prototype }- deneyin console.dir( Array(5) ). Bildirim herhangi özelliklere sahip olmadığını 0, 1, 2vb Ama sizi zaman applyşöyle böyle Arrayyapıcı, bu demek gibi Array(undefined, undefined, undefined, undefined, undefined). Ve biraz benzeyen bir nesne elde edersiniz { length: 5, 0: undefined, 1: undefined...}. mapözellikleri 0, 1vb. üzerinde çalışır, bu nedenle örneğiniz çalışmaz, ancak kullandığınızda applyçalışır.
zertosh

4
İçin ilk parametre .applyaslında olmasını istediğiniz şeydir this. Bu amaçlar için thisönemli değil - sadece parametrenin "özelliğini" yaymakla ilgileniyoruz .apply- bu yüzden herhangi bir değer olabilir. Beğendim nullçünkü ucuz, muhtemelen kullanmak istemiyorsunuz {}veya []bir nesneyi sebepsiz yere somutlaştırdığınız için.
zertosh

2
Ayrıca boyut + atama ile başlatmak itme çok daha hızlıdır. Test senaryosuna
Colin

2
Array.apply (null, Array (5)) haritasından (x => 0) ne olacak? Biraz daha kısa!
Arch Linux Tux

97

Bir diziyi önceden hesaplanmış değerlerle doldurmanın zarif yolu

İşte ES6'yı kullanarak kimsenin bahsetmediği başka bir yol:

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

Bir harita fonksiyonunu ikinci parametre olarak geçirerek çalışır Array.from.

Yukarıdaki örnekte, ilk parametre değerle dolu 3 konumlu bir dizi undefinedayırır ve sonra lambda işlevi her birini değere eşler 0.

Her ne kadar Array(len).fill(0)daha kısa olsa da, önce bazı hesaplama yaparak diziyi doldurmanız gerekiyorsa işe yaramaz (sorunun sorulmadığını biliyorum, ancak birçok insan burada bunu arıyor) .

Örneğin, 10 rasgele sayı içeren bir diziye ihtiyacınız varsa:

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

Eşdeğerden daha özlü (ve zarif):

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

Bu yöntem, geri aramada sağlanan dizin parametresinden yararlanarak numara dizileri oluşturmak için de kullanılabilir:

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

Bonus cevap: Dizeyi kullanarak bir diziyi doldurun repeat()

Bu cevap çok dikkat çektiğinden, bu havalı hileyi de göstermek istedim. Benim ana cevap kadar yararlı olmasa da, hala çok bilinmeyen, ama çok yararlı String repeat()yöntemini tanıtacaktır . İşte hile:

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

Havalı değil mi? repeat()orijinal dizenin belirli sayıda yinelenmesi olan bir dize oluşturmak için çok kullanışlı bir yöntemdir. Bundan sonra, split()bizim için bir dizi oluşturur, bu daha sonra map()istediğimiz değerlere ayak uydurur. Adımlarla yıkmak:

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

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

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

Bu yazıdaki birçok salon hilesi, ancak umarım üretim koduna ulaşamaz :)
Eric Grange

repeatHile kesinlikle üretimde Array.from()
Lucio Paiva

Gerçekten değil, Array.from () burada temel olarak bir dizi yaratıyor, map () ile yineleniyor, her öğe üzerinde yeni bir dizi oluşturmak için bir işlev çağırıyor, sonra ilk diziyi atıyor ... Küçük diziler için bu olabilir zararsız, daha büyük diziler için, bu tür tarayıcılar "bellek domuzları" çağırmak sonuçlanan desen türüdür :)
Eric Grange

Büyük dizilerle uğraşan insanlar kesinlikle bundan daha iyi bilmelidir. Bununla birlikte, yaygın uygulamalar için, hemen atılacak normal boyutlu bir aux dizisi (10k öğeye kadar) oluşturmak mükemmel derecede iyidir (ekstra dizi oluşturma işleminden kaçındığınız zamanla aynı süreyi alır - en son Chrome ile test edilmiştir). Bu gibi durumlarda, okunabilirlik küçük performans optimizasyonlarından daha önemli hale gelir. O (n) zamanı hakkında, her eleman için farklı bir şey hesaplamanız gerekiyorsa (cevabımın ana konusu) gereklidir. Bu tartışma çok ilginç, yükselttiğiniz için mutluyuz!
Lucio Paiva

88

Kısacası

En hızlı çözüm

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

En kısa (kullanışlı) çözüm (küçük diziler için 3 kat daha yavaş, büyükler için biraz daha yavaş (Firefox'ta en yavaş))

Array(n).fill(0)


ayrıntılar

Bugün 2020.06.09 Chrome 83.0, Firefox 77.0 ve Safari 13.1 tarayıcılarında macOS High Sierra 10.13.6 üzerinde testler yapıyorum. İki test vakası için seçilen çözümleri test ediyorum

  • küçük bir dizi - 10 elementli - BURAYA test yapabilirsiniz
  • büyük diziler - 1M elemanları ile - BURAYA test yapabilirsiniz

Sonuçlar

  • new Array(n)+for(N) tabanlı çözüm, küçük diziler ve büyük diziler için en hızlı çözümdür (Chrome hariç ancak yine de çok hızlıdır) ve hızlı çapraz tarayıcı çözümü olarak önerilir
  • new Float32Array(n)(I) 'ye dayalı çözüm, tipik olmayan bir dizi döndürür (örneğin, bunu çağıramazsınız) push(..), bu yüzden sonuçlarını diğer çözümlerle karşılaştırmam - ancak bu çözüm, tüm tarayıcılardaki büyük diziler için diğer çözümlerden yaklaşık 10-20x daha hızlıdır
  • for(L, M, N, O) tabanlı çözümler küçük diziler için hızlıdır
  • fill(B, C) tabanlı çözümler Chrome ve Safari'de hızlı, ancak büyük diziler için Firefox'ta şaşırtıcı derecede yavaştır. Küçük diziler için orta hızlıdır
  • Array.apply(P) 'ye dayalı çözüm büyük diziler için hata atar

resim açıklamasını buraya girin

Kod ve örnek

Aşağıdaki kod ölçümlerde kullanılan çözümleri sunar

Chrome için örnek sonuçlar

resim açıklamasını buraya girin


Sadece Chrome 77 üzerinde bazı testler yaptık ve push () ile basit bir döngü doldurma () 'dan iki kat daha hızlı ...
Eric Grange

@EricGrange Cevabı güncelliyorum - en altta teklifinizle benchamrk bağlantısını güncelliyorum: case P let a=[]; for(i=n;i--;) a.push(0);- ama bundan 4 kat daha yavaş fill(0)- bu yüzden resim cadı güncellemeyeceğim bile.
Kamil Kiełczewski

2
Güzel ölçümler. Analiz: G, diziyi her yinelemede yeniden boyutlandırdığı için yavaştır ve yeniden boyutlandırma, yeni bir bellek ayırma işlemi yapmak anlamına gelir. A, B, M hızlı çünkü boyutlandırma sadece bir kez yapılır. +1
Roland

Bu, gerçekten sorulan soruya cevap vermeye çalışan birkaç cevaptan biridir. Ne yazık ki, tartışmasız bir cevap vermeden yapışıp en hızlı gerçek cevabı kaçırmak, en azından Haziran 2020'de tüm tarayıcılarda başarısız oluyor a = new Array(n); for (let i = 0; i < n; ++i) a[i] = 0;. Test
gman

@gman aslında haklısın - Nasıl özleyebileceğimi anlamıyorum - Yeni testler yapıyorum ve cevabı daha fazla ayrıntıyla güncelliyor - teşekkürler :)
Kamil Kiełczewski

63

Daha önce bahsedilen ES 6 doldurma yöntemi bunu iyi halleder. Çoğu modern masaüstü tarayıcısı bugün itibariyle gerekli Array prototip yöntemlerini desteklemektedir (Chromium, FF, Edge ve Safari) [ 1 ]. MDN ile ilgili ayrıntıları arayabilirsiniz . Basit bir kullanım örneği

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

Mevcut tarayıcı desteği verildiğinde, kitlenizin modern Masaüstü tarayıcıları kullandığından emin olmadığınız sürece bunu kullanmaya dikkat etmelisiniz.


4
Bir başvuru türünü doldurursanız, bu tüm referanslar için aynı referans olacaktır. yeni Array (10) .fill (null) .map (() => []) bunun üstesinden gelmek için kısa bir yol olurdu (başlangıçta beni yaktı haha)
John Culviner

4
GÜNCELLEME 2016 : Bu yöntem her şeyi sudan dışarı atıyor
K -

bu diziler için işe yarayacaktır. a = Array(10).fill(null).map(() => { return []; });
Andy

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

50

Not Ağustos 2013, güncellenmiş Şubat 2015 eklendi: 2009'dan aşağıdaki cevap JavaScript'in genel Arraytürü ile ilgilidir . ES2015'te tanımlanan (ve şimdi birçok tarayıcıda kullanılabilir), vb. Gibi daha yeni yazılan dizilerle ilgili değildir Int32Array. Ayrıca ES2015 bir ekler dikkat fillhem yöntemini Dizileri ve daktilo diziler bunları doldurmak için en verimli şekilde olması muhtemeldir, ...

Ayrıca, diziyi nasıl oluşturduğunuzda bazı uygulamalarda büyük fark yaratabilir. Özellikle Chrome'un V8 motoru, yalnızca gerektiğinde nesne tabanlı diziye geçerek, olabileceğini düşünüyorsa yüksek verimli, bitişik bir bellek dizisi kullanmaya çalışır.


Çoğu dilde, önceden tahsis edildikten sonra sıfır doldurulur, şöyle:

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

Ancak , JavaScript dizileri gerçekte diziler değildir , tıpkı diğer tüm JavaScript nesneleri gibi anahtar / değer haritalarıdır, bu nedenle yapılacak "ön ayırma" yoktur (uzunluğu ayarlamak, birçok alanı doldurmak için ayırmaz) veya Uygulama, anahtarların işlenmesini optimize ettiğinde anahtarları ters sırayla ekleyerek sıfıra geri saymanın (yalnızca döngüdeki karşılaştırmayı hızlı hale getirmek için) daha fazla olmadığına inanmak için herhangi bir neden var mı? teoriye göre dizilerle ilgili olarak bunları genellikle sırayla yaparsınız.

Aslında, Matthew Crumley, geri sayımın Firefox'ta sayımdan belirgin şekilde daha yavaş olduğunu, onaylayabildiğim bir sonuç olduğuna dikkat çekti - bunun dizi kısmı (sıfıra inmek hala bir var olan bir sınıra kadar döngüden daha hızlı). Görünüşe göre öğeleri diziye ters sırayla eklemek Firefox'ta yavaş bir işlemdir. Aslında, sonuçlar JavaScript uygulamasıyla oldukça değişkendir (bu şaşırtıcı değildir). İşte tarayıcı uygulamaları için hızlı ve kirli bir test sayfası (aşağıda) (çok kirli, testler sırasında verim yok, bu nedenle minimum geri bildirim sağlar ve komut dosyası zaman sınırları ile çalışır). Testler arasında yenilenmeyi öneriyorum; Eğer yapmazsanız FF (en azından) tekrarlanan testlerde yavaşlar.

Array # concat kullanan oldukça karmaşık sürüm, 1.000 ila 2.000 eleman dizisi arasında bir yerde olduğu gibi FF'deki düz bir başlatmadan daha hızlıdır. Bununla birlikte, Chrome'un V8 motorunda her seferinde düz init kazanıyor ...

İşte test sayfası ( canlı kopya ):

<!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>

Yalnızca öğelere eriştiğiniz (bunları silmediğiniz) ve önceden ayırdığınız için geriye doğru doldurmanın önemli olacağından emin değilsiniz. Yanlış mıyım?
Triptik

geriye doğru dolgunun noktası özellikle dizi ile ilgili değildir, bu süre için kaçış koşulu ile ilgilidir
falsey

(Ben sadece bu kod aslında bundan yararlanmadığını fark ettim)
annakata

@annakata, burada bundan yararlanamazsınız, çünkü 0 geçerli bir dizintir.
Triptik

@triptych: doğru değil, tek gereken doğru sipariş
yazıma

34

Varsayılan Uint8Arrayolarak Uint16Arrayve Uint32Arraysınıflar sıfırları değerleri olarak tutar, bu nedenle karmaşık doldurma tekniklerine ihtiyacınız yoktur, sadece şunları yapın:

var ary = new Uint8Array(10);

dizinin tüm öğeleri aryvarsayılan olarak sıfır olacaktır.


5
Bu güzel ama bu zihni notu örneğin normal bir dizide aynı muamele edilemez Array.isArray(ary)olduğunu false. Uzunluk da salt okunur olduğundan, yeni öğeleri olduğu gibi ary.push
itemezsiniz

Tüm yazılan dizileri 0varsayılan değer olarak tutar.
jfunk

2
@MusikAnimal, Array.from(new Uint8Array(10))normal bir dizi sağlar.
Tomas Langkaas

@TomasLangkaas: Evet, ancak başka bir cevap , Array(n).fill(0)gerçekten ihtiyacınız olan bir JS Dizisi ise Chrome'dan yaklaşık 5 kat daha yavaş olduğunu gösteriyor . TypedArray kullanabiliyorsanız .fill(0), özellikle de varsayılan başlatıcı değerini kullanabiliyorsanız bu çok daha hızlıdır 0. C ++ 'ın std::vectorsahip olduğu gibi bir dolgu değeri ve uzunluğu alan bir kurucu yok gibi görünüyor . Sıfır olmayan herhangi bir değer için, sıfırlanmış bir TypedArray oluşturmanız ve sonra doldurmanız gerekir. : /
Peter Cordes

29

ES6 kullanıyorsanız, aşağıdaki gibi Array.from () kullanabilirsiniz :

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

İle aynı sonuca sahiptir

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

Çünkü

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']

Not whilegenellikle daha etkilidir for-in, forEachvb


3
Is not iyerel değişken gereksiz? lengthdeğeri geçerek doğrudan azaltabiliyor olmanız gerekir.
Sean Bright

3
Bu başlangıçta harika görünse de, maalesef bir arardaki keyfi bir noktada değerler atamak çok yavaştır (örn. arr[i] = value). Başından sonuna kadar döngü yapmak ve kullanmak çok daha hızlı arr.push(value). Bu sinir bozucu, çünkü yönteminizi tercih ediyorum.
Nick Brunt

19

nesne gösterimini kullanma

var x = [];

sıfır dolu mu? sevmek...

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

'undefined' ile dolu ...

var x = new Array(7);

sıfırlarla obj gösterimi

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

Bir yan not olarak, Array'ın prototipini değiştirirseniz, her ikisi de

var x = new Array();

ve

var y = [];

bu prototip değişikliklerine sahip olacak

Her halükarda, bu operasyonun verimliliği veya hızı ile aşırı derecede ilgilenmiyorum, muhtemelen sıfırlar içeren bir dizi rasgele uzunluk oluşturmaya kıyasla çok daha israf ve pahalı olacak başka şeyler de var.


5
Hata ... nullBu dizide hiç s yok -var x = new Array(7);
kanguru

5
Aslında dizi, 'tanımsız' bile değil, yeni Array (n) ile hiçbir şeyle dolmaz, dizilerin uzunluk değerini n olarak ayarlar. Bunu (yeni Array (1)) öğesini arayarak kontrol edebilirsiniz. ForEach (...). forEach asla [undefined] olarak çağırırsanız asla çalışmaz.
JussiR

4
new Array(7)yok değil bir dizi "tanımlanmamış ile dolu" oluşturun. Uzunluğu 7 olan boş bir dizi oluşturur .
RobG

1
@RobG'nin söylediği kritik öneme sahip olduğu için cevabınızın bazı bölümlerini yeniden düşünmek isteyebilirsiniz (söylediğiniz şey doğruysa, haritalama çok daha kolay olurdu)
Abdo

1
Bugünlerde yapabilirsin (new Array(10)).fill(0).
Javier de la Rosa

18

IE 6/7/8, Firefox 3.5, Chrome ve Opera'da ön ayırma / ön ayırmama, yukarı / aşağı sayma ve döngüler için tüm kombinasyonları test ettim.

Aşağıdaki işlevler Firefox, Chrome ve IE8'de sürekli olarak en hızlı veya son derece yakındı ve Opera ve IE 6'daki en hızlıdan çok daha yavaş değildi. While döngüsü sürümünün biraz daha hızlı olduğu birkaç tarayıcı buldum, bu yüzden referans için de dahil ediyorum.

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

veya

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

1
Ayrıca, var array = []bildirimi yalnızca virgülle ayrılmış olarak for döngüsünün ilk kısmına da atabilirsiniz .
Damianb

Damianb'ın bu önerisini beğendim, ancak ödevi ve virgülü eklemeden önce koymayı unutmayın! `(var i = 0; i <uzunluk; dizi [i] = val, i ++);
15'te cezalandırıcı

İkincisinde eksik olan her şeyi yapın ve dizinin uzunluğunu, lengthsürekli olarak değişmeyecek şekilde önceden verilen değere ayarlayın . Makinemde 40 milyondan 8'e 1 milyon uzunlukluk sıfır dizi getirdik.
Jonathan Gray

Bu çözümü tek bir astara yeniden düzenlediğimde% 10-15'lik bir hız artışı görüyorum. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Daha az blok mu? ... her neyse, ayrıca ... eğer array.lengthyeni diziyi uzunluğa ayarlarsam .. FF'de% 10 -% 15'lik bir başka hız artışı görüyorum ... Chrome'da, hızı iki katına çıkıyor gibi görünüyor -> var i, array = []; array.length = length; while(i < length) array[i++] = val;(bir fordöngü olarak bıraktığımda hala daha hızlıydı ... ama init artık gerekli değil, bu yüzden whilebu sürümde daha hızlı görünüyor)
Pimp Trizkit 22:15

Bunu testlerimde de not edeceğim. Test durumlarımın iyi bir sayısında, yukarıdaki son sürüm 3
kattan

13

Kodunuzun yürütülmesi sırasında farklı uzunluklarda sıfır dolu diziler oluşturmanız gerekiyorsa, bunu başarmanın en hızlı yolu , bu konuda belirtilen yöntemlerden birini kullanarak bir kez sıfır dizi oluşturmaktır. asla aşılmayacağını bildiğiniz bir diziyi gerektiği gibi dilimleyin.

Örneğin (diziyi başlatmak için yukarıdaki seçili cevaptaki işlevi kullanarak), sıfır diziye ihtiyaç duyan kodun görebileceği bir değişken olarak sıfır dolu uzunluk maxLength dizisi oluşturun:

var zero = newFilledArray(maxLength, 0);

Şimdi uzunluğu bir sıfır dolu dizi gerek bu dizi her şey dilim requiredLength < maxLength :

zero.slice(0, requiredLength);

Kodumun yürütülmesi sırasında binlerce kez sıfır dolu diziler oluşturuyordum, bu süreci çok hızlandırdı.


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; })Gerçek rakamları almak için de kullanabilirsiniz
Yuval

6
@Yuval Veya sadecenew Array(size+1).join('0').split('').map(Number)
Paul

11

Karşı hiçbir şeyim yok:

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

Zertosh tarafından önerilen, ancak yeni bir ES6 dizi uzantısında bunu fillyöntemle yerel olarak yapmanızı sağlar . Şimdi IE edge, Chrome ve FF destekliyor, ancak uyumluluk tablosunu kontrol edin

new Array(3).fill(0)verecek [0, 0, 0]. Diziyi herhangi bir değerle new Array(5).fill('abc')(hatta nesneler ve diğer diziler) doldurabilirsiniz .

Üstelik önceki dizileri dolgu ile değiştirebilirsiniz:

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

hangi size verir: [1, 2, 3, 9, 9, 6]


10

Genellikle yaptığım (ve inanılmaz hızlı) yolu kullanıyor Uint8Array. Örneğin, 1M öğelerinin sıfır dolu bir vektörü oluşturma:

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

Ben bir Linux kullanıcısıyım ve her zaman benim için çalıştım, ancak Mac kullanan bir arkadaşın sıfır olmayan elementleri vardı. Makinesinin arızalı olduğunu düşündüm, ancak yine de düzeltmek için bulduğumuz en güvenli yol:

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

Düzenlenen

Krom 25.0.1364.160

  1. Frederik Gottlieb - 6.43
  2. Sam Barnum - 4.83
  3. Eli - 3.68
  4. Yeşu 2.91
  5. Mathew Crumley - 2.67
  6. bduran - 2.55
  7. Allen Rice - 2.11
  8. kanguru - 0.68
  9. Tj. Kalabalık - 0.67
  10. zertosh - HATA

Firefox 20.0

  1. Allen Rice - 1.85
  2. Yeşu - 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. Kalabalık - 0.13
  10. zertosh - HATA

En önemli testi (en azından benim için) eksik: Node.js. Chrome karşılaştırmasına yakın olduğundan şüpheleniyorum.


Bu, parmaklarım ve gözlerim için en etkili yoldur. Ancak Chrome için çok yavaş (bu jsperf'e göre.% 99 daha yavaş).
Orwellophile

1
Arkadaşınızın Mac'indeki sorunun aşağıdakilerle ilgili olup olmadığını merak ediyorum: stackoverflow.com/questions/39129200/… ya da belki Array.slice UInt8Array'i işlemiyor ve başlatılmamış belleği sızdırmıyor mu? (bir güvenlik sorunu!).
robocat

@robocat İyi yakala! İyi hatırlıyorsam Node.js 0.6 veya 0.8 kullanıyorduk. Bir tür sızıntıyı düşündük, ancak üretim yığını ile çoğaltamadık, bu yüzden görmezden gelmeye karar verdik.
durum

10

Lodash veya alt çizgi kullanma

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

Veya mevcut bir diziniz varsa ve aynı uzunlukta bir dizi istiyorsanız

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

Alt çizgiyi kullandığım için bu yanıtı eklediğiniz için çok mutluyum ve bunun için bir şey olduğunu biliyordum ... ama henüz bulamadım. Keşke bunu kullanarak nesnelerin dizilerini oluşturabilseydim
PandaWood

@PandaWood _.range (0, uzunluk -1, 0) .map (Object.new), sanırım.
djechlin

Olmalı _.range(0, length, 0), inanıyorum. Lodash son değeri hariç
user4815162342

9

ES6 çözümü:

[...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0]

8

ECMAScript2016 itibarıyla büyük diziler için açık bir seçenek vardır.

Bu yanıt Google aramalarında hala en üstte göründüğünden, 2017 için bir cevap.

İşte bu soruya şimdiye kadar teklif edilenler de dahil olmak üzere birkaç düzine popüler yöntemle mevcut bir jsbench . Daha iyi bir yöntem bulursanız lütfen ekleyin, çatallayın ve paylaşın.

Ben rastgele uzunluk sıfır dolu dizi oluşturmak için gerçek en verimli yolu olmadığını not etmek istiyorum. Hız veya netlik ve sürdürülebilirlik için optimize edebilirsiniz - ya projenin ihtiyaçlarına bağlı olarak daha verimli bir seçim olarak düşünülebilir.

Hız için optimizasyon yaparken aşağıdakileri yapmak istersiniz: değişmez sözdizimini kullanarak dizi oluşturmak; uzunluğu ayarlayın, yineleme değişkenini başlatın ve bir while döngüsü kullanarak dizi boyunca yineleyin. İşte bir örnek.

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

Başka bir olası uygulama:

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

Ancak, daha az net olduğu ve dizi değişkeniniz üzerinde blok kapsamını korumanıza izin vermediğinden, bu ikinci implantasyonu pratikte kullanmayı kesinlikle tavsiye etmiyorum.

Bunlar bir for döngüsü ile doldurmaktan önemli ölçüde daha hızlıdır ve standart yöntemden yaklaşık% 90 daha hızlıdır.

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

Ancak bu doldurma yöntemi, açıklığı, kısalığı ve sürdürülebilirliği nedeniyle daha küçük diziler için hala en verimli seçimdir. Binlerce veya daha fazla uzunlukta uzunlukları olan çok sayıda dizi yapmadığınız sürece performans farkı muhtemelen sizi öldürmez.

Birkaç önemli not daha. Çoğu stil kılavuzu var, ES6 veya daha yenisini kullanırken artık çok özel bir neden olmadan kullanmamanızı önerir. constYeniden tanımlanamayan letdeğişkenler ve değişkenler için kullanın . MDN ve Airbnb en Stil Kılavuzu en iyi uygulamalar hakkında daha fazla bilgi için gitmek için harika yerlerdir. Sorular sözdizimi ile ilgili değildi, ancak JS'de yeni insanların bu eski ve yeni cevaplar arasında arama yaparken bu yeni standartları bilmesi önemlidir.


8

Yepyeni bir Dizi oluşturmak için

new Array(arrayLength).fill(0);

Mevcut bir Dizinin sonuna bazı değerler eklemek için

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

Misal

//**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

Cevaplarda bu yöntemi görmedim, işte burada:

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

Sonuç olarak, sıfır değerli uzunluk 200 dizisini alırsınız:

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

Bu kodun performansı hakkında emin değilim, ancak nispeten küçük diziler için kullanırsanız bir sorun olmamalıdır.


5
Ne en hızlı ne de en kısa ama çözüm çeşitliliğine güzel bir katkı.
7vujy0f0hy


4

Bu concatsürüm, Chrome'daki (2013-03-21) testlerimde çok daha hızlı. 10.000.000 eleman için 200ms ve düz init için 675.

function filledArray(len, value) {
    if (len <= 0) return [];
    var result = [value];
    while (result.length < len/2) {
        result = result.concat(result);
    }
    return result.concat(result.slice(0, len-result.length));
}

Bonus: Dizinizi Dizeler ile doldurmak istiyorsanız, bunu yapmanın kısa bir yoludur ( concatsanki o kadar hızlı değil ):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}

2
Tamam, vahşi. Bu, yeni Array (len) kullanmaktan daha hızlıdır. FAKAT! Chrome'da, bu verilere sonraki okumaların önemli ölçüde daha uzun sürdüğünü görüyorum. Ne demek istediğimi göstermek için bazı zaman damgaları: (Yeni Dizi (len) kullanma) 0.365: Dizi 4.526: Konvolüsyon Yürütme 10.75: Konvolüsyon Tamamlandı (concat kullanma) 0.339: Dizi 0.591 Yapma: Konvolüsyon // OMG, WAY daha hızlı 18.056: Evrişim Tamamlandı
Brooks

4

TJ Crowder'ın büyük cevabını test ediyordum ve Chrome'daki testlerinde herhangi bir performanstan daha iyi performans gösteren concat çözümüne dayanan yinelemeli birleştirme ile geldim (diğer tarayıcıları test etmedim).

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

yöntemi ile çağırın makeRec(29).



4

ECMAScript 6 (Harmony) teklifinin birArray.prototype.fill parçası olarak eklenmiş olduğunu belirtmek gerekir . Ben iplik üzerinde belirtilen diğer seçenekleri düşünmeden önce, aşağıda yazılı çok dolgu ile gitmek istiyorum.

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

Döngü kodu için en kısa

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;

Güvenli değişken sürümü

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

edit:
for(var i=100,a=[];i--;)a[i]=0;

2
Uzunluğun tanımlanmış bir değişken olduğu göz önüne alındığında n, bu daha kısa olacaktır:for(var a=[];n--;a[n]=0);
Tomas Langkaas


3

En hızlı fonksiyonum:

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

Diziye öğe eklemek için yerel itme ve kaydırma özelliğini kullanmak, dizi kapsamını bildirmekten ve değerini ayarlamak için her öğeye başvurmaktan çok daha hızlıdır (yaklaşık 10 kez).

fyi: Firebug'da (firefox uzantısı) çalıştırırken geri sayan ilk döngü ile sürekli daha hızlı zamanlar alıyorum.

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'ın bundan ne yaptığını bilmek isterim? :-)


Bunu değiştirerek daha hızlı yapabilirsiniz while (len--).. işlem sürelerimi yaklaşık 60ms'den 54ms'ye çıkardı
nickf

Matthew Crumbly'nin cevabı hala bunu aşıyor (30ms)!
nickf

3

Bu prototipin bir yerlerde olduğunu biliyordum :)

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

Düzenleme: testler

Joshua ve diğer yöntemlere yanıt olarak kendi kıyaslamamı yaptım ve bildirilenlerden tamamen farklı sonuçlar görüyorum.

İşte test ettiklerim:

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

Sonuçlar:

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

Yani benim hesaplaşma itme ile gerçekten genel olarak daha yavaş ama FF daha uzun diziler ile daha iyi performans ama IE genel olarak sadece kötü berbat (quel sürpriz).


Bunu test ettim: İkinci yöntem ( b = []...) birinciden % 10-15 daha hızlı, ancak Joshua'nın cevabından 10 kat daha yavaş.
nickf

Bunun eski bir yazı olduğunu biliyorum . Ama belki de (benim gibi) başkaları için hala ilgi çekicidir. Bu nedenle prototip işlevine bir ad önermek istiyorum: -check'ten else {this.length=n;}sonra this.length. Bu, gerektiğinde mevcut bir diziyi initfarklı bir uzunluğa yeniden ayarlarken kısaltacaktır n.
cars10m

2

Anonim işlev:

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

For-loop ile biraz daha kısa:

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

Herhangi biriyle çalışır Object, sadece içindekileri değiştirin this.push().

Hatta işlevi kaydedebilirsiniz:

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

Şunu kullanarak arayın:

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

Mevcut bir diziye eleman ekleme:

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

Performans: http://jsperf.com/zero-filled-array-creation/25

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.