0

I have an existing array of objects which share a property titled type like so

[
 {
   id: 1,
   name: 'a',
   type: 'foo',
 },{
   id: 2,
   name: 'b',
   type: 'bar',
 },{
   id: 3,
   name: 'c',
   type: 'fizz',
 },{
   id: 4,
   name: 'd',
   type: 'foo',
 },
]

I need to be able to structure a new array of objects with the existing ones grouped together by type like this

[
 {
  type: 'foo',
  groups: [
    {
      id: 1,
      name: 'a',
      type: 'foo',
    },{
      id: 4,
      name: 'd',
      type: 'foo',
    },
  ]
 },{
  type: 'bar',
  groups: [
    {
      id: 2,
      name: 'b',
      type: 'bar',
    }
  ]
 },{
  type: 'fizz',
  groups: [
    {
      id: 3,
      name: 'c',
      type: 'fizz',
    }
  ]
 }
]

this is what I have so far but im not able to create the new array and organize the objects by type, only able to grab the type itself any help would be greatly appreciated!

Observable.value.map(objects => {
    typesArr = [...new Set(objects.data.map(object => object.type))];
}); /// output = ['foo', 'bar', 'fizz']

1 Answer 1

3

Reduce the array to a Map, using the type as the key. Use Array.from() to convert the Map's values iterator to an array:

const arr = [{"id":1,"name":"a","type":"foo"},{"id":2,"name":"b","type":"bar"},{"id":3,"name":"c","type":"fizz"},{"id":4,"name":"d","type":"foo"}]

const result = Array.from(arr.reduce((acc, o) => {
  const type = o.type
  if(!acc.has(type)) acc.set(type, { type, groups: [] })
  
  acc.get(type).groups.push(o)
  
  return acc
}, new Map()).values())

console.log(result)

To make TS infer the type of the grouped array, infer the type of an item from the original array, and use it to set the type of the Map (TS playground):

const arr = [{"id":1,"name":"a","type":"foo"},{"id":2,"name":"b","type":"bar"},{"id":3,"name":"c","type":"fizz"},{"id":4,"name":"d","type":"foo"}]

type Item = (typeof arr)[0]

const result = Array.from(arr.reduce((acc, o) => {
  const type = o.type
  if(!acc.has(type)) acc.set(type, { type, groups: [] })
  
  acc.get(type)!.groups.push(o)
  
  return acc
}, new Map<string, { type: Item['type'], groups: Item[] }>()).values())

console.log(result)

Another option is to reduce the array to a map of groups [type, object], and then use Array.from() to convert the Map's entries to the required form (TS playground):

const arr = [{"id":1,"name":"a","type":"foo"},{"id":2,"name":"b","type":"bar"},{"id":3,"name":"c","type":"fizz"},{"id":4,"name":"d","type":"foo"}]

const result = Array.from(
  arr.reduce((acc, o) => {
    const type = o.type
    if(!acc.has(type)) acc.set(type, [])

    acc.get(type).push(o)

    return acc
  }, new Map()),
  ([type, groups]) => ({ type, groups })
)

console.log(result)

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

2 Comments

well done sir much appreciated, I didnt think to approach this using reduce and creating a type based of the original arr via its first object will help me in many instances with this current feature you-are-the-best-nacho-libre.gif
You're welcome :) I've added another variation on the same idea.

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.