0

I have an array of objects that looks like:

const arr = [
    {
        "name": "Day",
        "value": "Monday"
    },
    {
        "name": "Month",
        "value": "November"
    },
    {
        "name": "Day",
        "value": "Monday"
    },
    {
        "name": "Month",
        "value": "December"
    },
    {
        "name": "Day",
        "value": "Friday"
    },
    {
        "name": "Month",
        "value": "November"
    },
    {
        "name": "Day",
        "value": "Friday"
    },
    {
        "name": "Month",
        "value": "December"
    }
]

but I'm needing to modify my array of objects to:

const final = [
    {
        "name": "Day",
        "values": [
            "Monday",
            "Friday"
        ]
    },
    {
        "name": "Month",
        "values": [
            "November",
            "December"
        ]
    }
]

I can filter out the duplicates with:

const filtered = arr.filter(
    (v, i, a) =>
      a.findIndex(v2 => v?.name === v2?.name && v?.value === v2?.value) === i
  );

but I'm stuck on the approach on an efficient way to do this without Lodash or underscore and I've tried using Object.assign() but my implementation isn't working:

const test = filtered.map(({ name, value }) =>
    Object.assign({ name, value: [] }, (name, value))
  );
console.log(test)

Thought I had seen this asked before and tested answers from:

In an array of objects and can I combine the objects with the same name but have the value in an array of strings?

3
  • After you've removed your duplicates, you probably want to have a look at reduce rather than map, where you build up a new array as you iterate over each element. Although given what your final data looks like, I'm not sure an array is the right choice. Having a final object with days and months keys that each point to an array of strings would be far easier to use in downstream code. Commented Nov 7, 2023 at 17:54
  • the issue is the final result in the question is what's expected downstream or I'd do the approach you referred. Commented Nov 7, 2023 at 17:57
  • Then reduce will get you there, and Set will help remove the need to filter anything. Commented Nov 7, 2023 at 18:07

1 Answer 1

0

Use reduce with a start value that has your days and moths elements preallocated, using a Set to automatically ignore duplicates, and then after you've reduced everything, converting that set into an array:

const arr = [{
    name: "Day",
    value: "Monday",
  },{
    name: "Month",
    value: "November",
  },{
    name: "Day",
    value: "Monday",
  },{
    name: "Month",
    value: "December",
  },{
    name: "Day",
    value: "Friday",
  },{
    name: "Month",
    value: "November",
  },{
    name: "Day",
    value: "Friday",
  },{
    name: "Month",
    value: "December",
}];

// reduce the data to the form you need:
const final = arr.reduce(
  (resultSoFar, {name, value}) => {
    // find the "bin" to add this value to:
    let bin = resultSoFar.find((e) => e.name === name);
    // or make a new bin if there isn't one:
    if (!bin) resultSoFar.push(bin = { name, values: new Set() });
    // then add our value, and return the new result so far.
    bin.values.add(value);
    return resultSoFar;
  },
  // and start with `resultSoFar` being an empty array:
  []
);

// as last step, convert the sets into plain arrays:
final.forEach(e => e.values = [...e.values]);

// what did we get?
console.log(final);

Sign up to request clarification or add additional context in comments.

5 Comments

that works and interesting approach with reduce going to study this and learn more about it. Think Im somewhat confused on what's resultSoFar.
Also could the new Set be modified to not have to convert down the pipe in the forEach if I'm understanding the approach correctly? Is there a reason for the implementation of the new Set() compared to other approaches?
ah I see from the recent comment that new Set removes the need for filtering.
You said that the form you showed for your const final was specifically what you needed, so the forEach just hard-converts the Set to an Array, since Sets don't "have" indices, and don't support bracket notation.
As for resultSoFar, that's the key aspect of reduce: it starts with whatever you specified as second argument of your reduce call, and at each iteration step your return value becomes the next iteration step's resultSoFar, with the last iteration step's return being what reduce itself returns.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.