web-dev-qa-db-fra.com

Filtrer les propriétés d'objet par clé dans ES6

Disons que j'ai un objet:

{
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

Je veux créer un autre objet en filtrant l'objet ci-dessus afin d'avoir quelque chose comme.

 {
    item1: { key: 'sdfd', value:'sdfd' },
    item3: { key: 'sdfd', value:'sdfd' }
 }

Je suis à la recherche d'un moyen propre de réaliser cela en utilisant Es6, afin que les opérateurs de spread soient disponibles pour moi.

125
29er

Si vous avez une liste de valeurs autorisées, vous pouvez facilement les conserver dans un objet en utilisant:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.keys(raw)
  .filter(key => allowed.includes(key))
  .reduce((obj, key) => {
    obj[key] = raw[key];
    return obj;
  }, {});

console.log(filtered);

Cela utilise:

  1. Object.keys pour lister toutes les propriétés dans raw (les données d'origine), puis
  2. Array.prototype.filter pour sélectionner les clés présentes dans la liste des permis, à l'aide de
    1. Array.prototype.includes pour s'assurer de leur présence
  3. Array.prototype.reduce pour construire un nouvel objet avec uniquement les propriétés autorisées.

Cela fera une copie superficielle avec les propriétés autorisées (mais ne copiera pas les propriétés elles-mêmes).

Vous pouvez également utiliser l'opérateur de propagation d'objet pour créer une série d'objets sans les transformer (grâce à rjerue pour avoir mentionné ceci ):

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.keys(raw)
  .filter(key => allowed.includes(key))
  .reduce((obj, key) => {
    return {
      ...obj,
      [key]: raw[key]
    };
  }, {});

console.log(filtered);

Si vous voulez supprimer les champs non désirés des données d'origine (ce que je ne voudrais pas recommander de faire, car cela implique des mutations laides), vous pouvez inverser le contrôle includes de la manière suivante:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

Object.keys(raw)
  .filter(key => !allowed.includes(key))
  .forEach(key => delete raw[key]);

console.log(raw);

J'inclus cet exemple pour montrer une solution basée sur la mutation, mais je ne suggère pas de l'utiliser.

251
ssube

Si vous êtes d'accord avec l'utilisation de la syntaxe ES6, je trouve que le moyen le plus propre de le faire, comme indiqué par ici et ici est:

const data = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const { item2, ...newData } = data;

Maintenant, newData contient:

{
  item1: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

Ou, si vous avez la clé stockée sous forme de chaîne:

const key = 'item2';
const { [key]: _, ...newData } = data;

Dans ce dernier cas, [key] est converti en item2, mais comme vous utilisez une affectation const, vous devez spécifier un nom pour l'affectation. _ représente une valeur à jeter.

Plus généralement:

const { item2, ...newData } = data; // Assign item2 to item2
const { item2: someVarName, ...newData } = data; // Assign item2 to someVarName
const { item2: _, ...newData } = data; // Assign item2 to _
const { ['item2']: _, ...newData } = data; // Convert string to key first, ...

Non seulement cela réduit votre opération à une ligne, mais vous n’avez pas besoin de savoir quelles sont les autres clés (celles que vous souhaitez conserver).

45
Ryan H.

La manière la plus propre que vous puissiez trouver est avec Lodash # pick

const _ = require('lodash');

const allowed = ['item1', 'item3'];

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

const filteredObj = _.pick(obj, allowed)
29
Guy Segev

Rien qui n'ait été dit auparavant, si ce n'est combiner quelques réponses à une réponse générale ES6:

const raw = {
  item1: { key: 'sdfd', value: 'sdfd' },
  item2: { key: 'sdfd', value: 'sdfd' },
  item3: { key: 'sdfd', value: 'sdfd' }
};

const filteredKeys = ['item1', 'item3'];

const filtered = filteredKeys
  .reduce((obj, key) => ({ ...obj, [key]: raw[key] }), {});

console.log(filtered);

12
Clemens Helm

Juste une autre solution dans une ligne de code .

Je jouais avec " Destructuring " feature:

const raw = {
    item1: { key: 'sdfd', value: 'sdfd' },
    item2: { key: 'sdfd', value: 'sdfd' },
    item3: { key: 'sdfd', value: 'sdfd' }
  };
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);
console.log(myNewRaw);

11
Novy

Vous pouvez ajouter une ofilter générique (implémentée avec oreduce générique) afin de pouvoir facilement filtrer les objets de la même manière que vous pouvez utiliser des tableaux -

const oreduce = (f, acc, o) =>
  Object
    .entries (o)
    .reduce
      ( (acc, [ k, v ]) => f (acc, v, k, o)
      , acc
      )

const ofilter = (f, o) =>
  oreduce
    ( (acc, v, k, o)=>
        f (v, k, o)
          ? Object.assign (acc, {[k]: v})
          : acc
    , {}
    , o
    )

Nous pouvons le voir fonctionner ici -

const data =
  { item1: { key: 'a', value: 1 }
  , item2: { key: 'b', value: 2 }
  , item3: { key: 'c', value: 3 }
  }

console.log
  ( ofilter
      ( (v, k) => k !== 'item2'
      , data
      )
      // [ { item1: { key: 'a', value: 1 } }
      // , { item3: { key: 'c', value: 3 } }
      // ]

  , ofilter
      ( x => x.value === 3
      , data
      )
      // [ { item3: { key: 'c', value: 3 } } ]
  )

Vérifiez les résultats dans votre propre navigateur ci-dessous -

const oreduce = (f, acc, o) =>
  Object
    .entries (o)
    .reduce
      ( (acc, [ k, v ]) => f (acc, v, k, o)
      , acc
      )

const ofilter = (f, o) =>
  oreduce
    ( (acc, v, k, o)=>
        f (v, k, o)
          ? Object.assign (acc, { [k]: v })
          : acc
    , {}
    , o
    )

const data =
  { item1: { key: 'a', value: 1 }
  , item2: { key: 'b', value: 2 }
  , item3: { key: 'c', value: 3 }
  }

console.log
  ( ofilter
      ( (v, k) => k !== 'item2'
      , data
      )
      // [ { item1: { key: 'a', value: 1 } }
      // , { item3: { key: 'c', value: 3 } }
      // ]

  , ofilter
      ( x => x.value === 3
      , data
      )
      // [ { item3: { key: 'c', value: 3 } } ]
  )

Ces deux fonctions pourraient être mises en œuvre de plusieurs manières. J'ai choisi d'attacher à Array.prototype.reduce dans oreduce mais vous pouvez tout aussi facilement tout écrire à partir de zéro

6
user633183

Voici comment je l'ai fait, récemment:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});
3
Rajat Saxena

Piggybacking sur la réponse de ssube .

Voici une version réutilisable.

Object.filterByKey = function (obj, predicate) {
  return Object.keys(obj)
    .filter(key => predicate(key))
    .reduce((out, key) => {
      out[key] = obj[key];
      return out;
    }, {});
}

Pour l'appeler utilisez

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

var filtered = Object.filterByKey(raw, key => 
  return allowed.includes(key));
});

