Как получить подмножество свойств объекта JavaScript
Скажем, у меня есть объект:
elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
Я хочу сделать новый объект с подмножеством его свойств.
// pseudo code
subset = elmo.slice('color', 'height')
//=> { color: 'red', height: 'unknown' }
Как я могу достичь этого?
37 ответов
Использование уничтожения объектов и сокращения свойств
const object = { a: 5, b: 6, c: 7 };
const picked = (({ a, c }) => ({ a, c }))(object);
console.log(picked); // { a: 5, c: 7 }
От Филиппа Кьюиша:
Это на самом деле просто анонимная функция, вызываемая мгновенно. Все это можно найти на странице Назначение деструктурирования в MDN. Вот расширенная форма
let unwrap = ({a, c}) => ({a, c});
let unwrap2 = function({a, c}) { return { a, c }; };
let picked = unwrap({ a: 5, b: 6, c: 7 });
let picked2 = unwrap2({a: 5, b: 6, c: 7})
console.log(picked)
console.log(picked2)
Однострочник для произвольного списка выбранных ключей становится короче с каждым выпуском JS:
ES5
Object.keys(obj)
.filter(function (key) {
return ['foo', 'bar'].indexOf(key) >= 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
ES6
Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => Object.assign(obj2, { [key]: obj[key] }), {});
Или с запятой оператор:
Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});
ES2019
ECMAScript 2017 имеет Object.entries
а также Array.prototype.includes
, ECMAScript 2019 имеет Object.fromEntries
они могут быть заполнены при необходимости и упростить задачу:
Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['foo', 'bar'].includes(key))
)
Если вы используете ES6, есть очень лаконичный способ сделать это, используя деструктуру. Разрушение позволяет легко добавлять объекты к объекту с использованием разворота, но также позволяет создавать подмножества объектов таким же образом.
const object = {
a: 'a',
b: 'b',
c: 'c',
d: 'd',
}
// Remove "d" field from original object:
const {c, d, ...partialObject} = object;
const subset = {c, d};
console.log(partialObject) // => { a: 'a', b: 'b'}
console.log(subset) // => { c: 'c', d: 'd'};
Хотя это немного более многословно, вы можете выполнить то, что все остальные рекомендовали подчеркивание /lodash 2 года назад, используя Array.prototype.reduce.
var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});
Этот подход решает его с другой стороны: вместо того, чтобы брать объект и передавать ему имена свойств для извлечения, взять массив имен свойств и преобразовать их в новый объект.
Хотя в простейшем случае это более многословно, обратный вызов здесь довольно удобен, так как вы можете легко выполнить некоторые общие требования, например, изменить свойство 'color' на 'color' для нового объекта, сгладить массивы и т. Д. - любой из вещи, которые вам нужно сделать при получении объекта из одной службы / библиотеки и создании нового объекта, необходимого где-то еще. Хотя подчеркивание /lodash - отличные, хорошо реализованные библиотеки, я предпочитаю подход, основанный на меньшей зависимости от поставщиков, и более простой и последовательный подход, когда моя логика построения подмножеств становится более сложной.
редактировать: версия es7 такая же:
const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});
редактировать: хороший пример для карри тоже! Имейте функцию выбора, возвращающую другую функцию.
const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});
Вышеприведенный пример довольно близок к другому методу, за исключением того, что он позволяет создавать "сборщик" на лету. например
pick('color', 'height')(elmo);
Что особенно приятно в этом подходе, так это то, что вы можете легко передавать выбранные "пики" во все, что выполняет функцию, например, Array#map
:
[elmo, grover, bigBird].map(pick('color', 'height'));
// [
// { color: 'red', height: 'short' },
// { color: 'blue', height: 'medium' },
// { color: 'yellow', height: 'tall' },
// ]
Я добавляю этот ответ, потому что ни один из ответов не использовался Comma operator
,
Это очень легко с деструктурирующим назначением и ,
оператор
const object = { a: 5, b: 6, c: 7 };
const picked = ({a,c} = object, {a,c})
console.log(picked); // { a: 5, c: 7 }
Еще одно решение:
var subset = {
color: elmo.color,
height: elmo.height
}
Это выглядит намного более читабельным для меня, чем практически любой ответ, но, возможно, это только я!
Нет ничего подобного встроенной в базовую библиотеку, но вы можете использовать деструктуризацию объектов, чтобы сделать это...
const {color, height} = sourceObject;
const newObject = {color, height};
Вы также можете написать служебную функцию сделать это...
const cloneAndPluck = function(sourceObject, keys) {
const newObject = {};
keys.forEach((obj, key) => { newObject[key] = sourceObject[key]; });
return newObject;
};
const subset = cloneAndPluck(elmo, ["color", "height"]);
Такие библиотеки, как Lodash, также имеют _.pick()
,
Решение TypeScript:
export function pick<T extends object, U extends keyof T>(obj: T, paths: Array<U>) {
return paths.reduce((o, k) => {
o[k] = obj[k];
return o;
}, Object.create(null));
}
Информация о наборе даже допускает автозаполнение:
Кредит определенно типизирован для U extends keyof T
обмануть!
Я хочу отметить, что очень хорошо курирование здесь:
pick-es2019.js
Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['whitelisted', 'keys'].includes(key))
);
pick-es2017.js
Object.entries(obj)
.filter(([key]) => ['whitelisted', 'keys'].includes(key))
.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
pick-es2015.js
Object.keys(obj)
.filter((key) => ['whitelisted', 'keys'].indexOf(key) >= 0)
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
omit-es2019.js
Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !['blacklisted', 'keys'].includes(key))
);
omit-es2017.js
Object.entries(obj)
.filter(([key]) => !['blacklisted', 'keys'].includes(key))
.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
omit-es2015.js
Object.keys(obj)
.filter((key) => ['blacklisted', 'keys'].indexOf(key) < 0)
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
Вы также можете использовать Lodash.
var subset = _.pick(elmo ,'color', 'height');
В дополнение, скажем, у вас есть массив "elmo":
elmos = [{
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
},{
color: 'blue',
annoying: true,
height: 'known',
meta: { one: '1', two: '2'}
},{
color: 'yellow',
annoying: false,
height: 'unknown',
meta: { one: '1', two: '2'}
}
];
Если вы хотите того же поведения, используя lodash, вы просто:
var subsets = _.map(elmos, function(elm) { return _.pick(elm, 'color', 'height'); });
Разбиение на динамически именованные переменные невозможно в JavaScript, как обсуждалось в этом вопросе.
Чтобы динамически устанавливать ключи, вы можете использовать функцию Reduce без изменения объекта следующим образом:
const getSubset = (keys, obj) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
console.log(getSubset(['color', 'annoying'], elmo));
Динамическое решение
['color', 'height'].reduce((a,b) => (a[b]=elmo[b],a), {})
let elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
let subset= ['color', 'height'].reduce((a,b)=> (a[b]=elmo[b],a),{});
console.log( subset );
Самый простой способ, который я нашел, который не создает ненужных переменных, - это функция, которую вы можете вызвать и которая работает так же, как lodash, следующая:
pick(obj, keys){
return Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
}
Например:
pick(obj, keys){
return Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
}
const obj = {a:1, b:2, c:3, d:4}
const keys = ['a', 'c', 'f']
const picked = pick(obj,keys)
console.log(picked)
Использование pick
метод библиотеки lodash, если вы уже используете.
var obj = { 'a': 1, 'b': '2', 'c': 3 };
_.pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }
Массив объектов
const aListOfObjects = [{
prop1: 50,
prop2: "Nothing",
prop3: "hello",
prop4: "What's up",
},
{
prop1: 88,
prop2: "Whatever",
prop3: "world",
prop4: "You get it",
},
]
Создание подмножества объекта или объектов может быть достигнуто путем деструктуризации объекта таким образом.
const sections = aListOfObjects.map(({prop1, prop2}) => ({prop1, prop2}));
Использование оператора with с сокращенным синтаксисом литерала объекта
Никто еще не продемонстрировал этот метод, вероятно, потому, что он ужасен и вам не следует его делать, но я чувствую, что его нужно перечислить.
var o = {a:1,b:2,c:3,d:4,e:4,f:5}
with(o){
var output = {a,b,f}
}
console.log(output)
Pro: Вам не нужно вводить имена свойств дважды.
Минусы: оператор " with " не рекомендуется по многим причинам.
Вывод: отлично работает, но не используйте его.
Если вы хотите сохранить больше свойств, чем те, которые вы хотите удалить, вы можете использовать синтаксис остальных параметров:
const obj = {
a:1,
b:2,
c:3,
d:4
};
const { a, ...newObj } = obj;
console.log(newObj); // {b: 2, c: 3, d: 4}
Просто по-другому...
var elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
}
var subset = [elmo].map(x => ({
color: x.color,
height: x.height
}))[0]
Вы можете использовать эту функцию с массивом объектов =)
Чтобы добавить еще один эзотерический способ, это тоже работает:
var obj = {a: 1, b:2, c:3}
var newobj = {a,c}=obj && {a,c}
// {a: 1, c:3}
но вы должны написать имена реквизита дважды.
Как насчет:
function sliceObj(obj) {
var o = {}
, keys = [].slice.call(arguments, 1);
for (var i=0; i<keys.length; i++) {
if (keys[i] in obj) o[keys[i]] = obj[keys[i]];
}
return o;
}
var subset = sliceObj(elmo, 'color', 'height');
Как и некоторые в этой теме, я согласен с evert, что наиболее очевидный старый способ сделать это на самом деле является лучшим из доступных, однако для развлечения позвольте мне предложить еще один нежелательный способ сделать это в определенных обстоятельствах, например, когда вы уже определили свое подмножество и вы хотите скопировать в него свойства из другого объекта, который содержит надмножество или пересекающийся набор его свойств.
Это работает для меня в консоли Chrome. Есть проблемы с этим?
var { color, height } = elmo
var subelmo = { color, height }
console.log(subelmo) // {color: "red", height: "unknown"}
преобразовать аргументы в массив
использование
Array.forEach()
подобрать недвижимостьObject.prototype.pick = function() { var obj = {}; var args = arguments Array.from(args).forEach((k) => { obj[k]=this[k] }) return obj } var a = {0:"a",1:"b",2:"c"} var b = a.pick('1','2') //output will be {1: "b", 2: "c"}
Я думаю, это ваш ответ. (и всех, кто ищет).
const object = { a: 5, b: 6, c: 7 };
const subset = (({ a, c }) => ({ a, c }))(object);
console.log(subset); // { a: 5, c: 7 }
Вот полное решение для Node.js
>>> npm i --save lodash.pick
var pick = require('lodash.pick');
var user = {name: 'Bill', number:'1', age:'10'}
var subset = pick(user, ['name', 'number']);
Я знаю, что это не самое чистое, но это просто и понятно.
function obj_multi_select(obj, keys){
let return_obj = {};
for (let k = 0; k < keys.length; k++){
return_obj[keys[k]] = obj[keys[k]];
};
return return_obj;
};
Разрушающее присваивание с динамическими свойствами
Это решение применимо не только к вашему конкретному примеру, но и более широко:
const subset2 = (x, y) => ({[x]:a, [y]:b}) => ({[x]:a, [y]:b});
const subset3 = (x, y, z) => ({[x]:a, [y]:b, [z]:c}) => ({[x]:a, [y]:b, [z]:c});
// const subset4...etc.
const o = {a:1, b:2, c:3, d:4, e:5};
const pickBD = subset2("b", "d");
const pickACE = subset3("a", "c", "e");
console.log(
pickBD(o), // {b:2, d:4}
pickACE(o) // {a:1, c:3, e:5}
);
Вы можете легко определить subset4
и т.д., чтобы принять во внимание больше свойств.
Старый добрый Array.prototype.reduce
:
const selectable = {a: null, b: null};
const v = {a: true, b: 'yes', c: 4};
const r = Object.keys(selectable).reduce((a, b) => {
return (a[b] = v[b]), a;
}, {});
console.log(r);
этот ответ использует магический оператор запятой, а также: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
если вы хотите стать действительно модным, это более компактно:
const r = Object.keys(selectable).reduce((a, b) => (a[b] = v[b], a), {});
Собираем все вместе в многократно используемую функцию:
const getSelectable = function (selectable, original) {
return Object.keys(selectable).reduce((a, b) => (a[b] = original[b], a), {})
};
const r = getSelectable(selectable, v);
console.log(r);
У меня такая же проблема, и я легко ее решил, используя следующие библиотеки:
object.pick
https://www.npmjs.com/package/object.pick
pick({a: 'a', b: 'b', c: 'c'}, ['a', 'b'])
//=> {a: 'a', b: 'b'}
object.omit
https://www.npmjs.com/package/object.omit
omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c'])
//=> { b: 'b' }