3
\$\begingroup\$

What would be the "correct" way of transforming an array of objects into an object that contains an array?

Suppose I have this array that contains objects:

const paintings = [
  { painting: "Mona Lisa" },
  { painting: "The Starry Night" },
  { painting: "The Last Supper" },
  { painting: "Girl with a Pearl Earring" },
  { painting: "American Gothic" },
  { painting: "The Night Watch" }
];

And I would like to transform it into this:

const paintings = {
  artworks: [
    "Mona Lisa",
    "The Starry Night",
    "The Last Supper",
    "Girl with a Pearl Earring",
    "American Gothic",
    "The Night Watch"
  ]
};

I know that I can accomplish this with two nested loops like below, but is there a more "elegant" way of doing it? I think that these tasks are nowadays usually solved using map or reduce functions but I find them a bit confusing to use. I would like to learn though!

const paintingsNew = {
  artworks: []
};

for (const painting of paintings) {
  for (const val of Object.values(painting).values()) {
    paintingsNew.artworks.push(val);
  }
}

console.log(paintingsNew);

// { artworks:
//    [ 'Mona Lisa',
//      'The Starry Night',
//      'The Last Supper',
//      'Girl with a Pearl Earring',
//      'American Gothic',
//      'The Night Watch' ] }
\$\endgroup\$
  • \$\begingroup\$ artworks maps every {painting:"MonaLisa", artist: ...} to its painting. In Computer Science a projection. \$\endgroup\$ – Joop Eggen Jan 29 at 11:13
5
\$\begingroup\$

Your code flattens the entire array of {} objects, indiscriminately flattening all properties. It does not mind, if there is only one single property, but it does not make sense semantically, especially if the array is named paintings.

Rather consider the resulting array a projection/mapping to a single property painting.

For a consistent array check and take only the painting property.

for (const p of paintings) {
  if ("painting" in p) {
    paintingsNew.artworks.push(p.painting);
  }
}

The extra if protects against undefined entering corrupted data.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

If you want to use Array.prototype.map you can write something like this:

const paintings = [
  { painting: "Mona Lisa" },
  { painting: "The Starry Night" },
  { painting: "The Last Supper" },
  { painting: "Girl with a Pearl Earring" },
  { painting: "American Gothic" },
  { painting: "The Night Watch" }
];

const paintingsNew = {
  artworks: []
};

paintings.map(x => {
  paintingsNew.artworks.push(x);
});

When you use map it practically iterates over every item of the array. We have to pass a callback function accepting the currentValue (here the x argument), and optionally the index and the array.

You can (and normally should) return some calculated value in the callback function. Since here everything you want is to create a new object containing the array, you push each item to the array inside the object paintingsNew. You don't even need map for this, you can just write:

const paintingsNew = {
  artworks: paintings // paintings is defined before
};

However I suppose you'd have some further requirements, which you could work out inside the callback function of the map. As suggested by @Joop Eggen, you could check if the key painting is present in the object:

paintings.map(x => {
  if ('painting' in x) {
    paintingsNew.artworks.push(x);
  }
});

Now this would make more sense when using map. You could certainly apply some more advanced logic if your initial array is more diverse and you'd like to create an output object with more complex structure.

EDIT: Immediatelly after posting I saw you want to get only the value for the key painting. This means in the map callback you push x.painting instead of just x.

paintings.map(x => {
  if ('painting' in x) {
    paintingsNew.artworks.push(x.painting);
  }
});
| improve this answer | |
\$\endgroup\$
  • 6
    \$\begingroup\$ You are misusing map. It is for creating a new array like this: const newPaintings = paintings.map(x => x.painting), not to execute side-effects. You should be using .forEach(). \$\endgroup\$ – RoToRa Jan 29 at 14:41
  • 1
    \$\begingroup\$ @RoToRa thank you for the comment. You're right, .forEach() is more appropriate for this. I used .map() because the OP stated in the question he/she wanted to use map or reduce. \$\endgroup\$ – cezar Jan 29 at 14:43
  • \$\begingroup\$ if you edit your answer to not suggest calling .push() inside the callback to .map() then I would consider changing my vote... \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Feb 28 at 20:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.