JavaScript: Nesneler için filtre ()


188

ECMAScript 5 türler için filter()prototipe sahiptir Array, ancak Objectdoğru anlarsam türler için değil .

JavaScript'te filter()for Objectfor'ları nasıl uygulayabilirim ?

Diyelim ki bu nesneye sahibim:

var foo = {
    bar: "Yes"
};

Ve ben s filter()üzerinde çalışan bir yazmak istiyorum Object:

Object.prototype.filter = function(predicate) {
    var result = {};

    for (key in this) {
        if (this.hasOwnProperty(key) && !predicate(this[key])) {
            result[key] = this[key];
        }
    }

    return result;
};

Bu, aşağıdaki demoda kullandığımda çalışır, ancak jQuery 1.5 ve jQuery UI 1.8.9 kullanan siteme eklediğimde, FireBug'da JavaScript hataları alıyorum.


Özellikle hangi hataları alıyorsunuz?
NT3RP

Aldığınız hatalar nelerdir? Mümkünse gönderin :)
Zack The Human

JQuery ve genişletilmiş komut dosyaları içeren biraz belirsiz tarih var Object.prototype: bugs.jquery.com/ticket/2721
Crescent Fresh

tam olarak ihtiyacım olan şey, bunun dışında "!" yükleminde (this [key]) gerçek filtre yöntemine sahip olmak.
NoxFly

Yanıtlar:


201

Asla uzatma Object.prototype.

Kodunuza korkunç şeyler olacak. İşler kırılacak. Nesne hazır bilgileri de dahil olmak üzere tüm nesne türlerini genişletiyorsunuz .

İşte deneyebileceğiniz hızlı bir örnek:

    // Extend Object.prototype
Object.prototype.extended = "I'm everywhere!";

    // See the result
alert( {}.extended );          // "I'm everywhere!"
alert( [].extended );          // "I'm everywhere!"
alert( new Date().extended );  // "I'm everywhere!"
alert( 3..extended );          // "I'm everywhere!"
alert( true.extended );        // "I'm everywhere!"
alert( "here?".extended );     // "I'm everywhere!"

Bunun yerine nesneyi ilettiğiniz bir işlev oluşturun.

Object.filter = function( obj, predicate) {
    let result = {}, key;

    for (key in obj) {
        if (obj.hasOwnProperty(key) && !predicate(obj[key])) {
            result[key] = obj[key];
        }
    }

    return result;
};

61
@patrick: bir adama ekmek verin ve onu bir günlüğüne besleyin, ona nasıl pişirileceğini öğretin ve bir ömür boyu onu besleyeceksiniz (ya da bir şeyim, Danimarkalı, doğru İngilizce sözleri bilmiyorum ;)
Martin Jespersen

13
Yanlış yapıyorsun ...! Yüklem (obj [anahtar]) yüklem (obj [anahtar]) olmalı
pyrotechnick

6
@pyrotechnick: Hayır. Birincisi, cevabın ana noktası uzatmak Object.prototypedeğil, sadece fonksiyonu yerleştirmektir Object. İkincisi, bu OP'nin kodu . Açıkçası OP'nin amacı olumlu sonuçları filtreleyecek.filter() şekilde olmaktır . Başka bir deyişle, pozitif bir dönüş değerinin sonuçtan hariç tutulduğu anlamına gelen negatif bir filtredir . JsFiddle örneğine bakarsanız, varolan özellikleri filtreliyor . undefined
user113716

4
@patrick dw: Hayır. İlk olarak, genişleyen / genişletmeyen prototiplerden bahsetmedim. İkinci olarak developer.mozilla.org/tr/JavaScript/Reference/Global_Objects/… - "Sağlanan işlev tarafından uygulanan testi geçen tüm öğelerle yeni bir dizi oluşturur." Tam tersi bir küresel üzerine uygulamak oldukça saçma görünüyor, değil mi?
pyrotechnick

8
@pyrotechnick: Bu doğru, genişleyen / genişletmeyen prototiplerden bahsetmediniz ve bu benim amacım. Yanlış yaptığımı söyledin, ama yaptığım tek "o" OP'ye uzatmamasını söylüyor Object.prototype. "Bu çalışıyor ..., ancak siteme eklediğimde ... JavaScript hataları alıyorum" sorusundan OP .filter()tam tersi davranışı ile bir uygulama yapmaya karar verirse Array.prototpe.filter, bu ona bağlıdır. Eğer kod yanlış olduğunu OP haber verip sorunun altındaki Yorum bırakmak, ama bu söyleme lütfen ben değilim benim kod olmadığı zaman yanlış yapıyor.
user113716

