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"}
]
str
  • 42,689
  • 17
  • 109
  • 127
Phani
  • 189
  • 1
  • 3
  • 14
  • 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? – Matías Fidemraizer Nov 04 '18 at 15:07

6 Answers6

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; }
Ele
  • 33,468
  • 7
  • 37
  • 75
  • I understand that `Object.create(null)` creates an object without a prototype, but what is the purpose of favoring it over the shorter `{}` here? – slider Nov 02 '18 at 15:16
  • this was fast and exactly what i needed. Thanks – Phani Nov 02 '18 at 15:16
  • 1
    @slider this is only for that, a very vanilla dictionary/map with no prototype. – Ele Nov 02 '18 at 15:22
1

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

izdo
  • 11
  • 3
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' ] } ]
*/
omri_saadon
  • 10,193
  • 7
  • 33
  • 58
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)
Nitish Narang
  • 4,124
  • 2
  • 15
  • 22
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)
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
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);
    })
Irwansyah
  • 21
  • 2