MarcoPavez
MarcoPavez

Reputation: 21

Refactor: How to use .reduce with an external function?

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:

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.

Upvotes: 1

Views: 219

Answers (2)

Khoa
Khoa

Reputation: 2932

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) {
    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)

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 a soup bowl πŸ₯˜
  • nexus is an array of ingredients [πŸ₯© meat, 🍼 milk, πŸ₯š egg, πŸ… tomato, πŸ₯” potato]
  • reduce is a cook πŸ§‘β€πŸ³
  • my_reducer is a cooker robot πŸ€–
  • [] is an empty bow πŸ₯£

Then 2 params pass to the my_reducer (the cooker robot πŸ€–) are:

  • acc: the current status of the bowl πŸ₯£
  • cur: the current ingredient we are processing

Thus

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

similar to

πŸ₯˜ = [πŸ₯©, 🍼, πŸ₯š, πŸ…, πŸ₯”].πŸ§‘β€πŸ³(πŸ€–, πŸ₯£)

πŸ€–(current bowl status, current ingredient)

+ the soup bowl πŸ₯˜ is created by using a cooker πŸ€–
+ to cook all ingredients [πŸ₯©, πŸ₯š, 🍼, πŸ…, πŸ₯”]
+ that prepared by the cook πŸ§‘β€πŸ³
+ start with an empty bowl πŸ₯£

How it works:

At the beginning, you have: a cook πŸ§‘β€πŸ³, a cooker robot πŸ€–, an empty bow πŸ₯£

πŸ§‘β€πŸ³(πŸ€–, πŸ₯£)

The cook πŸ§‘β€πŸ³ will loop through each ingredient (follow the order in the array)

  1. The cook πŸ§‘β€πŸ³ picks meat πŸ₯©
  • πŸ€–(πŸ₯£, πŸ₯©) returns (πŸ₯£πŸ₯©) [aka bowl with cooked meat]
  1. The cook πŸ§‘β€πŸ³ picks the next ingredient which is egg πŸ₯š
  • πŸ€–(πŸ₯£πŸ₯©, πŸ₯š) returns (πŸ₯£πŸ₯©πŸ₯š) [bowl with cooked ...]
  1. The cook πŸ§‘β€πŸ³ picks the next ingredient which is milk 🍼
  • πŸ€–(πŸ₯£πŸ₯©πŸ₯š, 🍼) returns (πŸ₯£πŸ₯©πŸ₯šπŸΌ) [bowl with cooked ...]
  1. so on

  2. The last ingredient is potato πŸ₯”

  • just like that we have a bow of all cooked ingredients (πŸ₯£πŸ₯©πŸ₯šπŸΌπŸ…πŸ₯”)

That is our soup πŸ₯˜

Upvotes: 1

Andy
Andy

Reputation: 63524

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

Upvotes: 3

Related Questions