console.log(filtered);

La belle chose à propos des fonctions fléchées de ES6 est qu'il n'est pas nécessaire de passer allowed en tant que paramètre.

3
Evan Plaice

ok, que diriez-vous de cette ligne

    const raw = {
      item1: { key: 'sdfd', value: 'sdfd' },
      item2: { key: 'sdfd', value: 'sdfd' },
      item3: { key: 'sdfd', value: 'sdfd' }
    };

    const filteredKeys = ['item1', 'item3'];

    const filtered = Object.assign({}, ...filteredKeys.map(key=> ({[key]:raw[key]})));
3
inabramova

Vous pouvez faire quelque chose comme ça:

const base = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const filtered = (
    source => { 
        with(source){ 
            return {item1, item3} 
        } 
    }
)(base);

// one line
const filtered = (source => { with(source){ return {item1, item3} } })(base);

Cela fonctionne, mais n'est pas très clair. En outre, l'instruction with n'est pas recommandée ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with ).

2
Bouni

Une solution plus simple sans utiliser filter peut être obtenue avec Object.entries() au lieu de Object.keys()

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.entries(raw).reduce((acc,Elm)=>{
  const [k,v] = Elm
  if (allowed.includes(k)) {
    acc[k] = v 
  }
  return acc
},{})
2
Itai Noam

