2

I have this array

var arr1 = [{id: 1, name: "lorem"}, {id: 1, name: "ipsum"}, {id: 2, name: "dolor"}]

as you can see here the first 2 indexs they got same id, I want the ouput to be something like this

newArr

[
  {
    id: 1,
    name: ["lorem", "ipsum"]
  },
  {
    id: 2,
    name: "dolor"
  }
]
elreeda
  • 4,525
  • 2
  • 18
  • 45
Manuel
  • 29
  • 1

2 Answers2

6

I like this solution better because it abstracts away the collation but allows you to control how items are collated using a higher-order function.

Notice how we don't say anything about x.id or x.name or names.concat([name]) inside collateBy. This procedure has no knowledge of the kind of data you might be collating.

// generic collation procedure
const collateBy = f => g => xs => {
  return xs.reduce((m,x) => {
    let v = f(x)
    return m.set(v, g(m.get(v), x))
  }, new Map())
}

// reusable collateById procedure
const collateById = collateBy (x => x.id)

// custom concatNames procedure
// note use of `{name:[]}` which is the "seed" value for an empty collation
const concatNames = xs=> {
  let collation = collateById ((a={name:[]}, b) =>
    Object.assign(a, b, { name: [...a.name, b.name] })
  ) (xs)
  return Array.from(collation.values())
}

// sample data
let arr1 = [
  {id: 1, name: "lorem"},
  {id: 1, name: "ipsum"},
  {id: 2, name: "dolor"}
]

console.log(concatNames (arr1))

Higher order functions demonstrate how powerful generic procedures likes collateBy can be. Here's another example using the exact same collateBy procedure but performing a very different collation

const collateBy = f => g => xs => {
  return xs.reduce((m,x) => {
    let v = f(x)
    return m.set(v, g(m.get(v), x))
  }, new Map())
}

const collateEvenOdd = collateBy (x => x % 2 === 0 ? 'even' : 'odd')

const sumEvenOdd = collateEvenOdd ((a=0, b) => a + b)

let data = [2,3,4,5,6,7]
let collation = sumEvenOdd (data)
let even = collation.get('even')
let odd = collation.get('odd')

console.log('even sum', even) // 2 + 4 + 6 === 12
console.log('odd sum', odd)   // 3 + 5 + 7 === 15
Mulan
  • 129,518
  • 31
  • 228
  • 259
2

Using reduce and map, you can quite easily convert it to what you wanted

var arr1 = [{id: 1, name: "lorem"}, {id: 1, name: "ipsum"}, {id: 2, name: "dolor"}];

var obj = arr1.reduce((a,b) => (b.id in a?a[b.id].push(b.name):a[b.id]=[b.name],a),{}),
    newArr = Object.keys(obj).map( id => ({id, name : obj[id]}));

console.log(newArr);
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • 2
    Unfortunately that ends up with nothing in array index `0`. Since this is tagged ES6, why not use a proper `Map` instead of that array? – Bergi Aug 09 '16 at 19:43