One-liner para tomar algunas propiedades del objeto en ES 6

153

¿Cómo se puede escribir una función, que toma solo unos pocos atributos de la manera más compacta en ES6?

Se me ocurrió una solución usando la desestructuración + literal de objeto simplificado, pero no me gusta que esa lista de campos se repita en el código.

¿Existe una solución aún más delgada?

(v) => {
    let { id, title } = v;
    return { id, title };
}
kirilloid
fuente

Respuestas:

124

Aquí hay algo más delgado, aunque no evita repetir la lista de campos. Utiliza la "desestructuración de parámetros" para evitar la necesidad del vparámetro.

({id, title}) => ({id, title})

(Vea un ejemplo ejecutable en esta otra respuesta ).

La solución de @ EthanBrown es más general. Aquí hay una versión más idiomática de la misma que usa Object.assignpropiedades calculadas (la [p]parte):

function pick(o, ...props) {
    return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

Si queremos preservar los atributos de las propiedades, como configurabley getters y setters, al tiempo que omitimos propiedades no enumerables, entonces:

function pick(o, ...props) {
    var has = p => o.propertyIsEnumerable(p),
        get = p => Object.getOwnPropertyDescriptor(o, p);

    return Object.defineProperties({},
        Object.assign({}, ...props
            .filter(prop => has(prop))
            .map(prop => ({prop: get(props)})))
    );
}
Dan Dascalescu
fuente
10
+1 buena respuesta, torazaburo; gracias por avisarme Object.assign; es6 es como un árbol de Navidad con tantos regalos debajo que todavía encuentro regalos meses después de las vacaciones
Ethan Brown
Se produjo un error: la descripción de la propiedad debe ser un objeto: indefinido. ¿No debería ser filter(...).map(prop => ({[prop]: get(prop)})))?
Sin fin
Para su primera pick()implementación, también podría hacer algo comoreturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts el
desafortunadamente esa versión de pick no será segura en flujo o mecanografiado. si desea escribir con seguridad, no hay forma de evitar la asignación de desestructuración del objeto original, y luego asignar cada uno a un nuevo objeto.
duhseekoh
Cuando una propiedad no existe en un objeto, obtienes undefined. A veces importa. Aparte de eso, buena idea.
x-yuri
43

No creo que haya ninguna forma de hacerlo mucho más compacto que su respuesta (o la de torazburo), pero esencialmente lo que está tratando de hacer es emular la pickoperación de Underscore . Sería bastante fácil volver a implementar eso en ES6:

function pick(o, ...fields) {
    return fields.reduce((a, x) => {
        if(o.hasOwnProperty(x)) a[x] = o[x];
        return a;
    }, {});
}

Entonces tiene una práctica función reutilizable:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
Ethan Brown
fuente
Gracias. Esta no es una respuesta para mi pregunta, pero es una muy buena adición.
kirilloid
77
(encogiéndose de hombros) Siento que es una respuesta para su solución; no hay una solución general más delgada (la solución de torazaburo se elimina de la basura extra, pero el problema esencial, que todos los nombres de propiedades deben escribirse dos veces, significa que no se escala mejor que su solución). Mi solución al menos se escala bien ... correcta la pickfunción una vez, y puede elegir tantas propiedades que desee y no las duplicará.
Ethan Brown
1
¿Por qué lo usas hasOwnProperty? Si los campos se seleccionan a mano, incluso inparece ser más apropiado; aunque me gustaría omitir el cheque por completo y simplemente dejarlos predeterminados undefined.
Bergi
Bergi, es un punto razonable ... Solo considero que las propiedades (no los métodos) en una cadena de prototipo son raras y "malolientes" (ya que son un olor a código), y prefiero filtrarlas por defecto. Si hay una aplicación que necesita propiedades de prototipo, bueno ... puede haber una opción para eso.
Ethan Brown
¿Qué pasa con json arrays?
Rizwan Patel
19

El truco para resolver esto como una línea es cambiar el enfoque adoptado: en lugar de comenzar desde el objeto original orig, uno puede comenzar desde las teclas que desea extraer.

El uso de Array#reduceuno puede almacenar cada clave necesaria en el objeto vacío que se pasa como initialValuedicha función.

Al igual que:

const orig = {
  id: 123456789,
  name: 'test',
  description: '…',
  url: 'https://…',
};

const filtered = ['id', 'name'].reduce((result, key) => { result[key] = orig[key]; return result; }, {});

console.log(filtered); // Object {id: 123456789, name: "test"}

Bramus
fuente
11

Una solución un poco más corta usando el operador de coma:

const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})

