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' },
];
And then you get, as an example, a kycCustomer
:
let kycCustomer = {
customerId: 1,
kyc: true,
createdOn: '2022-11-01',
};
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'
}
*/
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' },
];
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',
},
]
*/
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,
})
);
Top comments (2)
Great series thank you :-)
This is exactly what I was looking for! I'm going to try to recreate it in TypeScript. Wish me luck.