Как получить подмножество свойств объекта 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))
)

Я предлагаю взглянуть на Лодаш; в ней много полезных функций.

Например pick()будет именно то, что вы ищете:

var subset = _.pick(elmo, ['color', 'height']);

играть на скрипке

Если вы используете 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 }

https://lodash.com/docs/4.17.10#pick

Массив объектов

      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"}
  1. преобразовать аргументы в массив

  2. использование 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' }
Другие вопросы по тегам