Les réponses ici sont certainement appropriées, mais elles sont un peu lentes car elles nécessitent la lecture en boucle de la liste blanche pour chaque propriété de l'objet. La solution ci-dessous est beaucoup plus rapide pour les grands ensembles de données car elle ne parcourt qu'une fois la liste blanche: 

const data = {
  allowed1: 'blah',
  allowed2: 'blah blah',
  notAllowed: 'woah',
  superSensitiveInfo: 'whooooah',
  allowed3: 'bleh'
};

const whitelist = ['allowed1', 'allowed2', 'allowed3'];

function sanitize(data, whitelist) {
    return whitelist.reduce(
      (result, key) =>
        data[key] !== undefined
          ? Object.assign(result, { [key]: data[key] })
          : result,
      {}
    );
  }

  sanitize(data, whitelist)
2
Joey Grisafe

Vous pouvez maintenant le rendre plus court et plus simple en utilisant la méthodeObject.fromEntries(vérifiez la prise en charge du navigateur):

const raw = { item1: { prop:'1' }, item2: { prop:'2' }, item3: { prop:'3' } };

const allowed = ['item1', 'item3'];

const filtered = Object.fromEntries(Object.entries(raw).filter(([key, val])=>allowed.includes(key)));

en savoir plus sur: Object.fromEntries

0
dudi harush

OK, que diriez-vous de ceci:

const myData = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

function filteredObject(obj, filter) {
  if(!Array.isArray(filter)) {
   filter = [filter.toString()];
  }
  const newObj = {};
  for(i in obj) {
    if(!filter.includes(i)) {
      newObj[i] = obj[i];
    }
  }
  return newObj;
}

et appelez ça comme ça:

filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}
0
Alireza

Pendant la boucle, ne retourne rien lorsque certaines propriétés/clés sont rencontrées et continue avec le reste:

const loop = product =>
Object.keys(product).map(key => {
    if (key === "_id" || key === "__v") {
        return; 
    }
    return (
        <ul className="list-group">
            <li>
                {product[key]}
                <span>
                    {key}
                </span>
            </li>
        </ul>
    );
});
0
Ryan Dhungel

Cette fonction va filtrer un objet en fonction d'une liste de clés, ce qui est plus efficace que la réponse précédente car il n'est pas nécessaire d'utiliser Array.filter avant d'appeler réduire. donc son O(n) par opposition à O (n + filtré)

function filterObjectByKeys (object, keys) {
  return Object.keys(object).reduce((accum, key) => {
    if (keys.includes(key)) {
      return { ...accum, [key]: object[key] }
    } else {
      return accum
    }
  }, {})
}
0
Khaled Osman

Vous pouvez supprimer une clé spécifique sur votre objet

items={
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

// Example 1
var key = "item2";
delete items[key]; 

// Example 2
delete items["item2"];

// Example 3
delete items.item2;
0
Esin ÖNER

Il y a plusieurs façons d'accomplir ceci. Le réponse acceptée utilise une approche Keys-Filter-Réduire, qui n’est pas la plus performante.

Utiliser plutôt une boucle for...in pour parcourir les clés d'un objet ou les clés autorisées, et then composer un nouvel objet est environ 50% plus performantune.

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const keys = ['item1', 'item3'];

function keysReduce (obj, keys) {
  return keys.reduce((acc, key) => {
    if(obj[key] !== undefined) {
      acc[key] = obj[key];
    }
    return acc;
  }, {});
};

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

keysReduce(obj, keys);   // Faster if the list of allowed keys are short
forInCompose(obj, keys); // Faster if the number of object properties are low

une. Voir jsPerf pour les points de repère d'un cas d'utilisation simple. Les résultats seront différents selon les navigateurs.

0
d4nyll