HellaDev
HellaDev

Reputation: 408

Best way to iterate a javascript object of data and finding the average

I am trying to iterate an object and display the average for each key inside the object.

Say I have:

data = {
  totals: {
    item1: {
      time: 15,
      speed: 20,
      amount: 12,
      units: 29
    },
    item2: {
      time: 3,
      speed: 75,
      amount: 14,
      units: 3
    },
    item3: {
      time: 19,
      speed: 4,
      amount: 44,
      units: 365
    }
  }
}

What is the best way to create an average of those values like this:

averages = {
  time: 12,
  speed: 33,
  amount: 40,
  units: 132
}

I know to divide the total of each key's value by the total items, but I am struggling to find a good way to iterate each item and add the totals up. The items will be dynamically created depending on the entries for each account.

Is using a for .. in loop the best way? If so how would I use that to accomplish this? something like below?

function findAverage() {
  const averages = {
    time: 0,
    speed: 0,
    amount: 0,
    units: 0
  }

  const totalItems = Object.keys(data.totals).length 

  for (item in data.totals) {
    averages.time += item.time;
    averages.speed += item.speed;
    averages.amount += item.amount;
    averages.units += item.units;
  }

  averages.time = averages.time / totalItems;
  averages.speed = averages.speed / totalItems;
  averages.amount = averages.amount / totalItems;
  averages.units = averages.units / totalItems;

  return averages;
}

Is there a better way to go about this problem? Am I totally off in what I am trying to do here?

Upvotes: 0

Views: 78

Answers (3)

zhuravlyov
zhuravlyov

Reputation: 503

So if you need more performance you have to use for loop. For loop is faster then forEach and forEach is faster then for in loop. Here you can find performance benchmarks https://jsperf.com/for-vs-foreach/75

This code as an example for unlimited amount of items and unlimited amount of values.

"use strict";

let data = {
  totals: {
item1: {
  time: 15,
  speed: 20,
  amount: 12,
  units: 29
},
item2: {
  time: 3,
  speed: 75,
  amount: 14,
  units: 3
},
item3: {
  time: 19,
  speed: 4,
  amount: 44,
  units: 365
}
  }
};

let items = Object.keys(data.totals);
let max = items.length;
let keys = Object.keys(data.totals[items[0]]);
let averages = {};

for (let i = 0, max = keys.length; i < max; i++) {
    averages[keys[i]] = 0;
}

for (let i = 0; i < max; i++) {
    for (let j = 0, max2 = keys.length; j < max2; j++) {
        averages[keys[j]] += data.totals[items[i]][keys[j]] || 0;
    }
}

for (let i = 0, max2 = keys.length; i < max2; i++) {
    averages[keys[i]] = Math.floor(averages[keys[i]]/max);
}

console.log(averages);

Also topicstarter has a mistake

averages = { time: 12, speed: 33, amount: 40, units: 132 }

amount value can't be 40 (12 + 14 + 44 => 70 / 3 = 23)

Upvotes: 1

Piotr Pasieka
Piotr Pasieka

Reputation: 2203

Instead of loops you can use Object.keys and reduce method on array

function calculateAverage(totals) {
  const items = Object.keys(totals).reduce((result, item) => result.concat(totals[item]), [])
  const itemsCount = items.length;
   
  const metricsTotals = items.reduce((sums, item) => {
    sums.time += item.time
    sums.speed += item.speed
    sums.amount += item.amount
    sums.units += item.units
    return sums
  }, { time: 0, speed: 0, amount: 0, units: 0})

  return Object.keys(metricsTotals).reduce((average, metric) => {
    average[metric] = Math.floor(metricsTotals[metric] / itemsCount)
    return average
  }, {})
}

var data = {
  totals: {
    item1: {
      time: 15,
      speed: 20,
      amount: 12,
      units: 29
    },
    item2: {
      time: 3,
      speed: 75,
      amount: 14,
      units: 3
    },
    item3: {
      time: 19,
      speed: 4,
      amount: 44,
      units: 365
    }
  }
}


const averages= calculateAverage(data.totals)
console.log(averages)

Upvotes: 3

Nina Scholz
Nina Scholz

Reputation: 386654

You could take the wanted properties and the object and iterate and take then part of the lnght for adding to the average.

var data = { totals: { item1: { time: 15, speed: 20, amount: 12, units: 29 }, item2: { time: 3, speed: 75, amount: 14, units: 3 }, item3: { time: 19, speed: 4, amount: 44, units: 365 } } },
    averages = {},
    keys = ['time', 'speed', 'amount', 'units'];

keys.forEach(function (k) {
    averages[k] = 0;
});

Object.keys(data.totals).forEach(function (l, _, ll) {
    keys.forEach(function (k) {
        averages[k] += data.totals[l][k] / ll.length;
    });
});

// flooring if neccessary
keys.forEach(function (k) {
    averages[k] = Math.floor(averages[k]);
});


console.log(averages);

Upvotes: 1

Related Questions