计算包含嵌套对象的数组的平均值



我试图通过对象数组循环并计算包含几个不同键的嵌套对象的平均值。这是起始数组:

[{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]

这是我的目标:

{2017:6.5,2018:9,2019:7}

现在它返回正确的2017年,但NaN为2018年和2019年。如果有人有更好的方法来解决这个问题,不需要这么多,请提供给。

这是我到目前为止所尝试的。我一直在寻找,但没有真正找到任何我可以使用的。


const testObject = [{
id: 4,
course: "math",
values: {
2017: 8,
2018: 9
}
},
{
id: 5,
course: "English",
values: {
2017: 8,
2018: 9
}
},
{
id: 4,
course: "math",
values: {
2017: 5,
2019: 7
}
},
{
id: 4,
course: "english",
values: {
2017: 5,
2019: 7
}
},
]
//First I filter out the id 4 and course Math
const mathid1 = testObject.filter((e) => e.id === 4 && e.course === "math");
//I than find all the different years
const ArrayOfAllYears = []
mathid1.map((element) => {
ArrayOfAllYears.push(Object.keys(element.values));
})
//I here find all the different years
const withDuplicates = ArrayOfAllYears.reduce(function(arrayOne, arrayTwo) {
return arrayOne.concat(arrayTwo);
}, []);
const withoutDuplicates = Array.from(new Set(withDuplicates));
//Here I just create the calculate average function
const Result = {}
const calculateAverage = (array) => {
const sum = array.reduce((a, b) => a + b);
return sum / array.length;
};
const newObj = {}
withoutDuplicates.map((year) => {
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
})
newObj[year] = calculateAverage(reformattedArray)
})
console.log(newObj)
// I want to calculate the average of the mathid1 values and return it on a Object like {2017:..,2018..}

这个问题有两个简单的步骤。

首先,您需要将数组简化为具有年份和值的对象:

// this outputs
// { 2017: [8, 5], 2018: [9], 2019: [7] }
function byYear(array) {
// take each item of an array
return array.reduce((acc, data) => {
// take the values of that item
Object.entries(data.values).forEach(([year, value]) => {
// and map all the values to years
acc[year] = acc[year] || []
acc[year].push(value)
})
return acc
}, {})
}

第二步就是取平均值:

function average(object) {
const averages = {}
for (let key in object) {
averages[key] = object[key].reduce((sum, value) => sum + value) / object[key].length 
}
return averages
}

现在你把它们放在一起:

average(byYear(input))

在这里,input是经过过滤的数组。作为整个代码片段:

function byYear(array) {
return array.reduce((acc, data) => {
Object.entries(data.values).forEach(([year, value]) => {
acc[year] = acc[year] || []
acc[year].push(value)
})
return acc
}, {})
}
function average(object) {
const averages = {}
for (let key in object) {
averages[key] = object[key].reduce((sum, value) => sum + value) / object[key].length 
}
return averages
}
const output = average(byYear([{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]))
console.log(output)

当前代码的问题在于如何构建reformattedArray变量。首先,注意映射函数隐式地在当前对象中缺少年份时返回undefined:

let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
// There is an implicit return undefined, right here...
})

使用数组.map方法时,数组的每一项都将被map函数的返回值所替换。在年份不存在的情况下,它将不会进入if块,因此它在到达函数结束时隐式返回undefined。

因此,最终您要做的就是从这个数组中删除undefined条目,您的代码将按原样工作。


这样做的一种方法是在数组上使用.filter(Boolean),这将删除任何虚假条目(undefined是)。如:

let reformattedArray = mathid1.map(obj => {
/* code here */
}).filter(Boolean); // Note the filter here...

下面是修改后的代码片段:

const testObject = [{
id: 4,
course: "math",
values: {
2017: 8,
2018: 9
}
},
{
id: 5,
course: "English",
values: {
2017: 8,
2018: 9
}
},
{
id: 4,
course: "math",
values: {
2017: 5,
2019: 7
}
},
{
id: 4,
course: "english",
values: {
2017: 5,
2019: 7
}
},
]
//First I filter out the id 4 and course Math
const mathid1 = testObject.filter((e) => e.id === 4 && e.course === "math");
//I than find all the different years
const ArrayOfAllYears = []
mathid1.map((element) => {
ArrayOfAllYears.push(Object.keys(element.values));
})
//I here find all the different years
const withDuplicates = ArrayOfAllYears.reduce(function(arrayOne, arrayTwo) {
return arrayOne.concat(arrayTwo);
}, []);
const withoutDuplicates = Array.from(new Set(withDuplicates));
//Here I just create the calculate average function
const Result = {}
const calculateAverage = (array) => {
const sum = array.reduce((a, b) => a + b);
return sum / array.length;
};
const newObj = {}
withoutDuplicates.map((year) => {
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
}).filter(Boolean)
newObj[year] = calculateAverage(reformattedArray)
})
console.log(newObj)
// I want to calculate the average of the mathid1 values and return it on a Object like {2017:..,2018..}

  1. 按年分组
  2. 计算平均。
const items=[{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]
const groupedValues=items.reduce((groupedValues,item)=>{
Object.entries(item.values).forEach(([year,value])=>{
if(groupedValues[year]){
groupedValues[year]={value:groupedValues[year].value+value,items:groupedValues[year].items+1};
} else {
groupedValues[year]={value,items:1};
}
});
return groupedValues;
},{})
console.log(groupedValues);
const result = Object.entries(groupedValues).reduce((result,item)=>{
result[item[0]]=item[1].value/item[1].items;
return result;
},{})
console.log(result);

我建议将年份信息提取到地图中:

/** @type {Map<string, number[]} */
const years = new Map();
testObject.forEach((obj) => {
Object.keys(obj.values).forEach((key) => {
if (!years.has(key)) years.set(key, []);
years.set(key, [...years.get(key), obj.values[key]]);
});
});

然后你可以简单地循环映射并创建结果对象:

const result = {};
years.forEach((values, key) => {
Object.defineProperty(result, key, {
value: values.reduce((acc, val) => acc + val) / values.length,
enumerable: true,
});
});
console.log(result);

它应该输出:

{ '2017': 6.5, '2018': 9, '2019': 7 }

最新更新