0

I'm have an object as shown below for which I need to find the total including the numbers that are outside as well as inside the curly braces.

 this.count = {
      "count1": "250 (220)",
      "count2": "125 (100)",
      "count3": "125 (100)",
      "count4": "125 (100)"
    }

Expected result: Sum : "625 (520)"

I was able t find out the sum of the first set of strings ie., 625 by doing below logic:

let total=this.getTotal(this.count);

 public getTotal(count) {
         const count1 = parseInt(items.count1.split(' ')[0]);
         const count2 = parseInt(items.count2.split(' ')[0]);
         const count3 = parseInt(items.count3.split(' ')[0]);
         const count4 = parseInt(items.count4.split(' ')[0]);
         const totalA = count1 + count2 + count3 + count4;
         console.log(totalA);
    }

But I was not able to split wisely () to calculate the other portion and concate with totalA .Do let me know any functional approach that suits best to calculate these type of objects.Thanks

Paul
  • 139,544
  • 27
  • 275
  • 264
forgottofly
  • 2,729
  • 11
  • 51
  • 93

9 Answers9

3

You could use reduce() method to get one object and then create a string.

const data = {"count1": "250 (220)","count2": "125 (100)","count3": "125 (100)","count4": "125 (100)"}

const total = Object.values(data).reduce((r, e) => {
  const [a, b] = e.split(/\(([^)]+)\)/);
  r.a = (r.a || 0) + +a;
  r.b = (r.b || 0) + +b;
  return r;
}, {})

const result = `Sum: ${total.a} (${total.b})`
console.log(result)

You could also use array as accumulator inside reduce and inside use forEach() loop.

const data = {"count1": "250 (220)","count2": "125 (100)","count3": "125 (100)","count4": "125 (100)"}

const [a, b] = Object.values(data).reduce((r, e) => {
  e.split(/\(([^)]+)\)/).forEach((e, i) => r[i] += +e)
  return r;
}, [0, 0])

const result = `Sum: ${a} (${b})`
console.log(result)
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • +1. Also, I think it might be a little nicer to initialize the `0` in the initial value of the reduce `{a:0,b:0}` and then inside you can just do `r.a += +a;` – Paul Mar 20 '18 at 16:51
2

Breaking the work down into eg a Count module makes the task easier

const Count =
  { fromString: s =>
      s.match (/\d+/g) .map (Number)
      
  , toString: ([a, b]) =>
      `${a} (${b})`
      
  , concat: ([a1, b1], [a2, b2]) =>
      [ a1 + a2, b1 + b2 ]
    
  , empty:
      [0, 0]
  }

const main = data =>
{
  const result =
    data.map (Count.fromString)
        .reduce (Count.concat, Count.empty)
  console.log (Count.toString (result))
}

const data =
  { "count1": "250 (220)"
  , "count2": "125 (100)"
  , "count3": "125 (100)"
  , "count4": "125 (100)"
  }

main (Object.values (data))

The .map-.reduce combo above results in two loops, producing unnecessary intermediate values. Using a generic combinator mapReduce we can collapse the loops into one - changes in bold

const mapReduce = (m, r) =>
  (acc, x) => r (acc, m (x))

const main = data =>
{
  const result =
    data.reduce (mapReduce (Count.fromString, Count.concat), Count.empty)
  console.log (Count.toString (result))
}
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    Nice! `n => Number (n)` could be just `Number` – georg Mar 20 '18 at 17:26
  • @georg, I generally avoid using `Array.prototype.map` like that because it passes extra arguments, but in this case `Number` works fine. Thanks for the comment :D – Mulan Mar 20 '18 at 17:56
1

Using Array.prototype.reduce this would work:

let count = {
  "count1": "250 (220)",
  "count2": "125 (100)",
  "count3": "125 (100)",
  "count4": "125 (100)"
}

let totalCount1 = Object.values(count).reduce(function(acc, val) {
  return acc + parseInt(val.split(' ')[0])
}, 0) 

let totalCount2 = Object.values(count).reduce(function(acc, val) {
  return acc + parseInt(val.split('(').pop().split(')')[0])
}, 0) 

console.log(`${totalCount1} (${totalCount2})`)
connexo
  • 53,704
  • 14
  • 91
  • 128
1

Since you've tagged "functional-programming", here's a possible FP solution:

// utilities

let map = (f, xs) => [...xs].map(f);
let sum = xs => xs.reduce((a, b) => Number(a) + Number(b));
let zip = (...xss) => xss[0].map((_, i) => xss.map(xs => xs[i]));

// here we go

count = {
    "count1": "250 (220) 10",
    "count2": "125 (100) 20",
    "count3": "125 (100) 30",
    "count4": "125 (100) 40"
};


