Cara paling efisien untuk membuat array JavaScript yang diisi nol?

602

Apa cara paling efisien untuk membuat array panjang nol yang diisi secara sewenang-wenang dalam JavaScript?

dil
sumber
7
Beberapa data aktual tentang ini: jsperf.com/zeroarrayjs
Web_Designer
7
Isi ES6 memungkinkan untuk melakukan ini secara asli.
Salvador Dali
1
arr = array baru (panjang + 1) .joint (karakter) .split ('');
Jordan Stefanelli
4
UPDATE 2016 : Tolok ukur ubahsuaian lain di sini: jsfiddle.net/basickarl/md5z0Lqq
K - Keracunan dalam SO semakin meningkat.
1
let i = 0; Array.from(Array(10), ()=>i++);
Bart Hoekstra

Jawaban:

543

ES6 memperkenalkan Array.prototype.fill. Dapat digunakan seperti ini:

new Array(len).fill(0);

Tidak yakin apakah itu cepat, tapi saya suka karena pendek dan menggambarkan diri sendiri.

Itu masih tidak di IE ( periksa kompatibilitas ), tetapi ada polyfill yang tersedia .

Oriol
sumber
15
isi cepat. new Array(len)sangat lambat. (arr = []).length = len; arr.fill(0);adalah tentang solusi tercepat ive terlihat di mana saja ... atau setidaknya terikat
Pimp Trizkit
7
@PimpTrizkit arr = Array(n)dan (arr = []).length = nberperilaku sesuai dengan spesifikasi. Dalam beberapa implementasi seseorang bisa lebih cepat, tetapi saya tidak berpikir ada perbedaan besar.
Oriol
2
Yah, saya mulai mengujinya dengan array multi dimensi dan sepertinya sangat mempercepat kasus pengujian saya. Baru saja melakukan beberapa pengujian pada FF41 dan Chrome45.0.2454.99 m. Ya, saya kira saya benar-benar membutuhkan lebih banyak ruang untuk menjelaskan diri saya sendiri. Sebagian besar pengujian saya bias saya akui. Tapi, lihat ini. Tentukan ulang var dan gunakan hanya garis ini (arr = []).length = 1000; terhadap arr = new Array(1000);kecepatan, ujilah di Chrome dan FF ... prosesnya newsangat lambat. Sekarang, untuk panjang array yang lebih kecil .. katakan <50 atau ada sekitar ... maka new Array()tampaknya berkinerja lebih baik. Tapi ..
Pimp Trizkit
4
... Saya akui saya melewatkan bagian ini ... ketika saya menambahkan baris kedua ke pengujian ... arr.fill(0) ... semuanya berubah. Sekarang, menggunakan new Array()lebih cepat dalam banyak kasus kecuali ketika Anda mendapatkan ukuran array> 100000 ... Kemudian Anda dapat mulai melihat kecepatan meningkat lagi. Tetapi jika Anda tidak benar-benar harus mengisinya dengan nol dan dapat menggunakan falisy standar array kosong. Maka (arr = []).length = xgila cepat dalam kasus pengujian saya sebagian besar waktu.
Pimp Trizkit
4
Perhatikan bahwa untuk beralih di atas array (misalnya peta atau forEach) nilai harus ditetapkan , jika tidak akan melewati indeks tersebut. Nilai-nilai yang Anda atur dapat berupa apa pun yang Anda inginkan - bahkan tidak terdefinisi. Contoh: try new Array(5).forEach(val => console.log('hi'));vs new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo
387

Meskipun ini adalah utas lama, saya ingin menambahkan 2 sen ke dalamnya. Tidak yakin seberapa lambat / cepatnya ini, tapi ini adalah kapal cepat. Inilah yang saya lakukan:

Jika saya ingin mengisi sebelumnya dengan nomor:

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

Jika saya ingin mengisi sebelumnya dengan string:

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

Jawaban lain telah menyarankan:

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

tetapi jika Anda ingin 0 (angka) dan bukan "0" (nol di dalam string), Anda dapat melakukan:

