JavaScript'te Array.map ile öğeleri kaldırma


97

map()İşlevi kullanarak bir dizi öğeyi filtrelemek istiyorum . İşte bir kod parçacığı:

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

Sorun şu ki, filtrelenmiş öğeler dizide hala alan kullanıyor ve onları tamamen silmek istiyorum.

Herhangi bir fikir?

DÜZENLEME: Teşekkürler, unuttum filter(), istediğim şey aslında a filter()sonra a map().

DÜZENLEME2: Özel kodumun bir tarayıcıda çalıştırılması amaçlanmasa da, bunu işaret ettiğiniz map()ve filter()tüm tarayıcılarda uygulanmadığını belirttiğiniz için teşekkür ederiz .


2 yinelemenin neden 1'den daha kötü olduğunu açıklayabilir misiniz? Demek istediğim, 2 * O (n) benim için O (2 * n) ile eşdeğerdir ...
Vincent Robert

Yanıtlar:


109

filterFiltrelemeye ek olarak dizideki öğeleri değiştirmek istemediğiniz sürece eşleme yerine yöntemi kullanmalısınız .

Örneğin.

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

[Düzenleme: Elbette her zaman sourceArray.filter(...).map(...)hem filtrelemek hem de değiştirmek için yapabilirsiniz]


3
mapmutasyona uğramaz
Teşekkürler

16
Ama içinde mutasyona uğrayabilirsin map.
Crazywako

Şuna dikkat edin: JS, haritayla bir şeyi değiştirdiğinizde referansı geçerken nesneyi değiştirir, ancak MDN olduğu gibi, haritalar mutasyona uğramış diziyi döndürür.
alexOtano

2
Soru nasıl filtreleme yapılacağını sormadı, soru haritadan nasıl silineceğini sordu
Dazzle

1
@alexOtano Hayır, harita değişmez ve değiştirilmiş bir dizi döndürmez. Yeni bir dizi döndürür. örneğin,x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
Kyle Baker

43

Bu cevabı yazarak esinlenerek, daha sonra bunu ayrıntılı bir şekilde ele alan bir blog yazısı yazdım ve genişlettim. Bu sorun hakkında daha derin bir anlayış geliştirmek istiyorsanız, bunu kontrol etmenizi öneririm - bunu parça parça açıklamaya çalışıyorum ve sonunda hız hususlarının üzerinden geçerek bir JSperf karşılaştırması yapıyorum.

Bununla birlikte, tl; dr şudur: İstediğiniz şeyi başarmak için (bir işlev çağrısı içinde filtreleme ve haritalama) kullanırsınızArray.reduce() .

Bununla birlikte, daha okunabilir ve (daha az önemli olarak) genellikle önemli ölçüde daha hızlı 2 yaklaşım, yalnızca filtre ve harita zincirinin birlikte kullanılmasıdır:

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

Aşağıda, nasıl Array.reduce()çalıştığı ve tek bir yinelemede filtreleme ve eşleme yapmak için nasıl kullanılabileceği açıklanmaktadır. Yine, eğer bu çok yoğunsa, yukarıda bağlantılı blog gönderisini görmenizi şiddetle tavsiye ederim, bu açık örnekler ve ilerlemeyle çok daha dostane bir giriş.


İndirgeme (genellikle anonim) bir işlev olan bir argüman verirsiniz.

