2

I have an array of objects and I would like to convert it into a different array. Each object in the original array has a category key and I would like the final result to group objects by category. I am trying to use the reduce method to do this but can not make sense of the examples I have found.

original array:

[
  {category: "film", title: "toy story"}, 
  {category: "film", title:"harry potter"},
  {category: "tv", title:"seinfeld"}
]

desired result:

[
  {
    category: "film",
    children: [
      {title: "toy story"}, 
      {title: "harry potter"}
    ],
  }
  {
    category: "tv",
    children: [
      {title: 'seinfeld' }
    ]
  }
]

I am trying to use d3 to create some graphs and the data needs to be sorted in a hierarchical structure. More on that here, https://github.com/d3/d3-hierarchy/blob/v1.1.9/README.md#hierarchy

dave
  • 2,199
  • 1
  • 16
  • 34
Sam Roehrich
  • 123
  • 1
  • 6
  • Does this answer your question? [Most efficient method to groupby on an array of objects](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) – frodo2975 Apr 26 '20 at 21:28
  • 1
    Your "desired" result won't work: you need an **object**, not an array, and you need a root node in that object. – Gerardo Furtado Apr 27 '20 at 00:09
  • This is a common thing that databases do, it is called grouping. If it helps, a similar problem has been addressed [in this question](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) – Bruno Ribarić Apr 26 '20 at 21:25
  • @GerardoFurtado can you offer some advice on how to convert this to a format d3 will accept? – Sam Roehrich Apr 27 '20 at 01:51
  • Ask it as a new question, comment section is not the place to provide solutions. Ask what you want, not how to achieve what you think the solution is. – Gerardo Furtado Apr 27 '20 at 03:49

2 Answers2

0

You can use a reduce function:

const arr = [
  {category: "film", title: "toy story"},
  {category: "film", title: "harry potter"},
  {category: "tv", title: "seinfeld"}
]

const arrByCategory = arr.reduce((acc, i) => {
  // Check if the category already exist in the new array
  const elIdx = acc.findIndex(_ => _.category === i.category);
  const elExist = elIdx > -1;

  if(elExist) {
    // If the category exist, we just add the title to the children list
    return acc[elIdx].children.push({title: i.title})

  } else {
    // If the category does not exist we create it and add the initial title in the children list
    return acc.concat({
      category: i.category,
      children: [{ title: i.title }]
    })
  }
},[])

To give you a better understanding of the reduce function, here is a simple example:

const array = [1, 3, 6, 2, 5]
const sum = array.reduce((acc, i) => {
  return acc + i;
}, 0)

console.log(sum) // 17

Simon Bruneaud
  • 2,263
  • 2
  • 12
  • 24
0

Use reduce function, first try to look if the item already exists in the array if yes, use the found index to locate the element and append to its children, if not add the initial item to the children array.

const input = [{
    category: "film",
    title: "toy story"
  },
  {
    category: "film",
    title: "harry potter"
  },
  {
    category: "tv",
    title: "seinfeld"
  }
]




const result = input.reduce((acc, x) => {
  const index = acc.findIndex(y => y.category === x.category)
  if (index >= 0) {
    acc[index].children.push({
      title: x.title
    })
  } else {
    acc = [...acc, {
      category: x.category,
      children: [{
        title: x.title
      }]
    }]
  }
  return acc;
}, []);

console.log(result)

For not having child duplicates:

const result = input.reduce((acc, x) => {
  const index = acc.findIndex(y => y.category === x.category)
  const indexTitle = acc[index] && acc[index].children.findIndex(y => y.title === x.title)
  if (index >= 0) {
    if (indexTitle !== 0) {
      acc[index].children.push({
        title: x.title
      })
    }
  } else {
    acc = [...acc, {
      category: x.category,
      children: [{
        title: x.title
      }]
    }]
  }
  return acc;
}, []);
EugenSunic
  • 13,162
  • 13
  • 64
  • 86
  • @eugenesunic Thanks this code worked exactly as I wanted. I have another question if you dont mind. How could I expand on this so that if there are duplicates in a category, for example seinfled appears twice in the tv category, seinfeld would only get added once to the array a value property existed in the object and that value would be increased by 1? – Sam Roehrich Apr 27 '20 at 01:52