new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]
zertosh
sumber
6
Jawaban bagus! Bisakah Anda jelaskan triknya Array.apply(null, new Array(5)).map(...)? Karena cukup melakukan (new Array (5)). Map (...) tidak akan berfungsi seperti yang diceritakan oleh spec
Dmitry Pashkevich
36
(btw, kami tidak benar-benar membutuhkannya new) Saat Anda melakukannya, Array(5)Anda membuat objek yang terlihat seperti ini: { length: 5, __proto__: Array.prototype }- coba console.dir( Array(5) ). Perhatikan bahwa itu tidak memiliki sifat setiap 0, 1, 2, dll Tapi ketika Anda applybahwa kepada Arraykonstruktor, itu seperti mengatakan Array(undefined, undefined, undefined, undefined, undefined). Dan Anda mendapatkan objek yang agak mirip { length: 5, 0: undefined, 1: undefined...}. mapbekerja pada properti 0,, 1dll. itulah sebabnya contoh Anda tidak berfungsi, tetapi ketika Anda menggunakannya applytidak.
zertosh
4
Parameter pertama untuk .applysebenarnya adalah yang Anda inginkan this. Untuk tujuan thisini, tidak masalah - kami hanya benar-benar peduli tentang penyebaran "fitur" parameter .apply- sehingga dapat berupa nilai apa pun. Saya suka nullkarena murah, Anda mungkin tidak ingin menggunakan {}atau []karena Anda akan membuat objek tanpa alasan.
zertosh
2
Juga menginisialisasi dengan ukuran + menetapkan jauh lebih cepat daripada push. Lihat test case jsperf.com/zero-fill-2d-array
Colin
2
bagaimana dengan Array.apply (null, Array (5)). map (x => 0)? Ini sedikit lebih pendek!
Arch Linux Tux
97

Cara yang elegan untuk mengisi array dengan nilai yang dikomputasi

Berikut ini cara lain untuk melakukannya menggunakan ES6 yang sejauh ini belum ada yang disebutkan:

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

Ini berfungsi dengan melewatkan fungsi peta sebagai parameter kedua Array.from.

Pada contoh di atas, parameter pertama mengalokasikan array 3 posisi yang diisi dengan nilai undefineddan kemudian fungsi lambda memetakan masing-masing dari mereka ke nilai 0.

Meskipun Array(len).fill(0)lebih pendek, itu tidak berfungsi jika Anda perlu mengisi array dengan melakukan perhitungan terlebih dahulu (saya tahu pertanyaannya tidak menanyakannya, tetapi banyak orang akhirnya mencari di sini) .

Misalnya, jika Anda memerlukan array dengan 10 angka acak:

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

Ini lebih ringkas (dan elegan) daripada yang setara:

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

Metode ini juga dapat digunakan untuk menghasilkan urutan angka dengan memanfaatkan parameter indeks yang disediakan dalam panggilan balik:

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

Jawaban bonus: isi array menggunakan String repeat()

Karena jawaban ini mendapat banyak perhatian, saya juga ingin menunjukkan trik keren ini. Meskipun tidak berguna sebagai jawaban utama saya, akan memperkenalkan repeat()metode String masih tidak begitu dikenal, tetapi sangat berguna . Inilah triknya:

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

Keren ya repeat()adalah metode yang sangat berguna untuk membuat string yang merupakan pengulangan dari string asli beberapa kali. Setelah itu, split()buat array untuk kita, yang kemudian map()ped ke nilai yang kita inginkan. Hancurkan dalam beberapa langkah:

> "?".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
sumber
Banyak trik salon di pos itu, tapi mudah-mudahan tidak ada yang akan mencapai kode produksi :)
Eric Grange
Meskipun repeattriknya jelas tidak diinginkan dalam produksi, Array.from()baik-baik saja :-)
Lucio Paiva
Tidak juga, Array.from () di sini pada dasarnya membuat array, mengulanginya dengan map (), memanggil fungsi pada setiap item untuk membuat array baru, kemudian membuang array pertama ... Untuk array kecil ini mungkin tidak berbahaya, untuk array yang lebih besar, ini adalah jenis pola yang mengakibatkan orang memanggil browser "memory hogs" :)
Eric Grange
Orang yang berurusan dengan array besar harus tahu lebih baik dari ini, pasti. Namun, untuk aplikasi umum, membuat array aux berukuran biasa (hingga 10r elemen) yang akan segera dibuang tidak masalah (membutuhkan waktu yang sama seperti jika Anda menghindari pembuatan array ekstra - diuji dengan Chrome terbaru). Untuk kasus seperti itu, keterbacaan menjadi lebih penting daripada optimisasi kinerja kecil. Tentang waktu O (n), perlu jika Anda perlu menghitung sesuatu yang berbeda untuk setiap elemen (subjek utama jawaban saya). Diskusi ini sangat menarik, senang Anda mengangkatnya!
Lucio Paiva
88

Pendeknya

