问题描述
我为每个用户提供了多门课程的用户评分。我需要获得每个课程代码的平均用户评分。
获得这些平均值的最有效方法是什么?可以在以下两种格式中的任何一种中找到数据(尽管最好第二种格式更好)。
我想对数组进行排序,然后聚合值,直到找到不同的课程代码,但我对数组函数不是很擅长,我想知道是否有使用哈希图更快的方法来做到这一点,{{1} }、.map
、sets
。请帮助我找到一个有效的解决方案,因为有很多用户,我希望它尽可能快,以便网站加载速度更快。
reduce
[
[
{courseCode: "SYD393",rating: 3},{rating: 3,courseCode: "STA244"},{courseCode: "STA255",rating: 5},{rating: 5,courseCode: "CSE201"},{courseCode: "CSE255",rating: 4},{rating: 2,courseCode: "CSE202"},{courseCode: "ASD323",],[
{courseCode: "ASD323",{courseCode: "SYD393",rating: 1},//...more arrays for each user
];
解决方法
看起来这与您自己发布的答案相似,只是我将 Map 的平均分成了自己的步骤。
const data = [[{ SYD393: 3 },{ STA244: 4 },{ STA255: 5 },{ CSE255: 4 },{ ASD323: 5 },],[{ ASD323: 5 },{ SYD393: 1 },];
const avg = a => a.reduce((a,b) => a + b,0) / a.length,sums = data.flat().reduce((a,o) => {
const [[c,r]] = Object.entries(o);
a.set(c,(a.get(c) ?? []).concat(r));
return a;
},new Map),avgs = new Map([...sums.entries()].map(([c,rs]) => [c,avg(rs)])),res = data.map(us => us.map(o => {
const [k] = Object.keys(o); return { [k]: avgs.get(k) }
}));
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
尽管使用您的第一个数据形状更简单,因为无需纠缠 Object.entries/keys
。
const data = [[{ courseCode: "SYD393",rating: 3 },{ rating: 3,courseCode: "STA244" },{ courseCode: "STA255",rating: 5 },{ rating: 5,courseCode: "CSE201" },{ courseCode: "CSE255",rating: 4 },{ rating: 2,courseCode: "CSE202" },{ courseCode: "ASD323",[{ courseCode: "ASD323",{ courseCode: "SYD393",rating: 1 },{ courseCode: c,rating: r }) =>
(a.set(c,(a.get(c) ?? []).concat(r)),a),res = data.map(us => us.map(({ courseCode: c }) => ({ courseCode: c,rating: avgs.get(c) })));
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
这应该会让你得到最终结果。它将每组数组映射到一个累加评分的reduce,然后通过另一个map/reduce组合获得最终输出。
let data = [
[
{courseCode: "SYD393",rating: 3},{rating: 3,courseCode: "STA244"},{courseCode: "STA244",rating: 4},{courseCode: "STA255",rating: 5},{rating: 1,courseCode: "SYD393"},{rating: 5,courseCode: "CSE201"},{courseCode: "CSE255",{rating: 2,courseCode: "CSE202"},{courseCode: "ASD323",[
{rating: 3,{courseCode: "SYD393",rating: 1},];
let newdata = data.map(e => e.reduce((b,a) => {
let i = b.findIndex(e => e.courseCode === a.courseCode);
if (i > 0) b[i].ratings.push(a.rating);
else b.push({ ...a,ratings: [a.rating] });
return b;
},[]).map(e => ({ [e.courseCode]: e.ratings.reduce((b,a) => b + a,0) / e.ratings.length
})))
console.log(newdata)
更新: 我的一个朋友提出了以下建议:
function averageRatings(data)
{
let step1 = data.flat()
let step2 = step1.reduce((c,i) => { if (!c.hasOwnProperty(i.courseCode)) {c[i.courseCode] = []} c[i.courseCode].push(i.rating); return c},{})
let step3 = Object.entries(step2).map(([k,i]) => { return { courseCode: k,avg: i.reduce((c,v) => c+v) / i.length }})
}
它应该能够在 O(n) 时间内创建我正在寻找的内容,因为它使用了哈希表,但我不确定,因为它确实做了许多部门(昂贵的操作)和计算机 i.length 多次。
>更简洁的版本:
function averageRatings(data)
{
return Object.entries(data.flat().reduce((c,{})).map(([k,v) => c+v) / i.length }})
}
我在朋友的帮助下能想出的最快版本:
function averageArray(array) {
let sum = 0;
let i = 0;
for (; array[i]; i++) {
sum += array[i];
}
return sum / i;
}
function getAverageRatings(data){
return Object.entries(
data
.flat()
.reduce((c,i) => {
if (!c.hasOwnProperty(i.courseCode)) {
c[i.courseCode] = [];
}
c[i.courseCode].push(i.rating);
return c;
},{})
).map(([k,i]) => {
return { courseCode: k,avg: averageArray(i) };
});
}
通过删除 .length()
和 .reduce()
操作/函数回调进一步优化代码
这可能不会像您想象的那样影响您的网站,但这是我的建议: 如果您只担心平均评分,那么完美的方法是在内部使用 Array#reduce 和 Array#map:
const myUsers = [
[
{courseCode: "SYD393",];
// The map function iterates throught myUsers array
// The reduce function will summ all the evaluation ratings
// the user.length divisor will transform the sum into an avarage
const usersAverageEval = myUsers.map(user => (
(user.reduce((accumulator,line) => (accumulator + line.rating),0))/user.length
))
这会产生更具可读性的代码,但不是最具执行力的方式。如果您只关心性能,我会使用您已经在使用的基本 for 循环,因为它们比这个 Array 方法快三到四倍。您可以在此处验证我所说的内容并了解更多信息:https://leanylabs.com/blog/js-forEach-map-reduce-vs-for-for_of/