Ответ 1
Использование уничтожения объектов и сокращения свойств
const object = { a: 5, b: 6, c: 7 };
const picked = (({ a, c }) => ({ a, c }))(object);
console.log(picked); // { a: 5, c: 7 }
Скажем, у меня есть объект:
elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
Я хочу создать новый объект с подмножеством его свойств.
// pseudo code
subset = elmo.slice('color', 'height')
//=> { color: 'red', height: 'unknown' }
Как я могу это достичь?
Использование уничтожения объектов и сокращения свойств
const object = { a: 5, b: 6, c: 7 };
const picked = (({ a, c }) => ({ a, c }))(object);
console.log(picked); // { a: 5, c: 7 }
Предлагаю взглянуть на Lodash; он имеет множество полезных функций.
Например pick()
будет именно то, что вы ищете:
var subset = _.pick(elmo, ['color', 'height']);
Если вы используете ES6, есть очень лаконичный способ сделать это, используя деструктуру. Разрушение позволяет легко добавлять объекты к объекту с использованием разворота, но также позволяет создавать подмножества объектов таким же образом.
const object = {
a: 'a',
b: 'b',
c: 'c',
d: 'd',
}
// Remove "c" and "d" fields 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), {});
редактировать: хороший пример для карри тоже! Сделайте так, чтобы функция 'pick' возвращала другую функцию.
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' },
// ]
Нет ничего подобного встроенной в базовую библиотеку, но вы можете использовать деструктуризацию объектов, чтобы сделать это...
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()
.
Я добавляю этот ответ, потому что ни один из ответов не использовал Comma operator
.
Это очень легко с destructuring assignment
и ,
оператора
const object = { a: 5, b: 6, c: 7 };
const picked = ({a,c} = object, {a,c})
console.log(picked);
Вы также можете использовать Lodash.
var subset = _.pick(elmo ,'color', 'height');
Дополняя, скажем, у вас есть массив "elmo" s:
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'); });
Еще одно решение:
var subset = {
color: elmo.color,
height: elmo.height
}
Это выглядит намного более читабельным для меня, чем практически любой ответ, но, возможно, это только я!
Разбиение на динамически именованные переменные невозможно в JavaScript, как обсуждалось в этом вопросе.
Чтобы динамически устанавливать ключи, вы можете использовать функцию Reduce без изменения объекта следующим образом:
const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
}
const subset = getSubset(elmo, 'color', 'annoying')
console.log(subset)
Используйте метод pick
из библиотеки lodash, если вы уже используете.
var obj = { 'a': 1, 'b': '2', 'c': 3 };
_.pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }
Еще один способ...
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]
Вы можете использовать эту функцию с массивом объектов =)
Это работает для меня в консоли Chrome. Любая проблема с этим?
var { color, height } = elmo
var subelmo = { color, height }
console.log(subelmo) // {color: "red", height: "unknown"}
Однострочник для произвольного списка выбранных ключей становится короче с каждым выпуском JS:
Object.keys(obj)
.filter(function (key) {
return ['foo', 'bar'].indexOf(key) >= 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
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), {});
ECMAScript 2017 имеет Object.entries
и Array.prototype.includes
, ECMAScript 2019 имеет Object.fromEntries
, они могут быть заполнены при необходимости и упростить задачу:
Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['foo', 'bar'].includes(key))
)
Как насчет:
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');
преобразовать аргументы в массив
используйте Array.forEach()
чтобы выбрать свойство
Object.prototype.pick = function(...args) {
var obj = {};
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 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}
);
Динамическое решение
['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 );
Решение 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));
}
Информация о наборе даже допускает автозаполнение:
Кредит на DefiniteTyped для U extends keyof T
!
Вот полное решение для 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']);
elmo.slice
был бы "плохим", потому что его нужно было бы положить в Object.prototype
, но вы могли бы сделать что-то вроде этого:
Object.slice = function(obj, props) {
var ret = {};
for(var i = 0, l = props.length; i < l; i++) {
var prop = props[i];
if(obj.hasOwnProperty(prop)) {
ret[prop] = obj[prop];
}
}
return ret;
}
function splice()
{
var ret = new Object();
for(i = 1; i < arguments.length; i++)
ret[arguments[i]] = arguments[0][arguments[i]];
return ret;
}
var answer = splice(elmo, "color", "height");
Старый 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);
Пытаться
const elmo={color:"red",annoying:!0,height:"unknown",meta:{one:"1",two:"2"}};
const {color, height} = elmo; newObject = ({color, height});
console.log(newObject); //{ color: 'red', height: 'unknown' }
Примечание: хотя исходный вопрос был задан для javascript, это может быть сделанный jQuery ниже решения
вы можете расширить jquery, если хотите, вот пример кода для одного фрагмента:
jQuery.extend({
sliceMe: function(obj, str) {
var returnJsonObj = null;
$.each( obj, function(name, value){
alert("name: "+name+", value: "+value);
if(name==str){
returnJsonObj = JSON.stringify("{"+name+":"+value+"}");
}
});
return returnJsonObj;
}
});
var elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
};
var temp = $.sliceMe(elmo,"color");
alert(JSON.stringify(temp));
вот скрипка для того же: http://jsfiddle.net/w633z/