Solusi tercepat

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

Solusi terpendek (praktis) (3x lebih lambat untuk array kecil, sedikit lebih lambat untuk besar (paling lambat di Firefox))

Array(n).fill(0)


Detail

Hari ini 2020.06.09 saya melakukan tes pada macOS High Sierra 10.13.6 pada browser Chrome 83.0, Firefox 77.0, dan Safari 13.1. Saya menguji solusi yang dipilih untuk dua kasus uji

  • array kecil - dengan 10 elemen - Anda dapat melakukan tes DI SINI
  • array besar - dengan elemen 1M - Anda dapat melakukan tes DI SINI

Kesimpulan

  • solusi berdasarkan new Array(n)+for(N) adalah solusi tercepat untuk array kecil dan array besar (kecuali Chrome tetapi masih sangat cepat di sana) dan direkomendasikan sebagai solusi lintas-browser yang cepat
  • solusi berdasarkan new Float32Array(n)(I) mengembalikan array non-tipikal (mis. Anda tidak dapat memanggilnya push(..)) jadi saya tidak membandingkan hasilnya dengan solusi lain - namun solusi ini sekitar 10-20x lebih cepat daripada solusi lain untuk array besar di semua browser
  • solusi berdasarkan for(L, M, N, O) cepat untuk array kecil
  • solusi berdasarkan fill(B, C) cepat di Chrome dan Safari tetapi mengejutkan paling lambat di Firefox untuk array besar. Mereka sedang cepat untuk array kecil
  • solusi berdasarkan Array.apply(P) melempar kesalahan untuk array besar

masukkan deskripsi gambar di sini

Kode dan contoh

Kode di bawah ini menyajikan solusi yang digunakan dalam pengukuran

Contoh hasil untuk Chrome

masukkan deskripsi gambar di sini

Kamil Kiełczewski
sumber
Baru saja menjalankan beberapa tes di Chrome 77 dan loop sederhana dengan push () dua kali lebih cepat daripada fill () ... Saya ingin tahu apa efek samping halus fill () yang mencegah implementasi yang lebih efisien?
Eric Grange
@EricGrange Saya memperbarui jawaban - di bagian bawah saya memperbarui tautan ke benchamrk dengan proposisi Anda: case P let a=[]; for(i=n;i--;) a.push(0);- tetapi 4x lebih lambat dari fill(0)- jadi saya bahkan tidak akan memperbarui penyihir gambar case itu.
Kamil Kiełczewski
2
Pengukuran yang bagus. Analisis: G lambat karena mengubah ukuran array di setiap iterasi, dan mengubah ukuran berarti melakukan alokasi memori baru. A, B, M cepat karena ukuran dilakukan hanya sekali. +1
Roland
63

Metode pengisian ES 6 yang disebutkan telah menangani hal ini dengan baik. Sebagian besar browser desktop modern sudah mendukung metode prototipe Array yang diperlukan hingga saat ini (Chromium, FF, Edge, dan Safari) [ 1 ]. Anda dapat melihat detail di MDN . Contoh penggunaan sederhana adalah

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

Mengingat dukungan browser saat ini, Anda harus berhati-hati untuk menggunakan ini kecuali Anda yakin audiens Anda menggunakan browser Desktop modern.

Gerald Senarclens de Grancy
sumber
4
Jika Anda mengisi dengan jenis referensi itu akan menjadi referensi yang sama di semua dari mereka. new Array (10) .fill (null) .map (() => []) akan menjadi cara yang ringkas untuk mengatasi ini (awalnya saya terbakar haha)
John Culviner
4
UPDATE 2016 : Metode ini mengeluarkan segala sesuatu yang keluar dari air, klik di sini untuk tolok ukur: jsfiddle.net/basickarl/md5z0Lqq
K - Keracunan dalam SO semakin meningkat.
ini akan berfungsi untuk array. a = Array(10).fill(null).map(() => { return []; });
Andy
2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Phrogz
50

Catatan ditambahkan Agustus 2013, diperbarui Februari 2015: Jawaban di bawah ini dari 2009 berkaitan dengan Arrayjenis generik JavaScript . Itu tidak berhubungan dengan array yang diketik lebih baru yang didefinisikan dalam ES2015 [dan tersedia sekarang di banyak browser], seperti Int32Arraydan itu. Perhatikan juga bahwa ES2015 menambahkan fillmetode untuk kedua array dan array yang diketik , yang mungkin merupakan cara paling efisien untuk mengisinya ...

