Filtrar propiedades de objetos por clave en ES6


Digamos que tengo un objeto:

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

Quiero crear otro objeto filtrando el objeto anterior para tener algo como.

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

Estoy buscando una manera limpia de lograr esto usando Es6, por lo que los operadores de propagación están disponibles para mí. ¡Gracias!

Author: Dan Dascalescu, 2016-08-03

14 answers

Si tiene una lista de valores permitidos, puede conservarlos fácilmente en un objeto utilizando:

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

Esto utiliza:

  1. Object.keys para listar todas las propiedades en raw (los datos originales), entonces
  2. Array.prototype.filter para seleccionar las claves que están presentes en la lista permitida, usando
    1. Array.prototype.includes para asegurarse de que están presentes
  3. Array.prototype.reduce para construir un nuevo objeto con solo el permitido propiedad.

Esto hará una copia superficial con las propiedades permitidas (pero no copiará las propiedades por sí mismas).

También puede usar el operador de propagación de objetos para crear una serie de objetos sin mutarlos (gracias a rjerue por mencionar esto):

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

Para fines de trivia, si desea eliminar los campos no deseados de los datos originales (lo que no recomendaría hacer, ya que implica algunas mutaciones feas), usted podría invertir el includes cheque así:

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

Estoy incluyendo este ejemplo para mostrar una solución basada en mutaciones, pero no sugiero usarla.

 175
Author: ssube,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-08 14:51:49

Si está de acuerdo con usar la sintaxis ES6, encuentro que la forma más limpia de hacer esto, como se indica aquí y aquí es:

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

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

Ahora, newData contiene:

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

O, si tiene la clave almacenada como una cadena:

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

En este último caso, [key] se convierte a item2 pero como está utilizando una asignación const, debe especificar un nombre para la asignación. _ representa un valor de desecho.

Más generalmente:

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, ...

No solo esto reduce su operación a un solo revestimiento, pero tampoco requiere que sepa cuáles son las otras claves (las que desea preservar).

 31
Author: Ryan H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-26 17:45:39

La forma más limpia que puedes encontrar es con 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)
 15
Author: Guy Segev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-01 07:21:39

Nada que no se haya dicho antes, sino combinar algunas respuestas a una respuesta general 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);
 8
Author: Clemens Helm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-15 00:15:58

Puede agregar un genérico ofilter (implementado con genérico oreduce) para que pueda filtrar fácilmente los objetos de la manera en que puede hacer arreglos.

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

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

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

// filter data where k ("key") is not equal to 'item2' 
let result = ofilter ((v,k)=> k !== 'item2', data)

console.log(result)

Estas dos funciones podrían implementarse de muchas maneras. Elegí adjuntar a Array.prototype.reduce dentro de oreduce pero podrías escribirlo todo desde cero

 6
Author: user633183,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-03 22:52:42

Otra solución que estaba jugando con la función de" Desestructuración "

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

Por cierto, no se por qué tengo que poner el retorno en este caso para que funcione de lo contrario me enfrenté a un indefinido. La parte más extraña es

var myNewRaw = (({ item1, item3}) => { item1, item3})(raw); 
// myNewRaw undefined

Pero esto a continuación funciona:

var myNewRaw = (({ item1, item3}) => [item1, item3] )(raw) ;
//myNewRaw ​​​​​[ { key: 'sdfd', value: 'sdfd' },​​​​​{ key: 'sdfd', value: 'sdfd' } ]​​​​​
 4
Author: Novy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-30 13:18:20

Así es como lo hice, recientemente:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});
 3
Author: Rajat Saxena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-23 18:03:02

Piggybacking en respuesta de ssube.

Aquí hay una versión reutilizable.

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

Para llamarlo use

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

Lo bueno de las funciones de flecha ES6 es que no tiene que pasar allowed como parámetro.

 2
Author: Evan Plaice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-17 05:22:12

Las respuestas aquí son definitivamente adecuadas, pero son un poco lentas porque requieren un bucle a través de la lista blanca para cada propiedad en el objeto. La solución a continuación es mucho más rápida para conjuntos de datos grandes porque solo recorre la lista blanca una vez:

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
Author: Joey Grisafe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-11 17:26:36

Puedes hacer algo como esto:

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

Esto funciona, pero no está muy claro, además de que la declaración with no se recomienda ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with).

 1
Author: Bouni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-14 06:09:40

OK, ¿qué tal esto:

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

Y llámalo así:

filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}
 0
Author: Alireza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-04 05:06:28

Hay muchas maneras de lograr esto. La respuesta aceptada utiliza un enfoque Keys-Filter-Reduce, que no es el más eficaz.

En su lugar, usar un bucle for...inpara recorrer las teclas de un objeto, o bucear a través de las teclas permitidas, y entoncescomponer un nuevo objeto es ~50% más 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

a. Ver jsPerf para los puntos de referencia de un caso de uso simple. Los resultados diferirán según los navegadores.

 0
Author: d4nyll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-07 12:41:59

Una solución más simple sin usar filter se puede lograr con Object.entries() en lugar 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
},{})
 0
Author: Itai Noam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-14 05:37:50

Esta función filtrará un objeto basado en una lista de claves, es más eficiente que la respuesta anterior ya que no tiene que usar Matriz.filtrar antes de llamar reducir. así que su O(n) en lugar de O (n + filtrado)

function filterObjectByKeys (object, keys) {
  return Object.keys(object).reduce((accum, key) => {
    if (keys.includes(key)) {
      return { ...accum, [key]: object[key] }
    } else {
      return accum
    }
  }, {})
}
 0
Author: Khaled Osman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-19 14:30:34