Skip to content

Latest commit

 

History

History
118 lines (101 loc) · 2.42 KB

extract-and-flatten.md

File metadata and controls

118 lines (101 loc) · 2.42 KB

Extract and flatten an object

Many times, I've found myself wanting to extract only some keys from an object and also flatten it.

As an example, here's a person's details:

const obj = {
  id: 1,
  name: 'john',
  city: 'denver',
  job: 'engineer',
  details: {
    age: 28,
    contact: {
      phone: '789787',
      email: 'john@doe.com',
    },
  },
};

And here's what I want to convert it into:

const newObj = {
  id: 1,
  name: 'john',
  email: 'john@doe.com',
  age: 28,
  location: 'denver',
};

So basically there's a bit of "extraction" involved (like, location in the new object maps to obj.city and email maps to obj.details.contact.email).

To do this, I use a custom transform function (note that the first two functions getKeyVal and pluck are helper functions):

const getKeyVal = (key) => (obj) => {
  if (!obj || !key) return undefined;
  if (!obj.hasOwnProperty(key)) return undefined;
  return obj[key];
};

const pluck = (path) => (object) => {
  if (!path) return null;
  const keys = path.split('.').length ? path.split('.') : [path];
  return keys.reduce((acc, nextKey) => getKeyVal(nextKey)(acc), object);
};

const transform = (definition) => (inputObject) => {
  return Object.fromEntries(
    Object.keys(definition).map((key) => [
      key,
      typeof definition[key] === 'string'
        ? pluck(definition[key])(inputObject)
        : transform(definition[key])(inputObject),
    ])
  );
};

And here's how I'd use the transform function:

// the actual input object
const obj = {
  id: 1,
  name: 'john',
  city: 'denver',
  job: 'engineer',
  details: {
    age: 28,
    contact: {
      phone: '789787',
      email: 'john@doe.com',
    },
  },
};

// the "definition" which is like a map or instruction
const definition = {
  name: 'name',
  age: 'details.age',
  email: 'details.contact.email',
  id: 'id',
  location: 'city',
};

// finally, apply the transformer
const result = transform(definition)(obj);
console.log(result);
/*
{
  id: 1,
  name: 'john',
  email: 'john@doe.com',
  age: 28,
  location: 'denver',
}
*/

What if in the definition, you had a wrong mapping?

const definition = {
  name: 'name',
  age: 'details.age',
  email: 'details.contact.email',
  id: 'id',
  location: 'city',
  job: 'details.job', // wrong mapping. there is no `details.job` in the input object
};

This will not error out but simply return undefined for the job key.