Filtrer les propriétés des objets par clé dans ES6


267

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 donc j'ai quelque chose comme.

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

Je cherche un moyen propre d'accomplir cela en utilisant Es6, donc des opérateurs de diffusion sont à ma disposition.


ES6 n'a pas d'opérateurs de propagation d'objet, et vous n'en avez pas besoin ici de toute façon
Bergi


@DanDascalescu Mais cette réponse donne un moyen ES6 d'accomplir ce que l'OP demande, n'est-ce pas?
Jonathan H

Et si je voulais filtrer par clé / valeur?
jmchauv

Réponses:


504

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.keyspour répertorier toutes les propriétés raw(les données d'origine), puis
  2. Array.prototype.filter pour sélectionner les clés présentes dans la liste autorisée, en utilisant
    1. Array.prototype.includes pour s'assurer qu'ils sont présents
  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 répartition des objets pour créer une série d'objets sans les muter (merci à rjerue de l'avoir mentionné ):

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

Pour des questions triviales, si vous vouliez supprimer les champs indésirables des données d'origine (ce que je ne recommanderais pas de faire, car cela implique des mutations moches), vous pouvez inverser la includesvérification comme suit:

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.


1
Merci qui a très bien fonctionné. J'ai également trouvé une approche en utilisant la syntaxe de déconstruction. IE: const {Item1, item3} = const brute newObject = {Item1, item3}
29er

2
La déconstruction fonctionnera (très bien), mais est purement au moment de la compilation. Vous ne pouvez pas en faire une liste dynamique de propriétés ou fournir des règles complexes (une boucle peut être associée à des rappels de validation, par exemple).
ssube

3
Je ne peux pas voter assez pour ça! Bravo pour utiliser le filtre et réduire et ne pas construire un autre objet à partir d'une boucle for. Et impressionnant que vous ayez explicitement séparé les versions immuables et mutables. +1
Sukima

3
Avertissement rapide: Array.prototype.includesne fait pas partie d'ES6. Il est introduit dans ECMAScript 2016 (ES7).
Vineet

3
Si vous voulez faire la réduction de manière immuable, vous pouvez également remplacer le contenu de la fonction par return {... obj, [key]: raw [key]}
rjerue

93

Si vous êtes d'accord avec l'utilisation de la syntaxe ES6, je trouve que la façon la plus propre de le faire, comme indiqué 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, newDatacontient:

{
  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 item2mais puisque vous utilisez une constaffectation, 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 fonctionnement à un seul revêtement, mais cela ne vous oblige pas non plus à savoir quelles sont les autres clés (celles que vous souhaitez conserver).


5
" _représente une valeur à jeter" d'où cela vient-il? Première fois que je le vois
yhabib

5
Je crois que c'est une convention adoptée par la communauté JS. An _est simplement un nom de variable valide qui peut être utilisé dans JS mais comme il est à peu près sans nom, il ne devrait vraiment pas être utilisé de cette façon si vous souhaitez transmettre l'intention. Donc, il a été adopté comme un moyen de désigner une variable dont vous ne vous souciez pas. Voici d'autres discussions à ce sujet: stackoverflow.com/questions/11406823/…
Ryan H.

2
C'est beaucoup plus ordonné que la réponse acceptée, évite la surcharge de création d'un nouveau tableau avec Object.keys (), et la surcharge d'itération du tableau avec filteret reduce.
ericsoco

3
@yhabib _n'a pas d'importance, c'est juste un nom de variable, vous pouvez le renommer comme vous voulez
Vic

1
@Gerrat Je ne pense pas qu'une solution généralisée serait un one-liner trivial. Pour cela, j'utiliserais la omitfonction de lodash : lodash.com/docs/4.17.10#omit ou l'une des autres solutions données ici.
Ryan H.

42

Le moyen le plus propre que vous pouvez 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)

3
Il est important de souligner que pour cela une dépendance de projet supplémentaire doit être téléchargée lors de l'exécution.
S. Esteves

1
_.omit (obj, ["item2"]) - lodash.omit est un opposé à lodash.pick
Alexander Solovyev

29

Rien de ce qui n'a pas été dit auparavant, mais pour 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);


1
c'est une solution beaucoup plus simple et performante que les autres;)
pomeh

26

Juste une autre solution dans une ligne de Modern JS sans bibliothèque externe .

Je jouais avec la fonction " Destructuration ":

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


2
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);Problème de syntaxe
Awol

1
Il y a quelques choses condensées ici: Tout d'abord, il y a la déclaration de la fonction. c'est une fonction fléchée (elle prend un objet et retourne un objet). C'est la même chose que function (obj) {return obj;}. la deuxième chose est que ES6 permet de déstructurer l'avenir. Dans ma déclaration de fonction, je déstructure mon objet {item1, item3}. Et la dernière chose est que j'invoque moi-même ma fonction. Vous pouvez utiliser la fonction d'auto-invocation pour gérer votre portée par exemple. Mais ici, c'était juste pour condenser le code. J'espère que c'est clair. Si je manque quelque chose, n'hésitez pas à en ajouter plus.
Novy

5
c'est l'approche moderne préférée à mon humble avis
mattdlockyer

20

Vous pouvez maintenant le rendre plus court et plus simple en utilisant la méthode Object.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


1
C'est maintenant la meilleure façon, je pense. Beaucoup plus intuitif que réduire.
Damon

10

Vous pouvez ajouter un générique ofilter(implémenté avec générique oreduce) afin que vous puissiez facilement filtrer les objets de la même manière que vous pouvez les 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 -

