338 votes

JavaScript : filter() pour les objets

ECMAScript 5 dispose de l'option filter() prototype pour Array mais pas Object types, si je comprends bien.

Comment mettre en place un filter() pour Object en JavaScript ?

Disons que j'ai cet objet :

var foo = {
    bar: "Yes"
};

Et je veux écrire un filter() qui fonctionne sur Object s :

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

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

    return result;
};

Cela fonctionne lorsque je l'utilise dans la démo suivante, mais lorsque je l'ajoute à mon site qui utilise jQuery 1.5 et jQuery UI 1.8.9, j'obtiens des erreurs JavaScript dans FireBug.

Object.prototype.filter = function(predicate) {
  var result = {};
  for (key in this) {
    if (this.hasOwnProperty(key) && !predicate(this[key])) {
      console.log("copying");
      result[key] = this[key];
    }
  }
  return result;
};

var foo = {
  bar: "Yes",
  moo: undefined
};

foo = foo.filter(function(property) {
  return typeof property === "undefined";
});

document.getElementById('disp').innerHTML = JSON.stringify(foo, undefined, '  ');
console.log(foo);

#disp {
  white-space: pre;
  font-family: monospace
}

<div id="disp"></div>

432voto

trincot Points 10112

Tout d'abord, il est considéré comme une mauvaise pratique d'étendre Object.prototype . Au lieu de cela, fournissez votre fonctionnalité comme une fonction autonome, ou si vous voulez vraiment étendre un global, fournissez-la comme fonction utilitaire sur Object comme il y en a déjà Object.keys , Object.assign , Object.is , ...etc.

Je propose ici plusieurs solutions :

  1. Utilisation de reduce y Object.keys
  2. Comme (1), en combinaison avec Object.assign
  3. Utilisation de map et la syntaxe de propagation au lieu de reduce
  4. Utilisation de Object.entries y Object.fromEntries

1. Utilisation de reduce y Object.keys

Avec reduce y Object.keys pour mettre en œuvre le filtre souhaité (en utilisant ES6 syntaxe de la flèche ) :

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

Notez que dans le code ci-dessus predicate doit être un inclusion (contrairement à la exclusion condition utilisée par le PO), de manière à ce qu'elle soit conforme à la façon dont les Array.prototype.filter travaux.

2. Comme (1), en combinaison avec Object.assign

Dans la solution ci-dessus, le opérateur de virgule est utilisé dans le reduce pour retourner la partie mutée res objet. On pourrait bien sûr écrire deux déclarations au lieu d'une seule expression, mais cette dernière est plus concise. Pour le faire sans l'opérateur virgule, vous pouvez utiliser Object.assign à la place, ce qui fait retourne l'objet muté :

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. Utilisation de map et la syntaxe de propagation au lieu de reduce

Ici, nous déplaçons le Object.assign hors de la boucle, afin qu'il ne soit effectué qu'une seule fois, et transmettez-lui les clés individuelles en tant qu'arguments séparés (à l'aide de la balise syntaxe de l'étalement ) :

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. Utilisation de Object.entries y Object.fromEntries

Comme la solution traduit l'objet en un tableau intermédiaire et le reconvertit ensuite en un objet ordinaire, il serait utile d'utiliser la fonction Object.entries (ES2017) et l'inverse (c'est-à-dire créer un objet à partir d'un tableau de paires clé/valeur ) avec Object.fromEntries (ES2019).

Cela mène à cette méthode "one-liner" sur 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);

La fonction prédicat reçoit ici une paire clé/valeur comme argument, ce qui est un peu différent, mais permet plus de possibilités dans la logique de la fonction prédicat.

286voto

user113716 Points 143363

Ne jamais prolonger Object.prototype .

Des choses horribles vont arriver à votre code. Des choses vont se briser. Vous étendez tous les types d'objets, y compris les littéraux d'objets.

Voici un exemple rapide que vous pouvez essayer :

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

Au lieu de cela, créez une fonction à laquelle vous passez l'objet.

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

144voto

Qui-Gon Jinn Points 325

Solution en Vanilla JS à partir de l'année 2020.


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

Vous pouvez filtrer romNumbers objet par clé :

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

Ou filtre romNumbers par valeur :

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

30voto

Bogdan D Points 857

Si vous êtes prêt à utiliser soulignement ou lodash vous pouvez utiliser pick (ou son contraire, omit ).

Exemples tirés de la documentation d'underscore :

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

Ou avec un callback (pour lodash utiliser pickBy ) :

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

22voto

Alireza Points 40192

ES6 approche...

Imaginez que vous avez cet objet ci-dessous :

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

Créez une fonction :

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

Et l'appeler :

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

et va retourner :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X