284

Her şeyden önce, uzatmak kötü bir uygulama olarak kabul edilirObject.prototype . Bunun yerine, üzerinde yarar fonksiyonu olarak sizin özelliği sağlamak Objectzaten var tıpkı Object.keys, Object.assign, Object.is, ... vb.

Burada çeşitli çözümler sunuyorum:

  1. kullanılması reduceveObject.keys
  2. (1) 'e göre Object.assign
  3. mapSözdizimi kullanmak ve yaymak yerinereduce
  4. kullanılması Object.entriesveObject.fromEntries

1. reduceve öğelerini kullanmaObject.keys

İle reduceve Object.keysarzu edilen filtre (ES6 kullanarak uygulamak için ok sözdizimi ):

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => (res[key] = obj[key], res), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

Yukarıdaki kodda predicatebir ekleme koşulu olması gerekir ( kullanılan OP hariç tutma koşulu aksine ), böylece nasıl Array.prototype.filterçalışır doğrultusunda .

2. (1) gibi, ile birlikte Object.assign

Yukarıdaki çözümde , mutasyona uğramış nesneyi döndürmek için parçada virgül operatörü kullanılır . Bu elbette bir ifade yerine iki ifade olarak yazılabilir, ancak ikincisi daha özlüdür. Virgül operatörü olmadan yapmak için, kullanabilirsiniz , hangi yerine gelmez mutasyona uğramış nesneyi döndürmek:reduceresObject.assign

Object.filter = (obj, predicate) => 
    Object.keys(obj)
          .filter( key => predicate(obj[key]) )
          .reduce( (res, key) => Object.assign(res, { [key]: obj[key] }), {} );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

3. mapSözdizimi kullanmak ve yaymak yerinereduce

Burada Object.assignçağrıyı döngüden çıkarıyoruz, bu yüzden sadece bir kez yapılır ve ayrı anahtarlar olarak ayrı anahtarlar olarak iletilir ( yayılma sözdizimini kullanarak ):

Object.filter = (obj, predicate) => 
    Object.assign(...Object.keys(obj)
                    .filter( key => predicate(obj[key]) )
                    .map( key => ({ [key]: obj[key] }) ) );

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};
var filtered = Object.filter(scores, score => score > 1); 
console.log(filtered);

4. Object.entriesve öğelerini kullanmaObject.fromEntries

Çözelti, bir ara dizi nesne çevirir ve ardından düz bir nesne için bu geri dönüştürür gibi, kullanmak yararlı olacaktır Object.entries(diğer bir deyişle (ES2017) ve ters anahtar / değer çiftleri bir diziden bir nesne oluşturmak birlikte) Object.fromEntries( ES2019).

Bu "tek astarlı" yönteme şu yolla götürür Object:

Object.filter = (obj, predicate) => 
                  Object.fromEntries(Object.entries(obj).filter(predicate));

// Example use:
var scores = {
    John: 2, Sarah: 3, Janet: 1
};

var filtered = Object.filter(scores, ([name, score]) => score > 1); 
console.log(filtered);

Yüklem işlevi burada bir anahtar / değer çifti argüman olarak alır, bu biraz farklıdır, ancak yüklem işlevinin mantığında daha fazla olasılık sağlar.


Daha karmaşık bir sorgu olabilir mi? Örneğin:x => x.Expression.Filters.Filter
IamStalker

2
@IamStalker, denedin mi? İkinci argümanda geçerli bir işlev sağladığınız sürece önemli değil. Not: .FilterSonunda ne olduğu hakkında hiçbir fikrim yok , ama eğer bir x => x.Expression.Filters.Filter()
işlevse,

1
Daha yeni özellikler daha az kod oluşturabilir, ancak daha düşük performans sağlar . Ed 3 uyumlu kod çoğu tarayıcıda iki kat daha hızlı çalışır.
RobG


1
İyi iş! Bu harika açıklamalar ve örnekler için teşekkür ederim.
Slavik Meltser

21

Kullanım döndük istekli Eğer varsa çizgi veya lodash kullanabileceğiniz pick(ya da tam tersi, omit).

Alt çizgi belgelerinden örnekler:

_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
// {name: 'moe', age: 50}

