Add values to object in JS when using groupBy

71
June 16, 2019, at 2:20 PM

I am using mapValues and groupBy to group and create keys when two items have the same typeId.

For example the original data is

{
    "Name": "One",
    "typeId": 1
},
{
    "Name": "Two",
    "typeId": 2
},
{
    "Name": "One Two",
    "typeId": 1
},
{
    "Name": "Three",
    "typeId": 3
},
{
    "Name": "Three Two",
    "typeId": 3
}

and by using groupBy I group the objects with the matching typeId and omit the typeId value from the `objects as...

const GroupedTypes = Object.entries(
   _.mapValues(_.groupBy(data, 'typeID'), rlist =>
      rlist.map(type => _.omit(type, 'typeID'))
   )
);

Returning as intended...

[
    "1",
    [
      {
        "Name": "One",
      },
      {
        "Name": "One Two",
      }
    ]
  ],
  [
    "2",
    [
      {
        "Name": "Two",
      }
    ]
  ],
  [
    "3",
    [
      {
        "Name": "Three",
      }
    ],
    [
      {
        "Name": "Three Two",
      }
    ]
]

However, I would also like to add an object for Name from the value Name in the first 0 object. Thus ending up with something like..

[
    "1",
    "One",
    [
      {
        "Name": "One",
      },
      {
        "Name": "One Two",
      }
    ]
],
[
    "2",
    "Two",
    [
      {
        "Name": "Two",
      }
    ]
],
[
    "3",
    "Three",
    [
      {
        "Name": "Three",
      }
    ],
    [
      {
        "Name": "Three Two",
      }
    ]
]

I've been looking through the lodash documentation and cannot find a method that works. How can I achieve this?

Answer 1

Just add another map with splice.

const FilterGuests = [{ 
  "Name": "One", 
  "typeId": 1 
}, { 
  "Name": "Two", 
  "typeId": 2 
}, { 
  "Name": "One Two", 
  "typeId": 1 
}, { 
  "Name": "Three", 
  "typeId": 3 
}, { 
  "Name": "Three Two", 
  "typeId": 3 
}]; 
 
const GroupedTypes = Object.entries( 
  _.mapValues(_.groupBy(FilterGuests, 'typeId'), rlist => 
    rlist.map(roomType => _.omit(roomType, 'typeId')) 
  ) 
).map(e => { 
  e.splice(1, 0, e[1][0].Name); 
  return e; 
}); 
 
console.log(GroupedTypes);
.as-console-wrapper { max-height: 100% !important; top: auto; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Answer 2

You could use reduce to group the items based on typeId:

const input=[{"Name":"One","typeId":1},{"Name":"Two","typeId":2},{"Name":"One Two","typeId":1},{"Name":"Three","typeId":3},{"Name":"Three Two","typeId":3}] 
 
const merged = input.reduce((acc, { Name, typeId }) => { 
  acc[typeId] = acc[typeId] || [ typeId, Name, []]; 
  acc[typeId][2].push({ Name }); 
  return acc; 
}, {}) 
 
console.log(Object.values(merged))

You need to create an accumulator object with each typeId as key and the array you need in the output as its value. If the key is not already present, add the key with [ typeId, Name, []] as value. This way, the first item's Name will be in the output. This is how the accumulator/merged will look like:

{
      "1": [1, "One", [{ "Name": "One" }, { "Name": "One Two" }]],
      "2": [2, "Two", [{ "Name": "Two" }]],
      "3": [3, "Three", [{ "Name": "Three" }, { "Name": "Three Two" }]]
}

Then use, Object.values() to get just the values of this object as an array

Answer 3

You can do this also with the lodash chain method and a reduce on the end. This will be more readable overall:

let data = [{ "Name": "One", "typeId": 1 }, { "Name": "Two", "typeId": 2 }, { "Name": "One Two", "typeId": 1 }, { "Name": "Three", "typeId": 3 }, { "Name": "Three Two", "typeId": 3 }] 
 
const GroupedTypes = _(data) 
  .groupBy('typeId') 
  .mapValues(x => _.map(x, ({Name, typeId}) => ({Name}))) 
  .entries() 
  .reduce((r,[k,v]) => (r.push([k, v[0].Name, v]), r), []) 
 
console.log(GroupedTypes)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Answer 4

Use _.map() and not _.mapValues(), which works on objects as well, and returns an array. In the map's callback, get the group key from the 2nd param, take the 1st elements Name, and return an array in the requested format:

const FilterGuests = [{"Name":"One","typeId":1},{"Name":"Two","typeId":2},{"Name":"One Two","typeId":1},{"Name":"Three","typeId":3},{"Name":"Three Two","typeId":3}]; 
 
const GroupedTypes = _.map( 
  _.groupBy(FilterGuests, 'typeId'),  
  (rlist, key) => 
  [ 
    key,  
    _.get(rlist, '[0].Name'),  
    rlist.map(roomType => _.omit(roomType, 'typeId')) 
  ] 
) 
 
console.log(GroupedTypes);
.as-console-wrapper { max-height: 100% !important; top: auto; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

READ ALSO
Javascript queue implementation with linkedlist

Javascript queue implementation with linkedlist

Here I am implementing Queue with linkedlistsIn else block of enqueue() method i am not assigning anything to this

53
How to add a pop-up in the home page of an app using react-native and javascript?

How to add a pop-up in the home page of an app using react-native and javascript?

I have a javascript code for a home page in my appI want to make every user see a pop-up on the screen when they open an app, click a button, and then they can use the app

68
toggle function works but not slideToggle in jQuery

toggle function works but not slideToggle in jQuery

I'm trying to make the slideToggle work but couldn't figure outAs per different answers, I tried adding display: block and display: none but that isn't working either

70
how to get substring length of sub array in javascript?

how to get substring length of sub array in javascript?

I am trying to solve this problem but I am not getting the expected output

46