2

I've stumbled several times already upon the following problem:

Let's assume we have the following object:

const book = {
  "title" : "The Pillars of the Earth",
  "author" : "Ken Follet",
  "year" : 1989,
  "genres" : ["fiction", "historical"]
}

I would like to extract on-the-fly only part of the information from the object. Obviously, I cannot use the Object.assign because it's going to copy all the properties. The fastest, but not very elegant way of doing it I've found so far is destructuring and then the concise syntax for creating an object, for example:

let {title, author} = book;
let displayInfo = {title, author};

It works, though it's not very elegant, mostly because as a side-effect I've just created the title and author variables, which I probably don't really need.

Is there a smarter, more concise syntax for this operation? Like a secret one-liner?

Thank you in advance!

Ismael
  • 67
  • 1
  • 7

3 Answers3

3

In terms of destructuring, it doesn't get a lot better:

let displayInfo = {};
({title: displayInfo.title, author: displayInfo.author} = book);

Live Example:

const book = {
  "title" : "The Pillars of the Earth",
  "author" : "Ken Follet",
  "year" : 1989,
  "genres" : ["fiction", "historical"]
};

let displayInfo = {};
({title: displayInfo.title, author: displayInfo.author} = book);

console.log(displayInfo);

But you can give yourself a helper function:

function pick(source, ...props) {
    const obj = {};
    for (const prop of props) {
        obj[prop] = source[prop];
    }
    return obj;
}

then it's:

let displayInfo = pick(book, "title", "author");

Live Example:

const book = {
  "title" : "The Pillars of the Earth",
  "author" : "Ken Follet",
  "year" : 1989,
  "genres" : ["fiction", "historical"]
};

function pick(source, ...props) {
    const obj = {};
    for (const prop of props) {
        obj[prop] = source[prop];
    }
    return obj;
}

let displayInfo = pick(book, "title", "author");

console.log(displayInfo);
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
2

One option is to combine the rest operator with destructuring to pluck out the properties you don't want, leaving the rest of them in your new object.

const book = {
  "title" : "The Pillars of the Earth",
  "author" : "Ken Follet",
  "year" : 1989,
  "genres" : ["fiction", "historical"]
}

const { year, genres, ...displayInfo } = book;

console.log(displayInfo);
Nick
  • 12,032
  • 3
  • 11
  • 27
  • This works and I have no idea why. Why does this work? – Seth Lutske Mar 10 '20 at 23:20
  • 1
    It assigns `book.year` into `year`, `book.genres` into `genres`, and then the rest into `displayInfo` – Nick Mar 10 '20 at 23:20
  • Makes sense now, thank you. Very elegant. – Seth Lutske Mar 10 '20 at 23:22
  • Hey Nick, thanks for the answer :) This way of dealing with the problem still creates the `year` and `genres` variables. Moreover, you specify the properties which you don't need, which probably obscures the intention of the operation. And last but not least, my practice showed that normally I need to extract only a small part of a big object, so specifying everything I don't need unfortunately isn't the fastest way :( – Ismael Mar 10 '20 at 23:23
  • 1
    @Ismael fair enough! In that case, I'd recommend one of the "pick" function approaches recommended by one of the other posters. That's probably the approach I would take. – Nick Mar 10 '20 at 23:24
1

A handy one-liner function using Array.prototype.reduce.

const cherryPick = (obj, attrToPick) => attrToPick.reduce((a, e) => ((a[e] = obj[e]), a), {});

Usage

const book = {
  "title" : "The Pillars of the Earth",
  "author" : "Ken Follet",
  "year" : 1989,
  "genres" : ["fiction", "historical"]
}

const attrToPick = ['title', 'author'];

cherryPick(book, attrToPick);

// result
// {
//   "title": "The Pillars of the Earth",
//   "author": "Ken Follet"
// }
GiorgiosJames
  • 578
  • 2
  • 8