console.log(
  pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
)  

shesek
fuente
¿como usar esto? ¿Puede dar un ejemplo?
Tomas M
1
Funciona igual que las otras pickfunciones en este hilo:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek
8

La propuesta de propiedades de reposo / propagación de objetos de TC39 hará que esta bonita mancha:

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
z; // { a: 3, b: 4 }

(Tiene el inconveniente de crear las variables xy yque puede que no necesite).

alxndr
fuente
33
Esta es una forma conveniente de omit, pero nopick
kirilloid
55
Me encantaría ver una variante que haga exactamente lo contrario de esto como una propuesta de ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam
3

Puede utilizar la desestructuración de objetos para desempaquetar propiedades del objeto existente y asignarlas a variables con diferentes nombres : campos de un nuevo objeto inicialmente vacío.

const person = {
  fname: 'tom',
  lname: 'jerry',
  aage: 100,
}

let newPerson = {};

({fname: newPerson.fname, lname: newPerson.lname} = person);

console.log(newPerson);

Saksham
fuente
(índice): 36 Sintaxis no capturada Error: objetivo de asignación de desestructuración no válido
Remzes
@Remzes no sabe dónde y cómo está ejecutando esto, pero funciona bien en el editor de código SO y en las herramientas de desarrollador de Chrome.
Saksham
Usé jsfiddle
Remzes
He mejorado un poco su respuesta, pero aún es demasiado detallada, en comparación con lo que solicitó el OP. Repite no solo los nombres de campo, sino también el nombre del nuevo objeto.
Dan Dascalescu
3

ES6 era la última especificación en el momento en que se escribió la pregunta. Como se explica en esta respuesta , la selección de claves es significativamente más corta en ES2019 que en ES6:

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)
Frasco Estus
fuente
2

Actualmente hay una propuesta de Strawman para mejorar la sintaxis abreviada de objetos de JavaScript, que permitiría "seleccionar" propiedades con nombre sin repetición:

const source = {id: "68646", genre: "crime", title: "Scarface"};
const target = {};
Object.assign(target, {source.title, source.id});

console.log(picked);
// {id: "68646", title: "Scarface"}

Desafortunadamente, la propuesta no parece ir a ningún lado pronto. Editado por última vez en julio de 2017 y todavía un borrador en la Etapa 0 , lo que sugiere que el autor puede haberlo abandonado o olvidado.

ES5 y versiones anteriores (modo no estricto)

La taquigrafía más concisa posible que se me ocurre implica una característica del lenguaje antiguo que ya nadie usa:

Object.assign(target, {...(o => {
    with(o) return { id, title };
})(source)});

withlas declaraciones están prohibidas en modo estricto, lo que hace que este enfoque sea inútil para el 99.999% de JavaScript moderno. Es una pena, porque este es el único uso medio decente que he encontrado para la withfunción. 😀


fuente
1

Tengo una solución similar a la de Ethan Brown, pero aún más corta pick. Otra función pick2es un poco más larga (y más lenta), pero permite cambiar el nombre de las propiedades de manera similar a ES6.

const pick = (o, ...props) => props.reduce((r, p) => p in o ? {...r, [p]: o[p]} : r, {})

const pick2 = (o, ...props) => props.reduce((r, expr) => {
  const [p, np] = expr.split(":").map( e => e.trim() )
  return p in o ? {...r, [np || p]: o[p]} : r
}, {}) 

Aquí está el ejemplo de uso:

const d = { a: "1", c: "2" }

console.log(pick(d, "a", "b", "c"))        // -> { a: "1", c: "2" }
console.log(pick2(d, "a: x", "b: y", "c")) // -> { x: "1", c: "2" }
Alexandr Priezzhev
fuente
1
¿Cuál es el motivo del voto negativo? ¿No te funciona?
Alexandr Priezzhev
0

Requerí esta solución, pero no sabía si las claves propuestas estaban disponibles. Entonces, tomé la respuesta de @torazaburo y mejoré para mi caso de uso:

function pick(o, ...props) {
  return Object.assign({}, ...props.map(prop => {
    if (o[prop]) return {[prop]: o[prop]};
  }));
}

// Example:
var person = { name: 'John', age: 29 };
var myObj = pick(person, 'name', 'sex'); // { name: 'John' }
Alwin Kesler
fuente
0

inspirado en el enfoque reducido de https://stackoverflow.com/users/865693/shesek :

const pick = (orig, ...keys) => keys.reduce((acc, key) => ({...acc, [key]: orig[key]}), {})

uso:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') resultados en: {model: "F40", productionYear: 1987}

Kevin K.
fuente