Ya da bir geri arama ile (için lodash kullanın pickBy ):

_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
  return _.isNumber(value);
});
// {age: 50}

1
Lodash kötü bir çözümdür çünkü boş nesnelerin filtrelenmesi sayıları da ortadan kaldırır.
mibbit

Bunun için lodash kullandım ve harika bir çözüm. Teşekkürler @Bogdan D!
Ira Herman

@mibbit daha spesifik olabilir misiniz? Sadece geri aramanın doğru bir şekilde uygulanması gerektiğine inanıyorum
Bogdan D

11

ES6 yaklaşımı ...

Aşağıda bu nesneye sahip olduğunuzu düşünün:

const developers = {
  1: {
   id: 1,
   name: "Brendan", 
   family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  },  
  3: {
   id: 3,
   name: "Alireza", 
   family: "Dezfoolian"
 }
};

Bir işlev oluşturun:

const filterObject = (obj, filter, filterValue) => 
   Object.keys(obj).reduce((acc, val) => 
   (obj[val][filter] === filterValue ? acc : {
       ...acc,
       [val]: obj[val]
   }                                        
), {});

Ve ara:

filterObject(developers, "name", "Alireza");

ve dönecektir :

{
  1: {
  id: 1,
  name: "Brendan", 
  family: "Eich"
  },
  2: {
   id: 2,
   name: "John", 
   family: "Resig"
  }
}

1
İyi görünüyor! Ama neden sadece diğer nesneyi geri döndürüyor ("Alireza" adının / filterDeğerine sahip olanı değil)?
Pille

1
@Pille, OP böyle olmasını istedi (negatif filtreyi !predicatekendi kodlarıyla not edin ).
trincot

7

Patrick'in daha önce de belirttiği gibi, kötü bir fikirdir, çünkü kesinlikle kullanmak istediğiniz herhangi bir 3. taraf kodunu kıracaktır.

Eklediğiniz işlevler yinelemenin bir parçası olacağından Object.prototype, jquery veya prototip gibi tüm kütüphaneler genişletirseniz , nesneler üzerindeki tembel yinelemenin ( hasOwnPropertykontroller olmadan ) kesilmesinin nedeni kırılacaktır.


bunun neden kötü bir fikir olduğunu açık ve öz bir şekilde açıklamak için seçildi.
Michael Liquori

5

Nasıl olur:

