Search code examples
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:

  • Flatten the structure so that each user's data is a single object with properties including the user's address in a flattened format.
  • Transform the data into a new structure where the user’s postalCode is used as the key and the value is an object containing name, age, and city.

Questions:

  • Are there more efficient ways to flatten and transform this data using lodash?

  • How can I improve the readability and maintainability of this code, especially if the data structure becomes more complex?

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.