Reputation: 35
I am working on a project comparing the age of wine to it's score and want to make a line chart with age on the X axis and average score for each age on the Y. I have an array of objects each containing two elements, age and score and want to group by each age and find the average score for that age.
I have a CSV file created by doing a request from Global Wine Score that has vintage, review date, score etc. In python, I created a column for age by subtracting the review year from the vintage. In Javascript I created a new array doing this:
var newArr = countryFilter.map(function(elem) {
return {
age: elem.wine_age,
score: elem.score
};
});
here is what my array looks like:
0: Object { age: "116", score: "98.51" }
age: "116"
score: "98.51"
1: Object { age: "113", score: "84.46" }
age: "113"
score: "84.46"
length: 23258
I tried this, but any object with more than 1 unique score yields NAN.
const reduced = newArr.reduce(function(m, d){
if(!m[d.age]){
m[d.age] = {...d, count: 1};
return m;
}
m[d.age].score += d.score;
m[d.age].count += 1;
return m;
},{});
const result = Object.keys(reduced).map(function(k){
const item = reduced[k];
return {
wine_age: item.age,
score: item.score/item.count,
}
})
console.log(result);
How would I go about finding the average wine score per wine age?
Upvotes: 1
Views: 126
Reputation: 23654
Here's one way to get it. You'll end up with an object of age:average pairs:
let grps={}
data.forEach(g => grps.hasOwnProperty(g.age)? // first we set up the age -> array of scores object
grps[g.age].push(+g.score) : grps[g.age] = [+g.score]),
avgs = Object.assign(...Object.entries(grps) // then we take that object and calculate the avg based on the total score tally / number of scores
.map(g=>({[g[0]]:
(g[1].reduce((b,a)=> b+a)/grps[g[0]].length).toFixed(2)}))) // reduce is a perfect vehicle for tallying up arrays of numbers.
let data = [
{ age: "113", score: "84.46" },
{ age: "113", score: "88.23" },
{ age: "112", score: "84.46" },
{ age: "113", score: "10.58" },
]
let grps={}, s = data.forEach(g=>grps.hasOwnProperty(g.age)?grps[g.age].push(+g.score) : grps[g.age] = [+g.score]), avgs = Object.assign(...Object.entries(grps).map(g=>({[g[0]]:(g[1].reduce((b,a)=> b+a)/grps[g[0]].length).toFixed(2)})))
console.log(avgs)
Upvotes: 0
Reputation: 31815
You could group the objects in sub-arrays by age, then reduce the sub-arrays into single objects with the average score:
const input = [
{ age: "113", score: "84.46" },
{ age: "113", score: "88.23" },
{ age: "112", score: "84.46" },
{ age: "113", score: "10.58" },
];
const result = input
.sort((a, b) => a.age - b.age)
.reduce(
(acc, cur, i, { [i - 1]: last }) =>
cur.age === last?.age
? Object.assign([...acc], {
[acc.length - 1]: [...acc[acc.length - 1], cur],
})
: [...acc, [cur]],
[]
)
.map((x) => ({
age: x[0].age,
score: x.reduce((a, b) => a + Number(b.score), 0) / x.length,
}));
console.log(result);
Upvotes: 1