TLDR: Gist
How can we write a 'utility' function that will take in a data set (think ๐คArray of Objects), some Array of specified ๐s and some function and return a new 'data set' with the values for the specified ๐s updated as per the function that was passed in?
Yeah...that's hard to describe.
Here's a simple example where we want to double (doubler
) only the specified values (keys
) within our data set (data
).
const data = [
{
a: 1,
b: 2,
c: 3,
d: 4
},
{
a: 7,
b: 9,
c: 0,
d: 8
},
{
a: 5,
b: 0,
c: 4,
d: 3
},
{
a: 9,
b: 3,
c: 7,
d: 2
}
];
const keys = ["a", "d"];
const doubler = num => num * 2;
This post assumes that you are familiar with all concepts shown in the code ๐๐ฝand that you also know about callback functions, map and reduce. You should also be able to follow ES2015 arrow function expressions and ES2015 Object spread operator.
This can be done more 'easily' with forEach
, but let's do a more elegant 'functional approach' using reduce
.
Since we want an Array that still contains all of the elements in data
, map
will be our choice. We want to 'map' ๐บ๏ธover every element inside of data
... data.map(d =>
๐ค.
Now, for each element, d
, we want to then iterate over keys
and at the end of that, we want just a single new Object with updated values. Well, any time, we are iterating over an Array and invoking a function on each element but we just want to get back 1 'total' or 'accumulated' or 'aggregated' piece of data, reduce
is the way to go.
data.map(d =>
// For every 'd', update all of the VALUES for some specified ARRAY of ๐s...'reduce' ๐s down to just 1 'updated object'.
keys.reduce(
(updatedObject, currentKey) =>
/**
* For each ๐...
* 'Spread' the current data object, 'd'.
* 'Spread' 'updatedObject' (it's empty on first iteration)
* 'Spread' a newly created Object that contains only the current 'key'
* and INVOKE the given CALLBACK FUNCTION to create an updated VALUE.
* 'merge' all of those OBJECTS ๐๐ฝand keep going until all of ๐s are iterated.
*/
({ ...d, ...updatedObject, ...{ [currentKey]: doubler(d[currentKey]) } }),
// Instantiate updatedObject as an empty Object
{}
)
);
Here it is wrapped up in a 'utility function' with JSDocs.
/**
* Update values of given ๐s in an object.
* @param {Object} data
* @param {Array} keys
* @param {Function} cb - the 'update' to perform
* @return {Object}
*/
function updateSpecifiedKeys(data, keys, cb) {
return data.map(d =>
// For every 'd', update all of the VALUES for some specified ARRAY of ๐s...'reduce' ๐s down to just 1 'updated object'.
keys.reduce(
(updatedObject, currentKey) => ({
...d,
...updatedObject,
...{ [currentKey]: cb(d[currentKey]) }
}),
{}
)
);
}
Invoking this 'utility function' with the data that we started above: updateSpecifiedKeys(data, keys, doubler);
const data = [
{
a: 1,
b: 2,
c: 3,
d: 4
},
{
a: 7,
b: 9,
c: 0,
d: 8
},
{
a: 5,
b: 0,
c: 4,
d: 3
},
{
a: 9,
b: 3,
c: 7,
d: 2
}
];
const keys = ["a", "d"];
const doubler = num => num * 2;
function updateSpecifiedKeys(data, keys, cb) {
return data.map(d =>
// For every 'd', update all of the VALUES for some specified ARRAY of ๐s...'reduce' ๐s down to just 1 'updated object'.
keys.reduce(
(updatedObject, currentKey) => ({
...d,
...updatedObject,
...{ [currentKey]: cb(d[currentKey]) }
}),
{}
)
);
}
console.log(updateSpecifiedKeys(data, keys, doubler));
[
{ a: 2, b: 2, c: 3, d: 8 },
{ a: 14, b: 9, c: 0, d: 16 },
{ a: 10, b: 0, c: 4, d: 6 },
{ a: 18, b: 3, c: 7, d: 4 }
]
Top comments (0)