Menghapus elemen dengan Array.map di JavaScript

90

Saya ingin memfilter berbagai item dengan menggunakan map()fungsi tersebut. Berikut ini potongan kode:

var filteredItems = items.map(function(item)
{
    if( ...some condition... )
    {
        return item;
    }
});

Masalahnya adalah bahwa item yang disaring masih menggunakan ruang dalam array dan saya ingin menghapusnya sepenuhnya.

Ada ide?

EDIT: Terima kasih, saya lupa filter(), yang saya inginkan sebenarnya adalah filter()a map().

EDIT2: Terima kasih telah menunjukkannya map()dan filter()tidak diterapkan di semua browser, meskipun kode khusus saya tidak dimaksudkan untuk dijalankan di browser.

Vincent Robert
sumber
Bisakah Anda menjelaskan mengapa 2 iterasi adalah yang terburuk dari 1? Maksud saya, 2 * O (n) setara dengan O (2 * n) bagi saya ...
Vincent Robert

Jawaban:

105

Anda harus menggunakan filtermetode daripada peta kecuali Anda ingin mengubah item dalam larik, selain pemfilteran.

misalnya.

var filteredItems = items.filter(function(item)
{
    return ...some condition...;
});

[Sunting: Tentu saja Anda selalu dapat melakukan sourceArray.filter(...).map(...)untuk memfilter dan bermutasi]

olliej.dll
sumber
3
maptidak bermutasi
Terima kasih
15
Tapi Anda bisa bermutasi map.
Crazywako
Hati-hati dengan ini: saat JS meneruskan referensi ketika Anda memutasi sesuatu dengan peta, itu akan mengubah objek tetapi sebagai MDN berdiri, peta mengembalikan array yang dimutasi.
alexOtano
1
Pertanyaannya tidak menanyakan bagaimana menyaring, pertanyaan menanyakan bagaimana menghapus pada peta
Dazzle
1
@alexOtano Tidak, map tidak bermutasi, dan tidak mengembalikan larik yang bermutasi. Ini mengembalikan array baru. misalnya,x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
Kyle Baker
40

Terinspirasi dengan menulis jawaban ini, saya akhirnya memperluas dan menulis posting blog yang membahas ini dengan cermat. Saya sarankan untuk memeriksanya jika Anda ingin mengembangkan pemahaman yang lebih dalam tentang bagaimana memikirkan masalah ini - saya mencoba menjelaskannya sepotong demi sepotong, dan juga memberikan perbandingan JSperf di akhir, membahas pertimbangan kecepatan.

Artinya, tl; dr adalah ini: Untuk mencapai apa yang Anda minta (pemfilteran dan pemetaan dalam satu panggilan fungsi), Anda akan menggunakanArray.reduce() .

Namun, pendekatan 2 yang lebih mudah dibaca dan (kurang penting) biasanya secara signifikan lebih cepat 2 adalah dengan menggunakan filter dan peta yang dirangkai bersama:

[1,2,3].filter(num => num > 2).map(num => num * 2)

Berikut ini adalah penjelasan tentang cara Array.reduce()kerja, dan bagaimana hal itu dapat digunakan untuk menyelesaikan filter dan memetakan dalam satu iterasi. Sekali lagi, jika ini terlalu padat, saya sangat menyarankan untuk melihat posting blog yang ditautkan di atas, yang merupakan pengantar yang jauh lebih ramah dengan contoh dan perkembangan yang jelas.


Anda memberikan pengurangan argumen yang merupakan fungsi (biasanya anonim).

Fungsi anonim itu mengambil dua parameter - satu (seperti fungsi anonim yang diteruskan ke map / filter / forEach) adalah iterasi yang akan dioperasikan. Ada argumen lain untuk fungsi anonim yang diteruskan untuk mengurangi, bagaimanapun, bahwa fungsi tersebut tidak menerima, dan itu adalah nilai yang akan diteruskan di antara pemanggilan fungsi, yang sering disebut sebagai memo .

Perhatikan bahwa sementara Array.filter () hanya mengambil satu argumen (fungsi), Array.reduce () juga membutuhkan argumen kedua yang penting (meskipun opsional): nilai awal untuk 'memo' yang akan diteruskan ke fungsi anonim itu sebagai argumen pertama, dan selanjutnya dapat dimutasi dan diteruskan di antara pemanggilan fungsi. (Jika tidak diberikan, maka 'memo' dalam pemanggilan fungsi anonim pertama secara default akan menjadi iterasi pertama, dan argumen 'iteratee' sebenarnya akan menjadi nilai kedua dalam larik)

Dalam kasus kami, kami akan mengirimkan array kosong untuk memulai, dan kemudian memilih apakah akan menyuntikkan iteratee kami ke dalam array kami atau tidak berdasarkan fungsi kami - ini adalah proses pemfilteran.

Akhirnya, kita akan mengembalikan 'array in progress' kita pada setiap pemanggilan fungsi anonim, dan reduce akan mengambil nilai kembali itu dan meneruskannya sebagai argumen (disebut memo) ke pemanggilan fungsi berikutnya.

Hal ini memungkinkan filter dan peta terjadi dalam satu iterasi, mengurangi setengah dari jumlah iterasi yang diperlukan - hanya melakukan pekerjaan dua kali lebih banyak setiap iterasi, jadi tidak ada yang benar-benar disimpan selain panggilan fungsi, yang tidak terlalu mahal dalam javascript .

Untuk penjelasan yang lebih lengkap, lihat dokumen MDN (atau posting saya yang direferensikan di awal jawaban ini).

Contoh dasar panggilan Reduce:

let array = [1,2,3];
const initialMemo = [];

