Search code examples
typescriptprojection

Typescript - object projection


Give an arbitrary array of objects and an array of paths as strings in "dot notation" format is there a way to project to a new object?

For example given an array people and a list of paths (dot notation):

const people = [
  {
    firstName: 'John',
    lastName: 'Doe',
    address: {
      city: 'New York',
      country: 'US'
    }
  },
  {
    firstName: 'Sally',
    lastName: 'Jane',
    address: {
      city: 'Londaon',
      country: 'UK'
    }
  }
] as Person[]

const paths = ['firstName','address.country'];

Is there a way using Array.map(p => ???) to dynamically project with just the provided paths? The result for the above would end up being:

[
  {
    firstName: 'John',
    address: {
      country: 'US'
    }
  },
  {
    firstName: 'Sally',
    address: {
      country: 'UK'
    }
  }
]

My end goal is to take an array of objects and serialize using JSON.stringify(people) using the selected paths.


Solution

  • You could recursively follow the paths and then always pass along the object of at the last path. E.g:

    function followPath(obj: any, pathParts: string[]): any {
      const result: { [k: string]: any } = {};
    
      const firstPath = pathParts.shift();
      if (firstPath === undefined) {
        return {};
      }
    
      if (pathParts.length > 0) {
        result[firstPath] = followPath(obj[firstPath], pathParts);
      } else {
        result[firstPath] = obj[firstPath];
      }
    
      return result;
    }
    
    
    const paths = ['firstName', 'address.country'];
    
    const newArray = oldArray.map((v) => {
      const newObject = {};
    
      for (const path of paths) {
        const parts = path.split('.');
        const firstPath = parts.shift();
        if (firstPath === undefined) {
          continue;
        }
        
        newObject[firstPath] = parts.length > 0
          ? followPath(v[firstPath], parts)
          : v[firstPath];
      }
    
      return newObject;
    });