skip to Main Content

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);

2

Answers


  1. 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.

    Login or Signup to reply.
  2. You don’t need to use flatMap since each user only correspond to one piece of object of data. Also, with many built-in array functions, lodash is probably not needed as well.

    Here’s an approach to get two types of those data with minimum hard-coded attributes, so it can be reused even with more complex data structure:

    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"
            }
          }
        }
      ]
    }
    
    const flattenObject = (obj, entries = []) => {
      Object.entries(obj).forEach(([key, value]) => {
        if (value && typeof value === 'object') {
          flattenObject(value, entries);
        } else {
          entries.push([key, value]);
        }
      });
      return Object.fromEntries(entries);
    }
    
    const flattened = nestedData.users.map(user => flattenObject(user));
    console.log('flattened ->', flattened);
    
    const transformed = flattened.reduce((acc, { postalCode, id, ...rest }) => {
      acc[postalCode] ??= [];
      acc[postalCode].push(rest);
      return acc;
    }, {});
    console.log('transformed ->', transformed);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search