array = array.reduce((memo, iteratee) => {
    // if condition is our filter
    if (iteratee > 1) {
        // what happens inside the filter is the map
        memo.push(iteratee * 2); 
    }

    // this return value will be passed in as the 'memo' argument
    // to the next call of this function, and this function will have
    // every element passed into it at some point.
    return memo; 
}, initialMemo)

console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]

versi yang lebih ringkas:

[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])

Perhatikan bahwa iterasi pertama tidak lebih besar dari satu, sehingga difilter. Perhatikan juga initialMemo, yang diberi nama hanya untuk memperjelas keberadaannya dan menarik perhatian padanya. Sekali lagi, ini diteruskan sebagai 'memo' ke pemanggilan fungsi anonim pertama, dan kemudian nilai yang dikembalikan dari fungsi anonim diteruskan sebagai argumen 'memo' ke fungsi berikutnya.

Contoh lain dari kasus penggunaan klasik untuk memo akan mengembalikan angka terkecil atau terbesar dalam sebuah array. Contoh:

[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.

Contoh bagaimana menulis fungsi reduce Anda sendiri (ini sering membantu memahami fungsi seperti ini, saya temukan):

test_arr = [];

// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
    // if we did not pass in a second argument, then our first memo value 
    // will be whatever is in index zero. (Otherwise, it will 
    // be that second argument.)
    const initialMemoIsIndexZero = arguments.length < 2;

    // here we use that logic to set the memo value accordingly.
    let memo = initialMemoIsIndexZero ? this[0] : initialMemo;

    // here we use that same boolean to decide whether the first
    // value we pass in as iteratee is either the first or second
    // element
    const initialIteratee = initialMemoIsIndexZero ? 1 : 0;

    for (var i = initialIteratee; i < this.length; i++) {
        // memo is either the argument passed in above, or the 
        // first item in the list. initialIteratee is either the
        // first item in the list, or the second item in the list.
           memo = reduceFunc(memo, this[i]);
        // or, more technically complete, give access to base array
        // and index to the reducer as well:
        // memo = reduceFunc(memo, this[i], i, this);
    }

    // after we've compressed the array into a single value,
    // we return it.
    return memo;
}

Implementasi sebenarnya memungkinkan akses ke hal-hal seperti indeks, misalnya, tetapi saya harap ini membantu Anda mendapatkan gambaran yang tidak rumit tentang intinya.

Kyle Baker
sumber
2
cemerlang! Saya ingin melakukan hal seperti ini selama bertahun-tahun. Memutuskan untuk mencoba dan mencari cara yang bagus dan cara dan wow, javascript alami!
jemiloii
Kegunaan lain dari reduceadalah, tidak seperti filter+ map, callback bisa melewati argumen indeks yang merupakan indeks dari larik asli, dan bukan yang difilter.
congusbongus
@KyleBaker Link ke posting blog Anda menuju ke halaman-tidak-ditemukan. Bisakah Anda memperbarui tautan? Terima kasih!
Tim Philip
10

Bukan itu yang dilakukan peta. Anda benar-benar menginginkan Array.filter . Atau jika Anda benar-benar ingin menghapus elemen dari daftar asli, Anda harus melakukannya secara imperatif dengan loop for.

Patrick
sumber
6

Metode Array Filter

var arr = [1, 2, 3]

// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })

// ES2015 syntax
arr = arr.filter(item => item != 3)

console.log( arr )

vsync
sumber
1
Anda juga dapat melakukannyavar arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
jack blank
Anda kembali 4 tahun kemudian untuk menambahkan teks besar? minus satu
Terima kasih
@ user633183 Siapa yang Anda maksud? apa "teks besar"? Komentar Anda tidak jelas. Anda yakin mengomentari tempat yang benar ...?
vsync
2

Namun Anda harus mencatat bahwa Array.filtertidak didukung di semua browser jadi, Anda harus membuat prototipe:

//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license

if (!Array.prototype.filter)
{
    Array.prototype.filter = function(fun /*, thisp*/)
    {
        var len = this.length;

        if (typeof fun != "function")
            throw new TypeError();

        var res = new Array();
        var thisp = arguments[1];

        for (var i = 0; i < len; i++)
        {
            if (i in this)
            {
                var val = this[i]; // in case fun mutates this

                if (fun.call(thisp, val, i, this))
                   res.push(val);
            }
        }

        return res;
    };
}

Dan dengan melakukan itu, Anda dapat membuat prototipe metode apa pun yang mungkin Anda perlukan.

ggasp
sumber
2
Jika Anda benar-benar ingin mem-polyfill metode ini, gunakan polyfill yang tepat, atau lebih baik lagi perpustakaan seperti Modernizr . Jika tidak, Anda mungkin akan mengalami bug yang membingungkan dengan browser yang tidak dikenal yang tidak akan Anda sadari sampai terlalu lama diproduksi.
Kyle Baker
0

Pernyataan berikut membersihkan objek menggunakan fungsi peta.

var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);
Nicolas
sumber
0

Saya baru saja menulis persimpangan array yang menangani dengan benar juga duplikat

https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0

// array intersection that correctly handles also duplicates

const intersection = (a1, a2) => {
  const cnt = new Map();
  a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
  return a1.filter(el => el in cnt && 0 < cnt[el]--);
};

const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]

gkucmierz.dll
sumber
0

Pertama, Anda dapat menggunakan peta dan dengan rantai Anda dapat menggunakan filter

state.map(item => {
            if(item.id === action.item.id){   
                    return {
                        id : action.item.id,
                        name : item.name,
                        price: item.price,
                        quantity : item.quantity-1
                    }

            }else{
                return item;
            }
        }).filter(item => {
            if(item.quantity <= 0){
                return false;
            }else{
                return true;
            }
        });
Rishab
sumber