Get the total value of the matching array elements

Viewed 578

This is my current array

0:{modelNumber: "123456789", balance: { amount:1000, currency:"EUR" }}
1:{modelNumber: "987654321", balance: { amount:2000, currency:"EUR" }}
2:{modelNumber: "322353466", balance: { amount:1500, currency:"GBP" }}
3:{modelNumber: "892347522", balance: { amount:1000, currency:"USD" }}
4:{modelNumber: "931883113", balance: { amount:3000, currency:"INR" }}
5:{modelNumber: "854300564", balance: { amount:2500, currency:"GBP" }}
6:{modelNumber: "931883113", balance: { amount:3000, currency:"INR" }}
7:{modelNumber: "854300564", balance: { amount:3500, currency:"USD" }}

I'm trying to return a new array, with each currency and the total value for each currency.

Like below return the total amount for each currency in the array above

0:{currency: "EUR", totalAmount: 3500}
1:{currency: "GBP", totalAmount: 5000}
2:{currency: "USD", totalAmount: 4500}
3:{currency: "INR", totalAmount: 6000}

My approach initially:

//the current array
let theInitialArray = state.vehicle;

const results = theInitialArray.reduce((accumalator, current) => {
    const { currency } = current.balance;
    if (accumalator[currency]) {
        accumalator[currency].push(current);
        return accumalator;
    }
    accumalator[currency] = [current];
    return accumalator;     
}, {});

let frank =  Object.keys(results)
let jim = [];
let expectedOutput = theInitialArray.filter((x) => {
    for (let i=0; i < frank.length; i++) {
        if (x.balance.currency === frank[i]) {
            jim.push({'currency': frank[i], 'amount': x.balance.amount});
        }
    }
});
console.log('expectedOutput', expectedOutput)
return expectedOutput
7 Answers

Here is a O(n) approach of getting that output:

  1. You first define a empty object tempObj which will be used to store the currency and totalAmount value as a object based on the currency key
  2. Then, if this currency key is defined in the tempObj you will simply add the amount with the totalAmount for an existing object.
  3. Else you will create a object with the amount as totalAmount, currency as key of tempObj and currency as currency of the item in forEach loop
  4. Finally you will need to do Object.values(tempObj) so that we get the object values and ignore the keys of tempObj to get the desired result.

var arr = [{modelNumber: "123456789", balance: { amount:1000, currency:"EUR" }},
{modelNumber: "987654321", balance: { amount:2000, currency:"EUR" }},
{modelNumber: "322353466", balance: { amount:1500, currency:"GBP" }},
{modelNumber: "892347522", balance: { amount:1000, currency:"USD" }},
{modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},
{modelNumber: "854300564", balance: { amount:2500, currency:"GBP" }},
{modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},
{modelNumber: "854300564", balance: { amount:3500, currency:"USD" }}];

var tempObj = {};
arr.forEach((obj)=>{
  if(tempObj[obj.balance.currency]){
    tempObj[obj.balance.currency].totalAmount += obj.balance.amount
  } else {
    tempObj[obj.balance.currency] = {
      currency: obj.balance.currency,
      totalAmount : obj.balance.amount
    }
  }
});
var resArray = Object.values(tempObj);
console.log(resArray);

You can Array.reduce() to iterate the data. If a currency doesn't exist in the accumulator (r in the reduce callback), initialize it. Add the current amount, to the currency amount in the accumulator. Get an array of currencies using Object.values:

const data = [{"modelNumber":"123456789","balance":{"amount":1000,"currency":"EUR"}},{"modelNumber":"987654321","balance":{"amount":2000,"currency":"EUR"}},{"modelNumber":"322353466","balance":{"amount":1500,"currency":"GBP"}},{"modelNumber":"892347522","balance":{"amount":1000,"currency":"USD"}},{"modelNumber":"931883113","balance":{"amount":3000,"currency":"INR"}},{"modelNumber":"854300564","balance":{"amount":2500,"currency":"GBP"}},{"modelNumber":"931883113","balance":{"amount":3000,"currency":"INR"}},{"modelNumber":"854300564","balance":{"amount":3500,"currency":"USD"}}];

const result = Object.values(data.reduce((r, { balance }) => {
  const { amount, currency } = balance;
  if(!r[currency]) r[currency] = { currency, amount: 0 };
  
  r[currency].amount += amount;
  
  return r;
}, {}));

console.log(result);

