Update a Data Set For Specified 🔑s
Tags: javascript
Published: 2020-03-19 16:43:57

TLDR: [Gist](https://gist.github.com/manavm1990/3fd932151def3a5a3fb754ccaa291783)

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`).

```javascript
    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,](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function)* [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). You should also be able to follow ES2015 [arrow function expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) and ES2015 [Object spread operator.](https://2ality.com/2016/10/rest-spread-properties.html#the-spread-operator-(...)-in-object-literals)**

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.

```javascript    
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](https://jsdoc.app/).

```javascript
    /**
     * 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);`

---

```javascript
    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));
```

---

```bash
    [
      { 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 }
    ]
```