10

Suppose there are two objects.

const a = [
  { id: '1-1-1', name: 'a111' },
  { id: '1-1-2', name: 'a112' },
  { id: '1-2-1', name: 'a121' },
  { id: '1-2-2', name: 'a122' },
  { id: '2-1-1', name: 'a211' },
  { id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']

and the result

  {

      '1-1':[
        { id: '1-1-1', name: 'a111' },
        { id: '1-1-2', name: 'a112' },
      ],
      '1-2':[
          { id: '1-2-1', name: 'a121' },
          { id: '1-2-2', name: 'a122' },
      ],
      '2-1':[
        { id: '2-1-1', name: 'a211' },
        { id: '2-1-2', name: 'a212' },
      ]
    }

Basically, I want to group the data.

I use includes to check if the item from b to match the id from a. Then construct the new array.

This is my attempt(fiddle):

return b.map(item => a.map(jtem => {
  if(jtem.id.includes(item)){
    return {
      [item]: jtem
    }
  }
}))

For somehow, it doesn't work.

and, is there a clever way to avoid the nested for loop or map function?

4
  • Shouldn't the result be an object? Commented Mar 25, 2019 at 7:02
  • @JackBashford Hey man, sry, u r right, I just updated it. Commented Mar 25, 2019 at 7:08
  • Do you really want includes? I'd recommend startsWith Commented Mar 25, 2019 at 8:26
  • @Bergi thx, I think startWith is better Commented Mar 26, 2019 at 0:30

1 Answer 1

13

You can do that in following steps:

  • Apply reduce() on the array b

  • During each iteration use filter() on the the array a

  • Get all the items from a which starts with item of b using String.prototype.startsWith()
  • At last set it as property of the ac and return ac

const a = [
  { id: '1-1-1', name: 'a111' },
  { id: '1-1-2', name: 'a112' },
  { id: '1-2-1', name: 'a121' },
  { id: '1-2-2', name: 'a122' },
  { id: '2-1-1', name: 'a211' },
  { id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']

let res = b.reduce((ac,b) => {
  
  ac[b] = a.filter(x => x.id.startsWith(b));
  return ac;

},{})
console.log(res)

As suggested by @Falco is the comments that It would be better to scan over the a once as its large. So here is that version.Actually its better regarding performance

const a = [
  { id: '1-1-1', name: 'a111' },
  { id: '1-1-2', name: 'a112' },
  { id: '1-2-1', name: 'a121' },
  { id: '1-2-2', name: 'a122' },
  { id: '2-1-1', name: 'a211' },
  { id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']


let res = a.reduce((ac,x) => {
  let temp = b.find(y => x.id.startsWith(y))
  if(!ac[temp]) ac[temp] = [];
  ac[temp].push(x);
  return ac;
},{})

console.log(res)

Note: startsWith is not supported by I.E. So you can create polyfill using indexOf

if(!String.prototype.startWith){
  String.prototype.startsWith = function(str){
    return this.indexOf(str) === 0
  }
}

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

3 Comments

While it is specifically said in the question that the op wants to use es6, and that IE don't support es6 features, I just want to mention that startsWith() don't work in IE (while reduce, filter, and setting a property of an object is totally fine if IE > 9) and if someone wants to do the same thing that startsWith do, they can implment their own with some substring :)
For big a and small b I would probably go with a.reduce(...) because of locality and only scan over the big array once.
@MaheerAli Thank you - here is a benchmark comparing the two :-) jsbench.me/dfjtoadysr/1

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.