Juga, ini dapat membuat perbedaan besar pada beberapa implementasi bagaimana Anda membuat array. Mesin V8 Chrome, khususnya, mencoba menggunakan larik memori yang sangat efisien dan bersebelahan jika dianggap bisa, bergeser ke larik berbasis objek hanya bila diperlukan.


Dengan sebagian besar bahasa, ini akan dialokasikan sebelumnya, lalu isi nol, seperti ini:

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

Tapi , array JavaScript tidak benar-benar array , mereka adalah kunci / nilai peta seperti semua objek JavaScript lainnya, jadi tidak ada "pra-alokasi" untuk dilakukan (pengaturan panjang tidak mengalokasikan banyak slot untuk diisi), atau apakah ada alasan untuk percaya bahwa manfaat penghitungan mundur ke nol (yang hanya untuk membuat perbandingan dalam loop cepat) tidak kalah dengan menambahkan kunci dalam urutan terbalik ketika implementasi mungkin telah dioptimalkan penanganannya terhadap kunci. terkait dengan array pada teori Anda biasanya akan melakukannya secara berurutan.

Bahkan, Matthew Crumley menunjukkan bahwa menghitung mundur jauh lebih lambat di Firefox daripada menghitung, hasil yang saya dapat konfirmasi - ini adalah bagian arraynya (pengulangan ke nol masih lebih cepat daripada pengulangan ke batas dalam var). Tampaknya menambahkan elemen ke array dalam urutan terbalik adalah operasi lambat di Firefox. Faktanya, hasilnya sedikit berbeda dengan implementasi JavaScript (yang tidak terlalu mengejutkan). Berikut ini adalah halaman pengujian cepat dan kotor (di bawah) untuk implementasi browser (sangat kotor, tidak menghasilkan selama pengujian, jadi berikan umpan balik minimal dan akan bertabrakan dengan batas waktu skrip). Saya sarankan menyegarkan di antara tes; FF (setidaknya) melambat pada tes berulang jika Anda tidak.

Versi yang cukup rumit yang menggunakan concat Array # lebih cepat daripada init langsung pada FF pada suatu tempat antara 1.000 dan 2.000 array elemen. Di mesin V8 Chrome, meskipun, init langsung menang setiap kali ...

Inilah halaman pengujian ( salinan langsung ):

<!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
sumber
Tidak yakin bahwa pengisian mundur penting di sini, mengingat Anda hanya mengakses elemen (tidak menghapusnya) dan Anda sudah mengalokasikan sebelumnya. Apakah aku salah?
Triptych
titik dari pengisian mundur tidak terutama berkaitan dengan array, ini berkaitan dengan kondisi pelarian untuk sementara waktu - falsey 0 mengakhiri loop dengan sangat efisien
annakata
(Meskipun saya baru saja memperhatikan kode ini tidak benar-benar memanfaatkannya)
annakata
@annakata, Anda tidak dapat menggunakannya di sini, karena 0 adalah indeks yang valid.
Triptych
@ triptych: tidak benar, yang diperlukan hanyalah urutan yang benar - lihat posting saya
annakata
34

Secara default Uint8Array, Uint16Arraydan Uint32Arraykelas menyimpan nol sebagai nilainya, jadi Anda tidak perlu teknik pengisian yang rumit, cukup lakukan:

var ary = new Uint8Array(10);

semua elemen array aryakan menjadi nol secara default.

deadrunk
sumber
5
Ini bagus tapi pikiran surat ini tidak dapat diperlakukan sama seperti array normal, misalnya Array.isArray(ary)adalah false. Panjangnya juga hanya baca sehingga Anda tidak dapat mendorong item baru ke dalamnya denganary.push
MusikAnimal
Fwiw semua array yang diketik tetap 0sebagai nilai default mereka.
jfunk
2
@MikikAnimal, Array.from(new Uint8Array(10))akan menyediakan array normal.
Tomas Langkaas
@TomasLangkaas: Ya, tetapi jawaban lain menunjukkan bahwa itu sekitar 5x lebih lambat daripada Array(n).fill(0)di Chrome jika yang benar-benar Anda butuhkan adalah JS Array. Jika Anda dapat menggunakan TypedArray, ini jauh lebih cepat daripada .fill(0), meskipun, terutama jika Anda dapat menggunakan nilai initializer default sebesar 0. Sepertinya tidak ada konstruktor yang membutuhkan nilai isian dan panjang, seperti yang dimiliki C ++ std::vector. Tampaknya untuk setiap nilai non-nol Anda harus membangun TypedArray yang nol dan kemudian mengisinya. : /
Peter Cordes
29