Bu anonim işlev iki parametre alır - biri (map / filter / forEach'e aktarılan anonim işlevler gibi) üzerinde çalıştırılacak yineleme. Ancak, bu işlevlerin kabul etmediği ve genellikle not olarak adlandırılan işlev çağrıları arasında iletilecek değerin azaltılması için geçirilen anonim işlev için başka bir argüman daha vardır .

Array.filter () yalnızca bir bağımsız değişken (bir işlev) alırken, Array.reduce () öğesinin önemli (isteğe bağlı olsa da) ikinci bir bağımsız değişken aldığını unutmayın: 'memo' için, onun olarak ilk argüman ve daha sonra mutasyona uğratılabilir ve işlev çağrıları arasında aktarılabilir. (Sağlanmazsa, ilk anonim işlev çağrısındaki 'not' varsayılan olarak ilk yineleme olur ve 'yineleme' argümanı aslında dizideki ikinci değer olur)

Bizim durumumuzda, başlamak için boş bir dizi geçireceğiz ve ardından yinelememizi dizimize enjekte edip etmemeyi fonksiyonumuza göre seçeceğiz - bu filtreleme işlemidir.

Son olarak, her anonim işlev çağrısında 'devam eden dizimizi' döndüreceğiz ve azaltma bu dönüş değerini alacak ve bir sonraki işlev çağrısına bir argüman (memo olarak adlandırılır) olarak aktaracaktır.

Bu, filtre ve eşlemenin tek bir yinelemede gerçekleşmesine izin vererek gerekli yineleme sayımızı yarıya indirir - her yinelemede yalnızca iki kat daha fazla iş yapar, bu nedenle javascript'te çok pahalı olmayan işlev çağrıları dışında hiçbir şey gerçekten kaydedilmez .

Daha eksiksiz bir açıklama için MDN belgelerine (veya bu cevabın başında atıfta bulunulan gönderime) bakın.

Azalt çağrısının temel örneği:

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

daha kısa ve öz sürüm:

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

İlk yinelemenin birden büyük olmadığına ve dolayısıyla filtrelendiğine dikkat edin. Ayrıca, varlığını netleştirmek ve dikkat çekmek için adlandırılan initialMemo'ya da dikkat edin. Bir kez daha, ilk anonim işlev çağrısına "not" olarak aktarılır ve ardından anonim işlevin döndürülen değeri, sonraki işleve "not" argümanı olarak aktarılır.

Not için klasik kullanım durumunun başka bir örneği, bir dizideki en küçük veya en büyük sayıyı döndürmektir. Misal:

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

Kendi azaltma işlevinizi nasıl yazacağınıza dair bir örnek (bu genellikle bunun gibi işlevleri anlamaya yardımcı olur, buluyorum):

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

Gerçek uygulama, örneğin dizin gibi şeylere erişime izin verir, ancak umarım bu, ana fikri için karmaşık olmayan bir his edinmenize yardımcı olur.


2
parlak! Yıllardır böyle bir şey yapmak istiyordum. Güzel bir yol ve vay, doğal bir javascript bulmaya karar verdim!
jemiloii

Diğer bir faydası, + 'nın reduceaksine , geri aramanın filtrelenmiş dizinin değil, orijinal dizinin indeksi olan bir dizin bağımsız değişkeni geçirilebilmesidir. filtermap
congusbongus

@KyleBaker Blog gönderinizin bağlantısı bir sayfa bulunamadı. Lütfen bağlantıyı günceller misiniz? Teşekkürler!
Tim Philip

10

Haritanın yaptığı bu değil. Gerçekten istiyorsun Array.filter. Ya da öğeleri orijinal listeden gerçekten kaldırmak istiyorsanız, bunu zorunlu olarak bir for döngüsü ile yapmanız gerekecektir.


7

Dizi Filtresi yöntemi

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 )


1
şunları da yapabilirsinizvar arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
jack blank

4 yıl sonra büyük bir metin eklemek için mi geldin? eksi bir
Teşekkür ederim

@ user633183 Kimi kastediyorsunuz? ne "büyük metin"? Yorumunuz net değil. Doğru yere yorum yaptığınızdan emin misiniz ...?
vsync

2

Bununla birlikte, Array.filtertüm tarayıcılarda desteklenmediğine dikkat etmelisiniz, bu nedenle prototip oluşturmanız gerekir:

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

Ve bunu yaparak, ihtiyacınız olabilecek herhangi bir yöntemi prototipleyebilirsiniz.


2
Bu yöntemi gerçekten çoklu doldurmayı düşünüyorsanız, lütfen uygun bir çoklu dolgu veya daha iyisi Modernizr gibi bir kitaplık kullanın . Aksi takdirde, çok uzun süredir üretimde olana kadar fark etmeyeceğiniz belirsiz tarayıcılarla kafa karıştırıcı hatalarla karşılaşmanız muhtemeldir.
Kyle Baker

0

Aşağıdaki ifade, harita işlevini kullanarak nesneyi temizler.

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

0

Yinelenenleri de doğru şekilde işleyen dizi kesişimini yazdım

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


0

Önce haritayı kullanabilir ve zincirleme ile filtre kullanabilirsiniz

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