javascriptnode.jstypescriptlodashnode-modules

How can I handle complex nested data transformations in TypeScript using lodash?


I'm working on a TypeScript project where I need to perform complex transformations on deeply nested JSON data. I'm using lodash for utility functions, but I'm struggling with the following scenario:

{
  "users": [
    {
      "id": 1,
      "name": "Alice",
      "details": {
        "age": 25,
        "address": {
          "city": "Wonderland",
          "postalCode": "12345"
        }
      }
    },
    {
      "id": 2,
      "name": "Bob",
      "details": {
        "age": 30,
        "address": {
          "city": "Builderland",
          "postalCode": "67890"
        }
      }
    }
  ]
}

I need to:

Questions:

Here's what I’ve tried:

import _ from 'lodash'; 

const nestedData = {
  users: [
    {
      id: 1,
      name: "Alice",
      details: {
        age: 25,
        address: {
          city: "Wonderland",
          postalCode: "12345"
        }
      }
    },
    {
      id: 2,
      name: "Bob",
      details: {
        age: 30,
        address: {
          city: "Builderland",
          postalCode: "67890"
        }
      }
    }
  ]
};

// Flattening data
const flattenedData = _.flatMap(nestedData.users, user => ({
  id: user.id,
  name: user.name,
  age: user.details.age,
  city: user.details.address.city,
  postalCode: user.details.address.postalCode
}));

console.log('Flattened Data:', flattenedData);

// Transforming data
const transformedData = _.mapValues(
  _.keyBy(flattenedData, 'postalCode'),
  ({ name, age, city }) => ({ name, age, city })
);

console.log('Transformed Data:', transformedData);

Solution

  • If your main concern is performance, I recommend skipping lodash and do something like the following.

    I'm assuming that your data looks like this:

    interface User {
        id: number;
        name: string;
        details: {
            age: number;
            address: {
                city: string;
                postalCode: string;
            }
        }
    }
    

    If so, you can use the following:

    function postCodeMap(users: User[]) {
        type UserMapped = { name: User['name'], age: User['details']['age'], city: User['details']['address']['city'] };
        const postCodeMap = new Map<string, Array<UserMapped>>();
        users.forEach(u => { // iterate the array only one time
            const postalCode = u.details.address.postalCode;
            if (postCodeMap.has(postalCode)) {
                const prev = postCodeMap.get(postalCode);
                postCodeMap.set(postalCode, [
                    ...prev ? prev : [],
                    {
                        name: u.name,
                        age: u.details.age,
                        city: u.details.address.city
                    }
                ]);
            } else {
                postCodeMap.set(postalCode, [
                    {
                        name: u.name,
                        age: u.details.age,
                        city: u.details.address.city
                    }
                ]);
            }
        });
        return postCodeMap;
    }
    

    The result looks like this:

    const result = postCodeMap(nestedData.users);
    console.log(result);
    
    /*
    Map (2) {"12345" => [{
      "name": "Alice",
      "age": 25,
      "city": "Wonderland"
    }], "67890" => [{
      "name": "Bob",
      "age": 30,
      "city": "Builderland"
    }]}
    */
    

    As you can see, you can achieve the expected result by traversing the array only once, and you do not need to add external libraries, which will help you keep your application as small as possible.