DEV Community

druchan
druchan

Posted on

Enrich an object - Javascript Recipe

This scenario is very common in apps that interact with data from an API/backend service.

Let's say you get a customers list which looks like this:

let customers = [
  { id: 1, name: 'john', age: 28, city: 'denver' },
  { id: 2, name: 'bob', age: 25, city: 'kyoto' },
  { id: 3, name: 'angela', age: 44, city: 'mumbai' },
];
Enter fullscreen mode Exit fullscreen mode

And then you get, as an example, a kycCustomer:

let kycCustomer = {
  customerId: 1,
  kyc: true,
  createdOn: '2022-11-01',
};
Enter fullscreen mode Exit fullscreen mode

But when I want to use the kycCustomer to, say, show on the UI or use as input to another function, I have to also get the customer's name, age and city where customerId equals 1, and "enrich" this kycCustomer object with that data.

To do this, I use an enrich function where I tell which keys to match, which keys to extract and the function returns an enriched list.

const enrich =
  ({ match, extract = [], source = [] }) =>
  (inputObj) => {
    if (!match) return inputObj;
    if (!extract?.length) return inputObj;
    if (!source) return inputObj;
    if (!source?.length) return inputObj;
    let matchingItem = source.find(
      (s) => s[match.sourceKey] === inputObj[match.inputKey]
    );
    if (!matchingItem) return inputObj;
    return extract.reduce((acc, key) => {
      return {
        ...acc,
        [key]: matchingItem[key],
      };
    }, inputObj);
  };

// USAGE
let customers = [
  { id: 1, name: 'john', age: 28, city: 'denver' },
  { id: 2, name: 'bob', age: 25, city: 'kyoto' },
  { id: 3, name: 'angela', age: 44, city: 'mumbai' },
];

let kycCustomer = {
  customerId: 1,
  kyc: true,
  createdOn: '2022-11-01',
};

let result = enrich({
  match: { sourceKey: 'id', inputKey: 'customerId' },
  extract: ['name', 'age', 'city'],
  source: customers,
})(kycCustomer);

/*
result = {
  customerId: 1,
  kyc: true,
  createdOn: '2022-11-01',
  name: 'john',
  age: 28,
  city: 'denver'
}
*/
Enter fullscreen mode Exit fullscreen mode

Handling lists instead of objects

We handled a single object kycCustomer but what if we have to enrich an entire list/array of such objects?

let kycCustomers = [
  { customerId: 1, kyc: true, createdOn: '2022-11-01' },
  { customerId: 2, kyc: false },
  { customerId: 3, kyc: true, createdOn: '2022-09-01' },
];
Enter fullscreen mode Exit fullscreen mode

Simply mapping over and using the enrich function will solve this for us. Like so:

let customers = [
  { id: 1, name: 'john', age: 28, city: 'denver' },
  { id: 2, name: 'bob', age: 25, city: 'kyoto' },
  { id: 3, name: 'angela', age: 44, city: 'mumbai' },
];

let kycCustomers = [
  { customerId: 1, kyc: true, createdOn: '2022-11-01' },
  { customerId: 2, kyc: false },
  { customerId: 3, kyc: true, createdOn: '2022-09-01' },
];

let enrichedKycCustomers = kycCustomers.map((kycCustomer) =>
  enrich({
    match: { sourceKey: 'id', inputKey: 'customerId' },
    extract: ['name', 'age', 'city'],
    source: customers,
  })(kycCustomer)
);

/*
enrichedKycCustomers = [
  {
    customerId: 1,
    kyc: true,
    createdOn: '2022-11-01',
    name: 'john',
    age: 28,
    city: 'denver',
  },
  { customerId: 2, kyc: false, name: 'bob', age: 25, city: 'kyoto' },
  {
    customerId: 3,
    kyc: true,
    createdOn: '2022-09-01',
    name: 'angela',
    age: 44,
    city: 'mumbai',
  },
]
*/
Enter fullscreen mode Exit fullscreen mode

And in fact, because enrich is a higher-order function (notice how it takes one argument and returns a function that takes the inputObject argument), we can also condense the enrichedKycCustomers to this:

let enrichedKycCustomers = kycCustomers.map(
  enrich({
    match: { sourceKey: 'id', inputKey: 'customerId' },
    extract: ['name', 'age', 'city'],
    source: customers,
  })
);
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
artydev profile image
artydev

Great series thank you :-)

Collapse
 
zomars profile image
Omar López

This is exactly what I was looking for! I'm going to try to recreate it in TypeScript. Wish me luck.