Search code examples
javascriptlodash

Group responses from 2 arrays based on a common id using lodash


I have 2 arrays one having the Group info and the other having Field info. I want to combine these 2 arrays to create proper Grouped Fields.

The API response looks like

{
  "type1": [
    {
      "groupId": "1",
      "fieldIds": [
        "field1",
        "field2"
      ]
    },
    {
      "groupId": "2",
      "fieldIds": [
        "field3"
      ]
    }
  ],
  "type2": [
    {
      "groupId": "3",
      "fieldIds": [
        "field4",
        "field5"
      ]
    },
    {
      "groupId": "4",
      "fieldIds": [
        "field3"
      ]
    }
  ],
  "fields": [
    {
      "fieldId": "field1",
      "fieldName": "FName1"
    }...
  ]
}

I want to group fields so that the output looks like

{
  "type1": [
    {
      "groupId": "1",
      "fields": [
        {
          "fieldId": "field1",
          "fieldName": "FName1"
        },
        {
          "fieldId": "field2",
          "fieldName": "FName2"
        }
      ]
    },
    {
      "groupId": "2",
      "fields": [
        {
          "fieldId": "field3",
          "fieldName": "FName3"
        }
      ]
    }
  ],
  "type2": [
    {
      "groupId": "3",
      "fields": [
        {
          "fieldId": "field4",
          "fieldName": "FName4"
        },
        {
          "fieldId": "field5",
          "fieldName": "FName5"
        }
      ]
    }
  ]
}

I have accomplished the "brutal" way where I am traversing the groups, picking the fieldId, searching that fieldId in the fields array and then putting it under the group. It works but what I am looking is some "clean" way to do this using lodash chaining.


Solution

  • My approach is to group the fields by id and create a lookup object so I can directly access it in O(1).

    Then transform the object to array of entries so I can iterate an replace the field ids with the actual fields. After that convert back to an object

    first i will give the non lodash

    const {fields, ...types} = { "type1": [ { "groupId": "1", "fieldIds": [ "field1", "field2" ] }, { "groupId": "2", "fieldIds": [ "field3" ] } ], "type2": [ { "groupId": "3", "fieldIds": [ "field4", "field5" ] }, { "groupId": "4", "fieldIds": [ "field3" ] } ], "fields": [ { "fieldId": "field1", "fieldName": "FName1" }, { "fieldId": "field2", "fieldName": "FName2" }, { "fieldId": "field3", "fieldName": "FName3" }, { "fieldId": "field4", "fieldName": "FName4" }, { "fieldId": "field5", "fieldName": "FName5" } ] }
    
    const groupedFields = fields.reduce((acc,curr) => {
      acc[curr.fieldId] = curr
      return acc
    },{})
    
    const result = Object.fromEntries(Object.entries(types).map(([k,v]) => {
      const newV = v.map(({groupId,fieldIds}) => {
        const fields = fieldIds.map(fieldId => groupedFields[fieldId])
        return {groupId,fields}
      })
      return [k,newV]  
    }))
    
    console.log(JSON.parse(JSON.stringify(result)))

    lodash solution

    const {fields, ...types} = { "type1": [ { "groupId": "1", "fieldIds": [ "field1", "field2" ] }, { "groupId": "2", "fieldIds": [ "field3" ] } ], "type2": [ { "groupId": "3", "fieldIds": [ "field4", "field5" ] }, { "groupId": "4", "fieldIds": [ "field3" ] } ], "fields": [ { "fieldId": "field1", "fieldName": "FName1" }, { "fieldId": "field2", "fieldName": "FName2" }, { "fieldId": "field3", "fieldName": "FName3" }, { "fieldId": "field4", "fieldName": "FName4" }, { "fieldId": "field5", "fieldName": "FName5" } ] }
    
    const groupedFields = _.groupBy(fields,'fieldId')
    
    const result = _.mapValues(types, (value) => {
      return _.map(value, ({ groupId, fieldIds }) => ({
        groupId,
        fields: _.map(fieldIds, (fieldId) => groupedFields[fieldId][0]),
      }));
    });
    
    console.log(JSON.parse(JSON.stringify(result)))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>