A simple solution with Array.prototype.reduce and Object.keys:

const data = [
    {modelNumber: "123456789", balance: { amount:1000, currency:"EUR" }},
    {modelNumber: "987654321", balance: { amount:2000, currency:"EUR" }},
    {modelNumber: "322353466", balance: { amount:1500, currency:"GBP" }},
    {modelNumber: "892347522", balance: { amount:1000, currency:"USD" }},
    {modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},
    {modelNumber: "854300564", balance: { amount:2500, currency:"GBP" }},
    {modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},
    {modelNumber: "854300564", balance: { amount:3500, currency:"USD" }}
];

const grouped = data.reduce((o, { balance: { amount:a, currency:c } }) =>
   ({...o, [c]: (o[c] || 0) + a }), {});

const result = Object.keys(grouped).map(currency =>
    ({currency, totalAmount: grouped[currency] }));

console.log(result);

It's possible to reduce the values you have and them map them into the format you require:

const values = [
  {modelNumber: "123456789", balance: { amount:1000, currency:"EUR" }},
  {modelNumber: "987654321", balance: { amount:2000, currency:"EUR" }},
  {modelNumber: "322353466", balance: { amount:1500, currency:"GBP" }},
  {modelNumber: "892347522", balance: { amount:1000, currency:"USD" }},
  {modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},
  {modelNumber: "854300564", balance: { amount:2500, currency:"GBP" }},
  {modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},
  {modelNumber: "854300564", balance: { amount:3500, currency:"USD" }},
];

const results = values.reduce((prev, curr) => ({
  ...prev,
  [curr.balance.currency]: (prev[curr.balance.currency] || 0) + curr.balance.amount
}), {})

const inCorrectFormat = Object.keys(results).map(key => ({
  currency: key,
  totalAmount: results[key]
}))

console.dir(inCorrectFormat)

You could take a Map and render the wanted array of currency/totalAmount pairs as new objects.

var data = [{ modelNumber: "123456789", balance: { amount: 1000, currency: "EUR" } }, { modelNumber: "987654321", balance: { amount: 2000, currency: "EUR" } }, { modelNumber: "322353466", balance: { amount: 1500, currency: "GBP" } }, { modelNumber: "892347522", balance: { amount: 1000, currency: "USD" } }, { modelNumber: "931883113", balance: { amount: 3000, currency: "INR" } }, { modelNumber: "854300564", balance: { amount: 2500, currency: "GBP" } }, { modelNumber: "931883113", balance: { amount: 3000, currency: "INR" } }, { modelNumber: "854300564", balance: { amount: 3500, currency: "USD" } }],
    result = Array.from(
        data.reduce((m, { balance: { amount, currency } }) =>
            m.set(currency, (m.get(currency) || 0) + amount), new Map),
        ([currency, totalAmount]) => ({ currency, totalAmount })
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

This uses two arrays and pushes the unique names of the currencies in one(currencies) and the objects in another (result).

a = [{modelNumber: "123456789", balance: { amount:1000, currency:"EUR" }}, {modelNumber: "987654321", balance: { amount:2000, currency:"EUR" }},{modelNumber: "322353466", balance: { amount:1500, currency:"GBP" }},{modelNumber: "892347522", balance: { amount:1000, currency:"USD" }},{modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},{modelNumber: "854300564", balance: { amount:2500, currency:"GBP" }},{modelNumber: "931883113", balance: { amount:3000, currency:"INR" }},{modelNumber: "854300564", balance: { amount:3500, currency:"USD" }}]

let currencies = [], result = [];
a.map((v)=>{
  let {currency, amount} = v.balance;
  if(!currencies.includes(currency)){
    currencies.push(currency);
    result.push({currency, totalAmount:amount});
  }else{
    result[currencies.findIndex(vv=>vv===currency)].totalAmount+=amount;
  }
})

My solution with native array methods map, filter and reduce

const res = arr
  // map to currency
  .map(i => i.balance.currency)
  // get unique currency
  .filter((v, i, a) => a.indexOf(v) === i)
  // map to result
  .map((i, a) => ({
    currency: i,
    totalAmount: calcTotalAmount(i)
  }));

// calculate amount
function calcTotalAmount(currency) {
  return (
    arr
      // filter to current curency
      .filter(i => i.balance.currency === currency)
      // reduce to number
      .reduce((accum, i) => accum + i.balance.amount, 0)
  );
}
Related