Reputation: 589
I've been stuck on this for a while. Thanks for any help!
I have these two JavaScript (es6) arrays:
const examListArray = [
{examId: 'eee1', examName: 'Qtr 1 Exam'},
{examId: 'eee2', examName: 'Qtr 2 Exam'},
{examId: 'eee3', examName: 'Qtr 3 Exam'},
{examId: 'eee4', examName: 'Final Exam'},
]
const examScoresArray = [
{resultId: 'rrr01', examId: 'eee1', studentId: 'sss1', grade: 78},
{resultId: 'rrr02', examId: 'eee1', studentId: 'sss2', grade: 82},
{resultId: 'rrr03', examId: 'eee1', studentId: 'sss3', grade: 93},
{resultId: 'rrr04', examId: 'eee1', studentId: 'sss4', grade: 85},
{resultId: 'rrr05', examId: 'eee1', studentId: 'sss5', grade: 78},
{resultId: 'rrr06', examId: 'eee1', studentId: 'sss6', grade: 84},
{resultId: 'rrr07', examId: 'eee2', studentId: 'sss1', grade: 89},
{resultId: 'rrr08', examId: 'eee2', studentId: 'sss2', grade: 88},
{resultId: 'rrr09', examId: 'eee2', studentId: 'sss3', grade: 81},
{resultId: 'rrr10', examId: 'eee2', studentId: 'sss4', grade: 95},
]
Here's the reduce function that I have so far, but need help with the Min, Max, and Avg parts, the examCount works perfectly.
const reducedExams = (list, scores) => {
const countReducer = (scores, currentId) => {
const countNum = scores.reduce((result, current) => {
const foundAMatch = currentId === current.examId
if (foundAMatch) {
result++;
}
return result;
}, 0);
return countNum
}
const minScoreReducer = (scores, currentId) => {
/*Need Help Here Please!*/
}
const maxScoreReducer = (scores, currentId) => {
/*Need Help Here Please!*/
}
const avgScoreReducer = (scores, currentId) => {
/*Need Help Here Please!*/
}
const resultData = list.reduce((result, current) => {
const currentId = current.examId
const examCount = countReducer(scores, currentId)
const minScore = minScoreReducer(scores, currentId)
const maxScore = maxScoreReducer(scores, currentId)
const avgScore = avgScoreReducer(scores, currentId)
return {
...result,
[current.examName]: {
...result[current],
examCount,
minScore,
maxScore,
avgScore,
}
}
}, {})
return resultData
}
const examListTable = reducedExams(examListArray, examScoresArray)
console.log('exam table output', examListTable)
What I'm trying to do is is loop through my Exams List and make a table that will show something like this.
_____________Count____Min____Max___Avg
Qtr 1 Exam:____6______78_____93___83.333
Qtr 2 Exam:____4______81_____95___88.25
Qtr 3 Exam:____0______0_____0_____0
Final Exam:____0______0_____0_____0
Any help would be greatly appreciated!
And one last thing... if I'm going about this all wrong, ie, unnecessarily using reduce instead of some type of loop, Please by all means, I'm open to any suggestions. This feels very messy.
My stack: React, Redux, Firebase/FireStore
I always upvote! btw Thanks again!
Upvotes: 0
Views: 590
Reputation: 5797
The trick to solving these problems is breaking them down into smaller objectives.
The first objective being to categorise scores by exam and the second being to derive metrics.
Arrays can typically be categorised into object form using reduce()
.
Math
is often useful for simple processes such as finding min
/ max
values.
See below for a practical example.
// Input.
const exams = [{examId: 'eee1', examName: 'Qtr 1 Exam'},{examId: 'eee2', examName: 'Qtr 2 Exam'},{examId: 'eee3', examName: 'Qtr 3 Exam'},{examId: 'eee4', examName: 'Final Exam'}]
const scores = [{resultId: 'rrr01', examId: 'eee1', studentId: 'sss1', grade: 78},{resultId: 'rrr02', examId: 'eee1', studentId: 'sss2', grade: 82},{resultId: 'rrr03', examId: 'eee1', studentId: 'sss3', grade: 93},{resultId: 'rrr04', examId: 'eee1', studentId: 'sss4', grade: 85},{resultId: 'rrr05', examId: 'eee1', studentId: 'sss5', grade: 78},{resultId: 'rrr06', examId: 'eee1', studentId: 'sss6', grade: 84},{resultId: 'rrr07', examId: 'eee2', studentId: 'sss1', grade: 89},{resultId: 'rrr08', examId: 'eee2', studentId: 'sss2', grade: 88},{resultId: 'rrr09', examId: 'eee2', studentId: 'sss3', grade: 81},{resultId: 'rrr10', examId: 'eee2', studentId: 'sss4', grade: 95}]
// Math.Average.
Math.average = (...args) => (args.reduce((total, x) => total + x, 0) / args.length)
// Categorise.
const categorise = scores => scores.reduce((output, score) => {
const {examId: id, grade} = score
if (output[id]) output[id].push(grade)
else output[id] = [grade]
return output
}, {})
// Metrics.
const getMetrics = (exams, scores) => exams.map(exam => {
const {examId: id, examName: name} = exam
const s = scores[id] || []
const isEmpty = !s.length
return {
name,
count: s.length,
min: isEmpty ? 0 : Math.min(...s),
max: isEmpty ? 0 : Math.max(...s),
average: isEmpty ? 0 : Math.average(...s)
}
})
// Output + Proof.
const categorisedScores = categorise(scores)
const metrics = getMetrics(exams, categorisedScores)
console.log(metrics)
Upvotes: 1
Reputation: 322
You can use the array method filter
to get the target you want and process.
const reducedExams = (list, scores) => {
const countReducer = (scores, currentId) => {
const matchedScores = scores.filter(score => score.examId === currentId);
return matchedScores.length;
}
const minScoreReducer = (scores, currentId) => {
const count = scores.filter(score => score.examId === currentId)
.reduce((min, score) => {
return score.grade < min ? score.grade : min;
}, Number.MAX_VALUE);
if (count === Number.MAX_VALUE) {
return 0;
}
return count;
}
const maxScoreReducer = (scores, currentId) => {
const count = scores.filter(score => score.examId === currentId)
.reduce((max, score) => {
return score.grade > max ? score.grade : max;
}, Number.MIN_VALUE);
if (count === Number.MIN_VALUE) {
return 0;
}
return count;
}
const avgScoreReducer = (scores, currentId) => {
let num = 0;
const total = scores.filter(score => score.examId === currentId)
.reduce((sum, score) => {
num++;
sum += score.grade;
return sum;
}, 0);
if (num === 0) {
return 0;
}
return total / num;
}
const resultData = list.reduce((result, current) => {
const currentId = current.examId
const examCount = countReducer(scores, currentId)
const minScore = minScoreReducer(scores, currentId)
const maxScore = maxScoreReducer(scores, currentId)
const avgScore = avgScoreReducer(scores, currentId)
return {
...result,
[current.examName]: {
...result[current],
examCount,
minScore,
maxScore,
avgScore,
}
}
}, {})
return resultData;
}
const examListTable = reducedExams(examListArray, examScoresArray)
console.log('exam table output', examListTable)
Upvotes: 1