Недавно у нас появилось требование в нашем NodeJS API — возвращать массив объектов, каждый из которых был подмножеством исходного объекта.

Если это звучит загадочно, пример поможет проиллюстрировать.

Наши исходные данные были примерно такими.

[ {
    "id": 1,
    "name": "testData1",
    "value": "CONSTANT",
    "config": "rootEVAL",
    "status": "Active",
    "Desc": "This is description of test data 1"
  },{
    "id": 2,
    "name": "testData2",
    "value": "MULTIPLY",
    "config": "rootEVAL",
    "status": "InActive",
    "Desc": "This is a description of test data 2"
  },
...
]

У нас было два API — один для получения list данных, а другой для заданного id возвращал конкретный object.

Для list мы не хотели возвращать все поля каждого объекта, а только подмножество. В этом случае,

[{
   "id": 1,
   "name": "testData1",
   "status": "Active",
 }, {
   "id": 2,
   "name": "testData2",
   "status": "InActive",
 },
...
]

Теперь, как нам получить подмножество объектов — точнее, массив подмножеств данных?

Javascript такой, какой он есть, он предоставляет ряд различных опций.

Быстрый поиск в stackoverflow дает нам среди прочего эту ссылку. Мы можем использовать старый добрый цикл for, метод reduce с служебными методами filter, lodash и так далее.

В нашем случае нам также необходимо иметь массив таких объектов.

Решение, которое мы в итоге использовали, оказалось довольно простым, но элегантным.

// res being nodejs response object
res.json(data.map(({id, name, status}) => ({id, name, status})))

data является исходным массивом

На первый взгляд выглядит забавно, особенно одинаковые поля с обеих сторон с несколькими скобками в придачу. Эта средняя статья отчасти проливает свет на синтаксис.

Мы хотим

data.map(item => {
  return {
    id: item.id,
    name: item.name,
    status: item.status
  }
}

По сути, для каждого item в массиве data возвращайте объект, который имеет поля id, name и status.

Теперь мы также знаем, что нам не нужно использовать return для однострочной функции. Например

const add = (a, b) => a + b
console.log(add(2,3))
// Output: 5

Это нормально для примитивных типов данных. Но как это работает для объектов?

const employee = (name, age) => { name, age }
console.log(employee("Jack Sparrow", 35))
// Output: undefined

Да, это работает, если мы предоставим return

const employee = (name, age) => {return { name, age };}
console.log(employee("Jack Sparrow", 35))
// Output: {name: 'Jack Sparrow', age: 35}

Но мы хотим избежать оператора return.

Здесь нам на помощь приходит скобка ().

Так вот этот работает

const employee = (name, age) => ({ name, age })
console.log(employee("Jack Sparrow", 35))
// Output: {name: 'Jack Sparrow', age: 35}

Итак, это объясняет правую часть нашего кода.

data.map(({id, name, status}) => ({id, name, status}))

Если вы посмотрите на нашу правую часть, мы делаем сокращенные имена свойств, а не

{id: id, name: name, status: status}

Если мы повторим каждый элемент data, используя объект, скажем, item, то мы запишем это как

data.map(item => ({id: item.id, name: item.name, status; item.status})

Вот где проявляется object dereferencing особенность javascript. Мы можем извлечь из объекта только релевантные поля с помощью

{ id, name, status} = item

Как только мы это сделаем, имя поля и имя переменной станут одинаковыми. Поэтому можно использовать сокращенное имя свойства.

data.map(({id, name, status}) => ({id, name, status}))

Виола! Теперь мы можем получить массив, содержащий подмножество полей нашего исходного объекта!