function filterObj(keys, obj) {
  const newObj = {};
  for (let key in obj) {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

Veya...

function filterObj(keys, obj) {
  const newObj = {};
  Object.keys(obj).forEach(key => {
    if (keys.includes(key)) {
      newObj[key] = obj[key];
    }
  });
  return newObj;
}

4

verilmiş

object = {firstname: 'abd', lastname:'tm', age:16, school:'insat'};

keys = ['firstname', 'age'];

sonra :

keys.reduce((result, key) => ({ ...result, [key]: object[key] }), {});
// {firstname:'abd', age: 16}


3
Bu, sorunun gerektirdiği şekilde belirli bir yüklem işlevi kullanmaz.
trincot

4

Ben yarattım Object.filter() sadece bir işlevi ile filtre değil, aynı zamanda dahil etmek için anahtarlar bir dizi kabul bir . İsteğe bağlı üçüncü parametre, filtreyi ters çevirmenize olanak tanır.

Verilen:

var foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
}

Dizi:

Object.filter(foo, ['z', 'a', 'b'], true);

İşlev:

Object.filter(foo, function (key, value) {
    return Ext.isString(value);
});

kod

Yasal Uyarı : Kısacası Ext JS çekirdeğini kullanmayı seçtim. Sorunun bir parçası olmadığı için nesne türleri için tip denetleyicileri yazmanın gerekli olduğunu hissetmedim.

// Helper function
function print(obj) {
    document.getElementById('disp').innerHTML += JSON.stringify(obj, undefined, '  ') + '<br />';
    console.log(obj);
}

Object.filter = function (obj, ignore, invert) {
    let result = {}; // Returns a filtered copy of the original list
    if (ignore === undefined) {
        return obj;   
    }
    invert = invert || false;
    let not = function(condition, yes) { return yes ? !condition : condition; };
    let isArray = Ext.isArray(ignore);
    for (var key in obj) {
        if (obj.hasOwnProperty(key) &&
                !(isArray && not(!Ext.Array.contains(ignore, key), invert)) &&
                !(!isArray && not(!ignore.call(undefined, key, obj[key]), invert))) {
            result[key] = obj[key];
        }
    }
    return result;
};

let foo = {
    x: 1,
    y: 0,
    z: -1,
    a: 'Hello',
    b: 'World'
};

print(Object.filter(foo, ['z', 'a', 'b'], true));
print(Object.filter(foo, (key, value) => Ext.isString(value)));
#disp {
    white-space: pre;
    font-family: monospace
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/4.2.1/builds/ext-core.min.js"></script>
<div id="disp"></div>


Benim vanilya kontrol edin cevap ve özünü
Z. Khullah

1
@trincot Teşekkürler, yerinde bir dönüş yerine nesnenin bir kopyasını döndürmek için yanıtı güncelledim.
Bay Polywhirl

2

Benim görüşüm:

function objFilter(obj, filter, nonstrict){
  r = {}
  if (!filter) return {}
  if (typeof filter == 'string') return {[filter]: obj[filter]}
  for (p in obj) {
    if (typeof filter == 'object' &&  nonstrict && obj[p] ==  filter[p]) r[p] = obj[p]
    else if (typeof filter == 'object' && !nonstrict && obj[p] === filter[p]) r[p] = obj[p]
    else if (typeof filter == 'function'){ if (filter(obj[p],p,obj)) r[p] = obj[p]}
    else if (filter.length && filter.includes(p)) r[p] = obj[p]
  }
  return r
}

Test senaryoları:

obj = {a:1, b:2, c:3}

objFilter(obj, 'a') // returns: {a: 1}
objFilter(obj, ['a','b']) // returns: {a: 1, b: 2}
objFilter(obj, {a:1}) // returns: {a: 1}
objFilter(obj, {'a':'1'}, true) // returns: {a: 1}
objFilter(obj, (v,k,o) => v%2===1) // returns: {a: 1, c: 3}

https://gist.github.com/bernardoadc/872d5a174108823159d845cc5baba337


2

2020 yılından itibaren Vanilla JS'de çözüm.


let romNumbers={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}

romNumbersNesneyi anahtarla filtreleyebilirsiniz :

const filteredByKey = Object.fromEntries(Object.entries(romNumbers).filter(([key, value]) => key === 'I'))
// filteredByKey = {I: 1} 

Veya romNumbersnesneyi değere göre filtreleyin :

 const filteredByValue = Object.fromEntries(Object.entries(romNumbers).filter(([key, value]) => value === 5))
 // filteredByValue = {V: 5} 

1

Yeni bir nesne oluşturmak yerine aynı nesneyi değiştirmek istiyorsanız.

Aşağıdaki örnek 0 veya boş değerlerin tümünü silecektir:

const sev = { a: 1, b: 0, c: 3 };
const deleteKeysBy = (obj, predicate) =>
  Object.keys(obj)
    .forEach( (key) => {
      if (predicate(obj[key])) {
        delete(obj[key]);
      }
    });

deleteKeysBy(sev, val => !val);

0

Herkesin dediği gibi, prototiple uğraşmayın. Bunun yerine, bunu yapmak için bir işlev yazmanız yeterlidir. İşte benim sürüm lodash:

import each from 'lodash/each';
import get from 'lodash/get';

const myFilteredResults = results => {
  const filteredResults = [];

  each(results, obj => {
    // filter by whatever logic you want.

    // sample example
    const someBoolean = get(obj, 'some_boolean', '');

    if (someBoolean) {
      filteredResults.push(obj);
    }
  });

  return filteredResults;
};

0

İhtiyacım olduğunda bunu kullanıyorum:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}

-1

Bu durumlarda, nesneleri işleyebilen jquery $ .map kullanıyorum. Diğer yanıtlarda belirtildiği gibi, yerel prototipleri değiştirmek iyi bir uygulama değildir ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes )

Aşağıda, yalnızca nesnenizin bazı özelliklerini kontrol ederek filtreleme örneği verilmiştir. Koşulunuz doğruysa kendi nesnesini döndürür veya undefineddeğilse döndürür . undefinedMülkiyet kaydı, nesne listesinden kaybolur olduğunu yapacaktır;

$.map(yourObject, (el, index)=>{
    return el.yourProperty ? el : undefined;
});

1
$.mapbir nesne alabilir, ancak bir dizi döndürür, böylece orijinal özellik adları kaybolur. OP filtrelenmiş düz bir nesneye ihtiyaç duyar.
trincot
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.