0

Is there a way to group an array of objects by referring to certain value of the object in the array

let say I do have an array called

a=[
{name: "abc", address: "xxx"}
{name: "def", address: "yyy"}
{name: "xyz", address: "xyz"}
{name: "abc", address1: "123"}
{name: "def", address1: "456"}
]

I need to add the address of same name into one object and the new Array should look like as below

b =[
{name:"abc", address: "xxx", address1: "123"}
{name:"def", address: "yyy", address1: "456"}
{name: "xyz", address: "xyz"}
]
1
  • Maybe I've misunderstood your question. Is address1 intended? I thought you wanted to merge two arrays of objects where the key is name and you would want each address in the output merged array, suffixing each address with N (where N is 1, 2, 3...). Am I mistaken? Commented Nov 4, 2018 at 15:07

6 Answers 6

3

You can use the function reduce to group by name and the function Object.values to extract the grouped objects.

let a = [{name: "abc", address: "xxx"},{name: "def", address: "yyy"},{name: "xyz", address: "xyz"},{name: "abc", address1: "123"},{name: "def", address1: "456"}],
    result = Object.values(a.reduce((a, {name, ...rest}) => {
      a[name] = Object.assign(a[name] || {name}, rest);
      return a;
    }, Object.create(null)));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

3 Comments

I understand that Object.create(null) creates an object without a prototype, but what is the purpose of favoring it over the shorter {} here?
this was fast and exactly what i needed. Thanks
@slider this is only for that, a very vanilla dictionary/map with no prototype.
1

You can utilize Key value pairs to keep track of the values and use the delete method to remove values

Comments

0

I know you have a good answer already but I wanted to show you another way.

I would save it as one key instead of address1 and so forth and use his value as a list of all addresses.

So that your result would look like:

[ { name: 'abc', address: [ 'xxx', '123' ] },
  { name: 'def', address: [ 'yyy', '456' ] },
  { name: 'xyz', address: [ 'xyz' ] } ]

a=[
  {name: "abc", address: "xxx"},
  {name: "def", address: "yyy"},
  {name: "xyz", address: "xyz"},
  {name: "abc", address: "123"},
  {name: "def", address: "456"}
]

new_array = a.reduce((agg, item) => {
  const index = agg.findIndex(find_item => find_item.name === item.name)
  if (index !== -1) {
    agg[index].address.push(item.address)
  } else {
    agg.push({ name: item.name, address: [item.address] })
  }
  return agg
},[])

console.log(new_array)
/*
[ { name: 'abc', address: [ 'xxx', '123' ] },
  { name: 'def', address: [ 'yyy', '456' ] },
  { name: 'xyz', address: [ 'xyz' ] } ]
*/

Comments

0

You can use "Object.Values" and "Reduce" to achieve this as below

var a=[
    {name: "abc", address: "xxx"}
    , {name: "def", address: "yyy"}
    , {name: "xyz", address: "xyz"}
    , {name: "abc", address1: "123"}
    , {name: "def", address1: "456"}
]

var result = Object.values(a.reduce(
                            (o,d) => (
                                       o[d.name] = { ...d, ...(o[d.name] && o[d.name]) }
                                       , o
                                     )
                            , {})
                           )

console.log(result)

Comments

0

This is a functional approach to solve the whole problem. See comments to understand how it works!

const a = [{
  name: "abc",
  address: "xxx"
}, {
  name: "def",
  address: "yyy"
}, {
  name: "xyz",
  address: "xyz"
}, {
  name: "abc",
  address: "123"
}, {
  name: "def",
  address: "456"
}]
    
const map = f => xs => xs.map (f)
    
const pipe = xs => x => 
      xs.reduce ((x, f) => f (x), x)

const objectFromEntries = xs => 
      xs.reduce ((o, [prop, value]) => ({ ...o, [prop]: value }), {})
      

const groupBy = prop => xs => 
      xs.reduce ((o, x) => 
         ({ ...o, [x[prop]]: [...(o[x[prop]] || []), x] })
      , [])
    
const addressPairs = map (({ address }, i) => 
      [`address${i + 1}`, address]
)
    
const organizeByName = pipe ([
   groupBy ('name'), // (1) Groups by name the whole objects
   Object.entries, // (2) Turns groups into pairs
   map (([name, xs]) => // (3) Flattens group entry's addresses
       [['name', name], ...addressPairs (xs)]
   ),
   map (objectFromEntries) // (4) Turns each flattened group into an object
])

const output = organizeByName (a)

console.log (output)

Comments

0

This the answer using Rxjs:

import { from } from 'rxjs';
import { groupBy, mergeMap, toArray, map, reduce } from 'rxjs/operators';

interface IPerson{
    name: string;
    address?: string;
    address1?: string;
}

let a:IPerson[]=[
        {name: "abc", address: "xxx"},
        {name: "def", address: "yyy"},
        {name: "xyz", address: "xyz"},
        {name: "abc", address1: "123"},
        {name: "def", address1: "456"},
    ]


let personSource = from(a);

personSource.pipe(
        groupBy(x => x.name),
        mergeMap(g => g.pipe(
            reduce((acc:IPerson, val:IPerson) => {
                acc.name = val.name;
                if(val.address1) acc.address1 = val.address1;
                if(val.address) acc.address = val.address;

                return acc;
            })    
        ))
    )
    .subscribe(y => {
        console.log(y);
    })

Comments

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.