res = map(sum, zip(
    ...map(
        s => s.match(/\d+/g),
        Object.values(count)
)));

console.log(res);

(I've added a third column to make things a little bit more interesting. The code works with any number of columns).

That being said, a real solution to your problem would be to fix that broken data structure in the first place.

georg
  • 211,518
  • 52
  • 313
  • 390
  • That's a very pretty `zip` function! – Scott Sauyet Mar 20 '18 at 17:22
  • @ScottSauyet: yep, here are a few more: https://stackoverflow.com/questions/4856717/javascript-equivalent-of-pythons-zip-function – georg Mar 20 '18 at 17:23
  • That's definitely the best of the bunch. Some of those are pretty awful. This one is limited, of course, using the length of the first array and possibly therefore adding a number of `undefined`s. But *caveat emptor*; it's still very pretty. – Scott Sauyet Mar 20 '18 at 17:42
  • @ScottSauyet: thanks! Unfortunately, FP support is still quite poor in JS, so almost every reasonable answer has to begin with this "utilities" block. The life would be so much easier with these things built-in in the language. – georg Mar 20 '18 at 17:50
  • 1
    As the founder of the FP library **[Ramda](http://ramdajs.com)**, I sympathize entirely. I tend to think in terms of such utility functions, often writing my answers to plain JS question in terms of Ramda functions before converting back (or including them, as you do.) – Scott Sauyet Mar 20 '18 at 18:01
1

This seems to do what you want, and I think is fairly readable:

const data = {"count1": "250 (220)","count2": "125 (100)","count3": "125 (100)","count4": "125 (100)"}

const splitSum = (data) => {
  const [a, b] = Object.values(data).reduce(([a0, b0], e) => {
    const [a1, b1] = e.split(/[^\d]+/).map(Number);
    return [a0 + a1, b0 + b1];
  }, [0, 0])
  return `Sum: ${a} (${b})`
}

console.log(splitSum(data))
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
0

To improve your wisdom (you can use RegEx to solve this):

var count = {
  "count1": "250 (220)",
  "count2": "125 (100)",
  "count3": "125 (100)",
  "count4": "125 (100)"
}
function getTotal(items) {
     let sum = 0
     let sum1 = 0
     for (let i in items) {
         let item = items[i]
         sum += Number.parseInt(item.split(' ')[0])
         sum1 += Number.parseInt(item.split(' ')[1].replace(/^\(|\)$/g, ''))
     }
     console.log('Sum: ' + sum + ' (' + sum1 + ')')
}
getTotal(count)
messerbill
  • 5,499
  • 1
  • 27
  • 38
0

You can use Object.values() and forEach function for array.

var count = {
        "count1": "250 (220)",
        "count2": "125 (100)",
        "count3": "125 (100)",
        "count4": "125 (100)"
    },
    lsum = 0,
    rsum = 0;

Object.values(count).forEach(v => {
    v = v.replace('(', '').replace(')', '').split(' ');
    lsum += +v[0];
    rsum += +v[1];
});
console.log(`${lsum} (${rsum})`)
Narendra Jadhav
  • 10,052
  • 15
  • 33
  • 44
0

Your references to this indicate that there is more that you aren't showing us and your use of the word public is not valid in JavaScript.

But, you can just loop over the keys in the object and use .split (a little differently than you were using it) to get the totals of each part of the value.

var count = {
    "count1": "250 (220)",
    "count2": "125 (100)",
    "count3": "125 (100)",
    "count4": "125 (100)" 
};

function getTotal(count) {
    let val1 = 0;
    let val2 = 0;
   
    for(var key in count){
      val1 += parseInt(count[key].split(" (")[0], 10);
      val2 += parseInt(count[key].split(" (")[1], 10);    
    }

    return "Sum 1: " + val1 + " - Sum 2: " + val2;
}

console.log(getTotal(count));
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
0

Here's a bit of a verbose reduce based solution:

  const count = {
    "count1": "250 (220)",
    "count2": "125 (100)",
    "count3": "125 (100)",
    "count4": "125 (100)"
  };

  const rowPattern = /(\d+) \((\d+)\)/;

  const parseRow = row => row.match(rowPattern).slice(1, 3).map(s => parseInt(s));

  const result = Object.values(count).reduce((a, b) => {
    const [ outerA, innerA ] = parseRow(a),
      [ outerB, innerB ] = parseRow(b);
    return `${outerA + outerB} (${innerA + innerB})`;
  }, '0 (0)');

  console.log(result); // 625 (520)
gpmcadam
  • 6,346
  • 2
  • 33
  • 37