Home > Software design >  Refactor: How to use .reduce with an external function?
Refactor: How to use .reduce with an external function?

Time:12-14

hope that you're doing well. I've a question related to the .reduce function. I'm refactoring an elder code and, in order to fulfill the DRY principle, I'd like to know how to create a function that allows my to achieve a 'simple' process from .reduce:

  • Group the data by a certain key (phoNumbId in the example).
  • Sum values of another key (but related to the first key) to summarize both (this key is mount).
  • Return a dictionary with the grouped key and the added values

To clarify my question, I've this example dictionary:

const nexus = [
  { namesId: 1, phoNumbId: 1, country: 'PERU', mount: 1200 },
  { namesId: 1, phoNumbId: 2, country: 'CANADA', mount: 2000},
  { namesId: 2, phoNumbId: 2, country: 'ENGLAND', mount: 3000},
  { namesId: 2, phoNumbId: 3, country: 'RUSSIA', mount: 40000},
  { namesId: 3, phoNumbId: 1, country: 'BELGIUM', mount: 500},
  { namesId: 3, phoNumbId: 2, country: 'SPAIN', mount: 500},
  { namesId: 3, phoNumbId: 3, country: 'PORTUGAL', mount: 2020}
]

And the (coded) process that I want to refactor is:

var result2 = [];

nexus.reduce(function (res, value) {
        if (!res[value.phoNumbId]) {
            res[value.phoNumbId] = { phoNumbId: value.phoNumbId, mount: 0 };
            result2.push(res[value.phoNumbId])
        }
        res[value.phoNumbId].mount = value.mount;
        return res;
    }, {});

The way that my limited knowledge in JS allows me to code a possible solution then is as follows:

function sumAll(firstValue,secondValue,finalArray){
 if (!res[value.firstValue]) {
            res[value.firstValue] = { firstValue: value.firstValue, secondValue: 0 };
            finalArray.push(res[value.firstValue])
        }
        res[value.primerValor].secondValue = value.secondValue;
        return res;
}

var result2 = [];

nexus.reduce(function (res, value) { sumAll(phoNumbId,mount,result2) }, {});

I'd really appreciate anykind of help :) P.S.: I couldn't think of another title for the question, feel that doesn't summarize the real problem. I accept suggestions.

CodePudding user response:

You can use computed property names - that's using your argument values in bracket notation - to create the object.

const data=[{namesId:1,phoNumbId:1,country:"PERU",mount:1200},{namesId:1,phoNumbId:2,country:"CANADA",mount:2e3},{namesId:2,phoNumbId:2,country:"ENGLAND",mount:3e3},{namesId:2,phoNumbId:3,country:"RUSSIA",mount:4e4},{namesId:3,phoNumbId:1,country:"BELGIUM",mount:500},{namesId:3,phoNumbId:2,country:"SPAIN",mount:500},{namesId:3,phoNumbId:3,country:"PORTUGAL",mount:2020}];

function sumAll(data, first, second) {

  const temp = data.reduce((acc, c) => {
    
    // For convenience assign the object key
    // to the first argument
    const key = c[first];
    
    // If the key doesn't exist on the object
    // add it and assign an object to it
    acc[key] ??= { [first]: key, [second]: 0 };
    
    // Then update the value
    acc[key][second]  = c[second];
   
    return acc;
  
  }, {});
  
  // Finally you probably want to return
  // an array of just those objects
  return Object.values(temp);
  
}

console.log(sumAll(data, 'phoNumbId', 'mount'));
console.log(sumAll(data, 'namesId', 'mount'));

Additional documentation

CodePudding user response:

Consider your original code (before refactoring) is runnable.


Here is the first integration:

var result2 = nexus.reduce(function (res, value) {
    if (!res[value.phoNumbId]) {
        res[value.phoNumbId] = { phoNumbId: value.phoNumbId, mount: 0 };
        res.push(res[value.phoNumbId])
    }

    res[value.phoNumbId].mount  = value.mount;
    return res;
}, []);

What changed:

  • the return from the reduce function is assigned directly to result2
  • the 2nd param of the reduce function is the initial value of the result2 from the origin (in this case, an empty array [])
  • the result2.push(....) is changed to res.push(....)

Here is the second integration:

var result = nexus.reduce(function (acc, cur, origin) {
    if (!acc[cur.phoNumbId]) {
        acc[cur.phoNumbId] = { phoNumbId: cur.phoNumbId, mount: 0 };
        acc.push(acc[cur.phoNumbId])
    }

    acc[cur.phoNumbId].mount  = cur.mount;
    return acc;
}, []);

What changed:

  • rename variables:
    • result2 -> result
    • res -> acc (it stands for accumulator)
    • value -> cur (it stands for current value)
  • add new param: origin

Here is the third integration

function my_reducer(acc, cur) {
    if (!acc[cur.phoNumbId]) {
        acc[cur.phoNumbId] = { phoNumbId: cur.phoNumbId, mount: 0 };
        acc.push(acc[cur.phoNumbId])
    }

    acc[cur.phoNumbId].mount  = cur.mount;
    return acc;
}

var result = nexus.reduce(my_reducer, []);

What changed:

  • Extract the no name function (the one that was passed as the 1st param to the reduce function) to a function with a name.

It will take time to explain the way the reduce function works. You can find out around the internet or other StackOverflow questions/answers.

Here is a short explanation:

Take a look at var result = nexus.reduce(my_reducer, []);, consider

  • result is an soup bow
  • Related