Jika Anda menggunakan ES6, Anda dapat menggunakan Array.from () seperti ini:

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

Memiliki hasil yang sama dengan

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

Karena

Array.from({ length: 3 })
//[undefined, undefined, undefined]
foxiris
sumber
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']

Perhatikan bahwa whilebiasanya lebih efisien daripada for-in, forEach, dll

Kangax
sumber
3
Bukankah ivariabel lokal itu asing? lengthdilewatkan oleh nilai sehingga Anda harus dapat menurunkannya secara langsung.
Sean Bright
3
Meskipun ini tampak hebat pada awalnya, sayangnya sangat lambat untuk memberikan nilai pada titik arbitrer dalam arary (misalnya arr[i] = value). Jauh lebih cepat untuk mengulang dari awal hingga akhir dan menggunakannya arr.push(value). Ini menjengkelkan, karena saya lebih suka metode Anda.
Nick Brunt
19

menggunakan notasi objek

var x = [];

nol diisi? Suka...

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

diisi dengan 'tidak terdefinisi' ...

var x = new Array(7);

notasi obj dengan nol

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

Sebagai catatan, jika Anda memodifikasi prototipe Array, keduanya

var x = new Array();

dan

var y = [];

akan memiliki modifikasi prototipe tersebut

Bagaimanapun, saya tidak akan terlalu khawatir dengan efisiensi atau kecepatan operasi ini, ada banyak hal lain yang Anda mungkin akan lakukan yang jauh lebih boros dan mahal daripada menginvestasikan array panjang yang mengandung nol.

Allen Rice
sumber
5
Err ... tidak ada nulldalam array ini -var x = new Array(7);
kangax
5
Sebenarnya, array tidak terisi dengan apa pun dengan Array baru (n), bahkan 'undefined's, itu hanya menetapkan nilai panjang array ke n. Anda dapat memeriksa ini dengan memanggil (Array baru (1)). ForEach (...). forEach tidak pernah dieksekusi, tidak seperti jika Anda menyebutnya di [tidak ditentukan].
JussiR
4
new Array(7)tidak tidak membuat sebuah array "diisi dengan undefined". Ini menciptakan array kosong dengan panjang 7.
RobG
1
Anda mungkin ingin mempertimbangkan kembali bagian dari jawaban Anda karena apa yang dikatakan @RobG sangat penting (jika apa yang Anda katakan benar, pemetaan akan jauh lebih mudah)
Abdo
1
Hari-hari ini kamu bisa melakukannya (new Array(10)).fill(0).
Javier de la Rosa
18

Saya telah menguji semua kombinasi pra-alokasi / bukan pra-alokasi, menghitung naik / turun, dan untuk / sementara loop di IE 6/7/8, Firefox 3.5, Chrome, dan Opera.

Fungsi di bawah ini secara konsisten paling cepat atau sangat dekat di Firefox, Chrome, dan IE8, dan tidak jauh lebih lambat daripada yang tercepat di Opera dan IE 6. Ini juga yang paling sederhana dan paling jelas menurut saya. Saya telah menemukan beberapa browser di mana versi loop sementara sedikit lebih cepat, jadi saya memasukkannya juga untuk referensi.

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

atau