Ces deux fonctions pourraient être mises en œuvre de plusieurs façons. J'ai choisi de l'attacher à l' Array.prototype.reduceintérieur oreducemais vous pourriez tout aussi facilement tout écrire à partir de zéro


J'aime votre solution, mais je ne sais pas combien elle est plus claire / plus efficace que celle- ci .
Jonathan H

3
Voici un benchmark montrant que votre solution est la plus rapide.
Jonathan H

Dans oreduce, le premier accest masqué .reduce(acc, k), et dans ofilterle oest masqué - oreduceest appelé avec une variable appelée oégalement - qui est lequel?
Jarrod Mosen

dans ofilterla variable oest ombrée mais elle pointe toujours vers la même entrée var; c'est pourquoi il est masqué ici, car c'est la même chose - l'accumulateur ( acc) est également masqué car il montre plus clairement comment les données se déplacent via le lambda; accn'est pas toujours la même liaison , mais il représente toujours l' état persistant actuel du résultat de calcul que nous souhaitons retourner
Merci

Bien que le code fonctionne bien et que la méthode soit très bonne, je me demande quelle aide pour écrire du code comme ça pour quelqu'un qui a évidemment besoin d'aide avec javascript. Je suis tout pour la brièveté mais c'est presque aussi compact qu'un code minimisé.
Paul G Mihai

7

Voici comment je l'ai fait récemment:

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

Hm. Il semble que vous mélangez l'ancienne et la nouvelle syntaxe. Object.assign == ...Vous pourriez écrire const dummyObj = { ...obj }et const target = { ...dummyObj }. De plus, ce dernier n'est pas du tout nécessaire, car vous pourriez ensuite travailler directement avec dummyObj.
Andy

7

ok, que diriez-vous de ce one-liner

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

2
La solution la plus étonnante de toutes les réponses. +1
Gergő Horváth

que faire si vous ne connaissez que les clés que vous souhaitez supprimer ... pas celles que vous souhaitez conserver?
Jason

6

Les réponses ici sont certainement appropriées mais elles sont un peu lentes car elles nécessitent de parcourir 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 boucle qu'une seule fois dans 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)

5

Ferroutage 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 ES6 est que vous n'avez pas à passer en allowedparamètre.


4

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, et la withdéclaration n'est pas recommandée ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with ).


4

Une solution plus simple sans utilisation filterpeut être obtenue avec Object.entries()au lieu deObject.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
},{})

3

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

Au lieu de cela, utiliser une for...inboucle pour parcourir les clés d'un objet, ou parcourir les clés autorisées, puis composer un nouvel objet est ~ 50% plus performant a .

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 repères d'un cas d'utilisation simple. Les résultats différeront en fonction des navigateurs.


3

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;

3
const filteredObject = Object.fromEntries(Object.entries(originalObject).filter(([key, value]) => key !== uuid))

2
Il y a d'autres réponses qui fournissent la question du PO, et elles ont été publiées il y a quelque temps. Lorsque vous publiez une réponse, assurez-vous d'ajouter soit une nouvelle solution, soit une meilleure explication, en particulier lorsque vous répondez à des questions plus anciennes.
help-info.de

2

Manière simple! Pour faire ça.

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


Vous ne filtrez rien ici, vous ne faites que détruire les données fournies. Mais que se passe-t-il lorsque les données changent?
Idris Dopico Peña

2

Autre solution utilisant la "nouvelle" Array.reduceméthode:

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

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

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

console.log(filtered);

Démonstration dans ce violon ...


Mais j'aime la solution dans cette réponse ici qui utilise et :Object.fromEntries Array.filterArray.includes

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

Démonstration dans ce violon ...


Wilt, votre deuxième approche est un double exact de cette réponse fournie l'année dernière: stackoverflow.com/a/56081419/5522000
Art Schmidt

@ArtSchmidt Merci pour votre commentaire: Manqué celui-là dans la longue liste. Je ferai référence à cette réponse à la place.
Flétrissement le

1

OK, qu'en est-il:

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-le comme ceci:

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

1

Cette fonction filtrera un objet en fonction d'une liste de clés, c'est plus efficace que la réponse précédente car elle n'a pas besoin d'utiliser Array.filter avant d'appeler Reduce. 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
    }
  }, {})
}

1

Pendant la boucle, ne retournez rien lorsque certaines propriétés / clés sont rencontrées et continuez 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>
    );
});

1

Je suis surpris que personne ne l'ait encore suggéré. C'est super propre et très explicite sur les clés que vous souhaitez conserver.

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}

const filterObject = ({a,b,c}) => ({a,b,c})
const filteredObject = filterObject(unfilteredObject)

Ou si vous voulez une doublure sale:

const unfilteredObj = {a: ..., b:..., c:..., x:..., y:...}

const filteredObject = (({a,b,c})=>({a,b,c}))(unfilteredObject);

Cette méthode ajoutera une clé non présente dans le tableau d'origine. Peut-être un problème ou non, cela devrait au moins être souligné.
ponchietto

0

Une autre approche serait d'utiliser Array.prototype.forEach()comme

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

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

var finalObj = {};
allowed.forEach(allowedVal => {
  if (raw[allowedVal])
    finalObj[allowedVal] = raw[allowedVal]
})
console.log(finalObj)

Il inclut uniquement les valeurs des clés disponibles dans les données brutes et empêche ainsi l'ajout de données indésirables.


0

Ce serait ma solution:

const filterObject = (obj, condition) => {
    const filteredObj = {};
    Object.keys(obj).map(key => {
      if (condition(key)) {
        dataFiltered[key] = obj[key];
      }
    });
  return filteredObj;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.