gwar9
gwar9

Reputation: 1082

Combine array of objects inner array based on property

say I have a data structure that looks like this:

data = [
        {title: "Alpha", content: [{ // 1 item }]},
        {title: "Beta", content: [{ // 10 items }]},
        {title: "Beta", content: [{ // 12 items }]}, 
        {title: "Beta", content: [{ // 8 items }]} 
       ]

I'd like to figure out a reduce function that would return the following structure:

data = [
        {title: "Alpha", content: [{ // 1 item }]},
        {title: "Beta", content: [{ // 30 items }]}
       ]

I've tried looking at some answers where reduce is used based on title but it did not return the new structure with the accumulated content array such as:

data = data.reduce((x, e) => {
          x.push(
            Object.assign(
              {},
              e,
              messages.find((x) => x.title == e.title)
            )
          )
          return x
        }, [])

Any help would be great.

Upvotes: 1

Views: 177

Answers (3)

vishnu raj
vishnu raj

Reputation: 106

I made small changes from @user120242 code.

data = [
        {title: "Alpha", content: [{ 1:1 }]},
        {title: "Beta", content: [{2:2 }]},
        {title: "Beta", content: [{ 3:3}]}, 
        {title: "Beta", content: [{4:4}] }
       ]
let acc={};
data.forEach((x)=>{
  acc[x.title] = acc[x.title] || {title: x.title, content: []}
  acc[x.title].content = acc[x.title].content.concat(x.content)
}
)
console.log(acc)

Upvotes: 0

user120242
user120242

Reputation: 15268

You don't need to use .find, which has to search the entire array every iteration.

Using reduce and an object map to collect dupes, Object.values to unwrap object to array. spread syntax to feed as push arguments.

data = [
        {title: "Alpha", content: [{ 1:1 }]},
        {title: "Beta", content: [{2:2 }]},
        {title: "Beta", content: [{ 3:3}]}, 
        {title: "Beta", content: [{4:4}] }
       ]
console.log(
Object.values(data.reduce((acc,{title,content})=>{
  acc[title] ? 
    acc[title].content.push(...content) :
    acc[title] = { title, content }
  return acc
},{})
))

If IE compatible is needed, there is a polyfill on the above linked documentation. This will also work:

data = [
        {title: "Alpha", content: [{ 1:1 }]},
        {title: "Beta", content: [{2:2 }]},
        {title: "Beta", content: [{ 3:3}]}, 
        {title: "Beta", content: [{4:4}] }
       ]
function values(obj){
  return Object.keys(obj).map(k=>obj[k])
}
console.log(
values(data.reduce((acc,x)=>{
  acc[x.title] = acc[x.title] || {title: x.title, content: []}
  acc[x.title].content = acc[x.title].content.concat(x.content)
  return acc
},{})
))

Upvotes: 2

planet_hunter
planet_hunter

Reputation: 3966

This might help -

var data = [{
    title: "Alpha",
    content: [1, 2, 3, 4, 5]
  },
  {
    title: "Beta",
    content: [6, 7, 8, 9, 10]
  },
  {
    title: "Beta",
    content: [11, 12, 13, 14, 15]
  },
  {
    title: "Beta",
    content: [16, 17, 18, 19, 20]
  }
]

data = data.reduce((array, item) => {
  const existingItem = array.find(innerItem => innerItem.title === item.title);
  if (!!existingItem) {
    existingItem.content = [...existingItem.content, ...item.content];
  } else {
    array.push(item);
  }
  return array;
}, []);


console.log(data);

Upvotes: 2

Related Questions