function newFilledArray(length, val) {
    var array = [];
    var i = 0;
    while (i < length) {
        array[i++] = val;
    }
    return array;
}
Matthew Crumley
sumber
1
Anda juga bisa membuang var array = []deklarasi ke bagian pertama dari for loop sebenarnya, dipisahkan oleh hanya koma.
damianb
Saya suka saran itu oleh damianb, tapi ingat untuk menempatkan tugas dan koma sebelum penambahan! `for (var i = 0; i <length; array [i] = val, i ++);
punstress
Lakukan apa yang orang lain lewatkan untuk yang kedua, dan atur panjang array ke lengthnilai yang sudah diberikan sehingga tidak terus berubah. Membawa array 1 juta panjang nol dari 40ms ke 8 di mesin saya.
Jonathan Gray
Saya sepertinya mendapatkan peningkatan kecepatan 10-15% ketika saya memperbaiki solusi ini menjadi satu liner. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Lebih sedikit blok? ... bagaimanapun juga ... jika saya mengatur array.lengtharray baru ke panjang .. saya sepertinya mendapatkan peningkatan kecepatan 10% -15% lainnya di FF ... di Chrome, sepertinya menggandakan kecepatan -> var i, array = []; array.length = length; while(i < length) array[i++] = val;(masih lebih cepat jika saya meninggalkannya sebagai sebuah forloop ... tetapi init tidak lagi diperlukan, sehingga whiletampaknya lebih cepat pada versi ini)
Pimp Trizkit
Saya juga akan mencatat bahwa dalam pengujian saya. Dalam jumlah yang layak dari kasus pengujian saya, versi final di atas tampaknya melakukan 3x hingga lebih dari 10x lebih cepat ... Saya tidak begitu yakin mengapa ... (ukuran array yang berbeda diuji antara chrome dan FF)
Pimp Trizkit
13

Jika Anda perlu membuat banyak array yang diisi nol dengan panjang yang berbeda selama eksekusi kode Anda, cara tercepat yang saya temukan untuk mencapainya adalah dengan membuat array nol sekali , menggunakan salah satu metode yang disebutkan pada topik ini, dari panjang yang Anda tahu tidak akan pernah terlampaui, dan kemudian mengiris array yang diperlukan.

Sebagai contoh (menggunakan fungsi dari jawaban yang dipilih di atas untuk menginisialisasi array), buat array panjang nol yang diisi maxLength , sebagai variabel yang terlihat oleh kode yang membutuhkan zero array:

var zero = newFilledArray(maxLength, 0);

Sekarang iris array ini setiap kali Anda membutuhkan array panjang yang diisi nol, dibutuhkan Panjang < maxLength :

zero.slice(0, requiredLength);

Saya membuat nol array yang diisi ribuan kali selama eksekusi kode saya, ini mempercepat proses dengan luar biasa.

Nenad Vukicevic
sumber
13
function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}
Eli
sumber
3
Anda mungkin juga menggunakan new Array(size+1).join("x").split("x").map(function() { return 0; })untuk mendapatkan angka aktual
Yuval
6
@Yuval Atau hanyanew Array(size+1).join('0').split('').map(Number)
Paul
11

Saya tidak menentang:

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

disarankan oleh Zertosh, tetapi dalam ekstensi array ES6 baru memungkinkan Anda untuk melakukan ini dengan fillmetode asli. Sekarang IE edge, Chrome dan FF mendukungnya, tetapi periksa tabel kompatibilitas

new Array(3).fill(0)akan memberimu [0, 0, 0]. Anda dapat mengisi array dengan nilai apa saja seperti new Array(5).fill('abc')(bahkan objek dan array lainnya).

Selain itu, Anda dapat memodifikasi array sebelumnya dengan mengisi:

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

yang memberi Anda: [1, 2, 3, 9, 9, 6]

Salvador Dali
sumber
10

Cara saya biasanya melakukannya (dan sangat cepat) menggunakan Uint8Array. Misalnya, membuat vektor elemen 1M yang diisi nol:

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

Saya adalah pengguna Linux dan selalu bekerja untuk saya, tetapi begitu seorang teman yang menggunakan Mac memiliki beberapa elemen yang tidak nol. Saya pikir mesinnya tidak berfungsi, tetapi masih inilah cara teraman yang kami temukan untuk memperbaikinya:

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

Diedit

Chrome 25.0.1364.160

  1. Frederik Gottlieb - 6.43
  2. Sam Barnum - 4.83
  3. Eli - 3.68
  4. Yosua 2.91
  5. Mathew Crumley - 2.67
  6. bduran - 2,55
  7. Allen Rice - 2.11
  8. kangax - 0,68
  9. Tj. Crowder - 0,67
  10. zertosh - GALAT

Firefox 20.0

  1. Allen Rice - 1.85
  2. Yosua - 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 - GALAT

Hilang ujian paling penting (setidaknya untuk saya): yang Node.js. Saya menduga itu dekat dengan tolok ukur Chrome.

durum
sumber
Ini adalah cara paling efisien untuk jari saya, dan untuk mata saya. Tapi itu sangat sangat lambat untuk Chrome (sesuai dengan jsperf. 99% lebih lambat).
Orwellophile
1
Saya ingin tahu apakah masalah pada Mac teman Anda terkait dengan: stackoverflow.com/questions/39129200/… atau mungkin Array.slice tidak menangani UInt8Array dan membocorkan memori yang tidak diinisialisasi? (masalah keamanan!).
robocat
@robocat Tangkapan bagus! Jika saya mengingatnya dengan baik, kami menggunakan Node.js 0.6 atau 0.8. Kami berpikir tentang semacam kebocoran tetapi kami tidak dapat mereproduksi dengan tumpukan produksi jadi kami memutuskan untuk mengabaikannya.
durum
10

Menggunakan lodash atau garis bawah

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

Atau jika Anda memiliki array yang ada dan Anda ingin array dengan panjang yang sama

array.map(_.constant(0));
Djechlin
sumber
Senang sekali Anda menambahkan jawaban ini, karena saya menggunakan garis bawah, dan saya tahu ada sesuatu untuk ini ... tetapi belum dapat menemukannya. Saya hanya berharap saya bisa membuat array objek menggunakan ini
PandaWood
@PandaWood _.range (0, panjang -1, 0) .map (Object.new), saya pikir.
Djechlin
Seharusnya _.range(0, length, 0), saya percaya. Lodash tidak termasuk nilai akhir
user4815162342
9

Solusi ES6:

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

Pada ECMAScript2016 , ada satu pilihan yang jelas untuk array besar.

Karena jawaban ini masih muncul di dekat bagian atas pencarian Google, inilah jawaban untuk 2017.

Berikut adalah jsbench saat ini dengan beberapa lusin metode populer, termasuk banyak yang diajukan hingga sekarang pada pertanyaan ini. Jika Anda menemukan metode yang lebih baik, tambahkan, garpu, dan bagi.

Saya ingin mencatat bahwa tidak ada cara yang benar-benar efisien untuk membuat panjang nol array diisi sewenang-wenang. Anda dapat mengoptimalkan untuk kecepatan, atau untuk kejelasan dan pemeliharaan - baik dapat dianggap sebagai pilihan yang lebih efisien tergantung pada kebutuhan proyek.

Saat mengoptimalkan kecepatan, Anda ingin: membuat array menggunakan sintaks literal; atur panjangnya, inisialisasi variabel iterasi, dan lakukan iterasi melalui array menggunakan loop sementara. Ini sebuah contoh.

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

Kemungkinan implementasi lain adalah:

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

Tapi saya sangat tidak menyarankan untuk menggunakan implantasi kedua ini karena kurang jelas dan tidak memungkinkan Anda untuk mempertahankan pelingkupan blok pada variabel array Anda.

Ini secara signifikan lebih cepat daripada mengisi dengan for loop, dan sekitar 90% lebih cepat daripada metode standar

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

Tetapi metode pengisian ini masih merupakan pilihan yang paling efisien untuk array yang lebih kecil karena kejelasan, keringkasan dan pemeliharaannya. Perbedaan kinerja kemungkinan tidak akan membunuh Anda kecuali jika Anda membuat banyak array dengan panjang pada urutan ribuan atau lebih.

Beberapa catatan penting lainnya. Sebagian besar panduan gaya merekomendasikan Anda untuk tidak lagi menggunakan vartanpa alasan yang sangat khusus saat menggunakan ES6 atau yang lebih baru. Gunakan constuntuk variabel yang tidak akan didefinisikan ulang dan letuntuk variabel yang akan. The MDN dan Airbnb ini Style Guide adalah tempat yang bagus untuk pergi untuk informasi lebih lanjut tentang praktik terbaik. Pertanyaannya bukan tentang sintaksis, tetapi penting bahwa orang yang baru mengenal JS tahu tentang standar baru ini ketika mencari melalui rim ini dari jawaban lama dan baru.

Isaac B
sumber
8

Untuk membuat Array baru

new Array(arrayLength).fill(0);

Untuk menambahkan beberapa nilai di akhir Array yang ada

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

Contoh

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

Tidak melihat metode ini dalam jawaban, jadi ini dia:

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

Hasilnya, Anda akan mendapatkan array bernilai nol dengan panjang 200:

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

Saya tidak yakin tentang kinerja kode ini, tetapi seharusnya tidak menjadi masalah jika Anda menggunakannya untuk array yang relatif kecil.

Eugene Tiurin
sumber
5
Baik yang tercepat maupun yang terpendek tetapi kontribusi yang bagus untuk keragaman solusi.
7vujy0f0hy
5

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

aflex dykyі
sumber
1
Ini tidak mengisi array dengan nol. Itu mengisinya dengan tidak terdefinisi.
jlh
@ jlh, terima kasih sudah diperbaiki
alex dykyі
4

Ini concatversi jauh lebih cepat dalam tes saya di Chrome (2013/03/21). Sekitar 200ms untuk 10.000.000 elemen vs 675 untuk init langsung.

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: jika Anda ingin mengisi array Anda dengan Strings, ini adalah cara ringkas untuk melakukannya (tidak secepat secepat concat):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}
Sam Barnum
sumber
2
Oke, liar. Itu WAY lebih cepat daripada menggunakan Array baru (len). TAPI! Saya melihat di Chrome bahwa pembacaan selanjutnya untuk data itu memakan waktu lebih lama. Berikut adalah beberapa cap waktu untuk menunjukkan apa yang saya maksud: (Menggunakan Array baru (len)) 0.365: Membuat Array 4.526: Melaksanakan Konvolusi 10.75: Konvolusi Lengkap (Menggunakan concat) 0.339: Membuat Array 0.591: Melaksanakan Konvolusi // OMG, WAY lebih cepat 18.056: Konvolusi Lengkap
Brooks
4

Saya sedang menguji jawaban yang bagus oleh TJ Crowder, dan muncul dengan penggabungan rekursif berdasarkan solusi concat yang mengungguli apa pun dalam tesnya di Chrome (saya tidak menguji browser lain).

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

panggil metode dengan makeRec(29).

Frederik Gottlieb
sumber
4

Bagaimana dengan new Array(51).join('0').split('')?

Cory Mawhorter
sumber
1
lalu .map(function(a){return +a})?
lonewarrior556
4

Mungkin layak untuk ditunjukkan, yang Array.prototype.filltelah ditambahkan sebagai bagian dari proposal ECMAScript 6 (Harmony) . Saya lebih suka menggunakan polyfill yang tertulis di bawah, sebelum mempertimbangkan opsi lain yang disebutkan di utas.

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
sumber
4

Kode terpendek untuk loop

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;

Versi var aman

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

edit:
for(var i=100,a=[];i--;)a[i]=0;
nathnolt
sumber
2
Mengingat bahwa panjangnya adalah variabel yang ditentukan n, maka ini akan menjadi lebih pendek:for(var a=[];n--;a[n]=0);
Tomas Langkaas
4

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

console.log(filled);

Tomiwa Adefokun
sumber
3

Fungsi tercepat saya adalah:

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

Menggunakan push dan shift asli untuk menambahkan item ke array jauh lebih cepat (sekitar 10 kali) daripada mendeklarasikan ruang lingkup array dan mereferensikan setiap item untuk menetapkan nilainya.

fyi: Saya secara konsisten mendapatkan waktu yang lebih cepat dengan loop pertama, yang menghitung mundur, ketika menjalankan ini di pembakar (ekstensi 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

Saya tertarik untuk mengetahui apa yang membuat TJ Crowder tentang itu? :-)

Joshua
sumber
Anda dapat membuatnya lebih cepat dengan mengubahnya ke while (len--).. butuh waktu pemrosesan saya dari sekitar 60 ms menjadi sekitar 54 ms
nickf
Jawaban Matthew Crumbly masih benar-benar mengalahkan ini (30 ms)!
nickf
3

Aku tahu aku punya proto di suatu tempat ini :)

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

Edit: tes

Menanggapi metode Joshua dan lainnya saya menjalankan tolok ukur saya sendiri, dan saya melihat hasil yang sangat berbeda dengan yang dilaporkan.

Inilah yang saya uji:

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

Hasil:

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

Jadi dengan perhitungan saya memang lebih lambat pada umumnya tetapi berkinerja lebih baik dengan array yang lebih panjang di FF tetapi lebih buruk di IE yang hanya menyebalkan secara umum (quel surprise).

annakata
sumber
Saya baru saja menguji ini: metode kedua ( b = []...) adalah 10-15% lebih cepat dari yang pertama, tetapi lebih dari 10 kali lebih lambat dari jawaban Joshua.
nickf
Saya tahu ini adalah pos kuno . Tapi mungkin itu masih menarik bagi orang lain (seperti saya). Oleh karena itu saya ingin menyarankan adition ke fungsi prototipe: sertakan else {this.length=n;}setelah this.lengthcentang. Ini akan memperpendek array yang sudah ada jika perlu ketika initmemanggil ulang ke panjang yang berbeda n.
cars10m
2

Fungsi anonim:

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

Sedikit lebih pendek dengan for-loop:

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

Bekerja dengan apa pun Object, ubah saja apa yang ada di dalamnya this.push().

Anda bahkan dapat menyimpan fungsi:

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

Sebut menggunakan:

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

Menambahkan elemen ke array yang sudah ada:

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

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

Mateo Gianolio
sumber