bier hier
bier hier

Reputation: 22520

How to convert array to object by key?

Suppose I got this array:

const users =[ 
   { 
      id:1,
      name:'bob',

   },
   { 
      id:2,
      name:'sally',

   },
   { 
      id:3,
      name:'bob',
      age:30,

   }
];

And I want to use any key(in this case 'name' ) to return an object :

{ 
   bob:[ 
      { 
         id:1,
         name:'bob',

      },
      { 
         id:3,
         name:'bob',
         age:30,

      }
   ],
   sally:[ 
      { 
         id:2,
         name:'sally',

      }
   ],
}

I tried this:

const go = (A,key) =>{
    return A.reduce((o, key) => ({ ...o, [key]:o }), {})
}

export default go;

But this returns:

{ '[object Object]': { '[object Object]': { '[object Object]': {} } } }

If the key is not present omit from the result. It should not mutate the original array though. How can I perform this kind of conversion?

Upvotes: 0

Views: 74

Answers (2)

skovy
skovy

Reputation: 5650

You were close but the key attribute in this case was each value (eg: { id: 1, name: 'bob' }) so the string representation is [object Object] which is why all the keys are that. Based off what you said, you want to use key.name as the property and set it's value as [key]. (I renamed key to arr in my example since it's the array value).

So this would be something like { ...o, [arr.name]: [arr] }

Because there can be an existing value, it adds a bit of complexity which is what [...(obj[arr.name] || []), arr] is doing. It's looking up the existing value (or defaulting to an empty array) and spreading those values and adding the new value.

const users = [{
    id: 1,
    name: 'bob',

  },
  {
    id: 2,
    name: 'sally',

  },
  {
    id: 3,
    name: 'bob',
    age: 30,

  }
];


const transform = (input, keyName) => {
  return input.reduce((obj, arr) => ({ ...obj,
    [arr[keyName]]: [...(obj[arr[keyName]] || []), arr]
  }), {})
}

console.log(transform(users, 'name'))
console.log(transform(users, 'id'))

Upvotes: 0

Robby Cornelissen
Robby Cornelissen

Reputation: 97120

With the approach you have, a new array is not instantiated in case the key is not yet present in the object.

This will work:

const result = users.reduce((a, v) => {
  a[v.name] = a[v.name] || [];
  a[v.name].push(v);
  return a;
}, {});

Complete snippet wrapping this logic in a function:

const users = [{
  id: 1,
  name: 'bob',

}, {
  id: 2,
  name: 'sally',

}, {
  id: 3,
  name: 'bob',
  age: 30,

}];

const go = (input, key) => input.reduce((a, v) => {
  a[v[key]] = a[v[key]] || [];
  a[v[key]].push(v);
  return a;
}, {});

console.log(go(users, 'name'));


If you really want to cram it into a one-liner, this will also work, by either spreading the already existing array, or an empty one:

const result = users.reduce((a, v) => ({...a, [v.name]: [...a[v.name] || [], v]}), {});

Complete snippet wrapping this logic in a function:

const users = [{
  id: 1,
  name: 'bob',

}, {
  id: 2,
  name: 'sally',

}, {
  id: 3,
  name: 'bob',
  age: 30,

}];

const go = (input, key) => input.reduce((a, v) => ({...a, [v[key]]: [...a[v[key]] || [], v]}), {});

console.log(go(users, 'name'));

Upvotes: 1

Related Questions