Average of array inside an object - javascript

I have an array of objects that contains the name and marks of students. like below
I need to calculate the 'average' marks each student has and compare the 'average' marks to get the top student. I am trying like below, I am not getting what am I missing?
var Students = [
{
name: "Bob",
marks: [78,80,89,90,68]
},
{
name: "Alin",
marks: [87,60,59,70,68]
},
{
name: "bikash",
marks: [82,60,79,60,80]
}
];
for (let i = 0; i < Students.length; i++){
var average = Students[i].reduce((total, next)=> total + next.marks) /2
}
console.log(average)
I need to have average marks of each students to compare the results of All students

Maybe this:
const students = [{
name: 'Bob',
marks: [78, 80, 89, 90, 68],
},
{
name: 'Alin',
marks: [87, 60, 59, 70, 68],
},
{
name: 'bikash',
marks: [82, 60, 79, 60, 80],
},
];
const topStudent = students
.map(student => ({
...student,
averageMark: student.marks.reduce((a, b) => a + b, 0) / student.marks.length,
}))
.sort((a, b) => a.averageMark - b.averageMark)
.pop();
console.log(topStudent);

Here we go. It returns you an array of objects with the name and the average score of the students.
Its also sorted from highest average to lowest
let arr = [
{
name: "Bob",
marks: [78,80,89,90,68]
},
{
name: "Alin",
marks: [87,60,59,70,68]
},
{
name: "bikash",
marks: [82,60,79,60,80]
}
]
let averages = arr.map(({ marks, name }) => {
let average = marks.reduce((a,v) => a + v) / marks.length
return { name , average }
}).sort((a,b) => b.average - a.average);
let [{ name }] = averages;
console.log(averages)
console.log("top student: ", name);

If you are looking for traditional loop:
const Students = [{
name: 'Bob',
marks: [78, 80, 89, 90, 68],
},
{
name: 'Alin',
marks: [87, 60, 59, 70, 68],
},
{
name: 'bikash',
marks: [82, 60, 79, 60, 80],
},
];
var average;
for (let i = 0; i < Students.length; i++){
var marks = Students[i]["marks"];
var total = 0;
console.log(marks);
for (var j = 0; j < marks.length; j++ ) {
total += marks[j];
}
average = total / marks.length;
// answer for question in the comment
var msg = Students[i]["name"] + " has average mark: " + average;
console.log(msg)
}
console.log(average)

You need to reduce the marks array of each Student, not a Student object, as this is not an array.
next is the next value in the array, not the next item in Students.
Finally, place the console.log line inside the loop so to get all the results printed out.
var Students = [
{
name: "Bob",
marks: [78,80,89,90,68]
},
{
name: "Alin",
marks: [87,60,59,70,68]
},
{
name: "bikash",
marks: [82,60,79,60,80]
}
];
for (let i = 0; i < Students.length; i++){
var average = Students[i].marks.reduce((total, next)=> total + next) / Students[i].marks.length;
console.log(average);
}

You can also extract it in a function:
var Students = [
{
name: "Bob",
marks: [78,80,89,90,68]
},
{
name: "Alin",
marks: [87,60,59,70,68]
},
{
name: "bikash",
marks: [82,60,79,60,80]
}
];
// Student avarage
var averages = []
for (let i = 0; i < Students.length; i++){
var avg = average(Students[i].marks);
console.log(Students[i].name + ": " + avg)
averages.push(avg)
}
// Total average
console.log("total average: " + average(averages))
function average(array) {
return array.reduce((total, mark) => total + mark, 0) / array.length;
}

Below is one of the ways of finding the student with maximum average using Array.reduce, Array.map.
var Students = [{name:"Alin",marks:[87,60,59,70,68]},{name:"Bob",marks:[78,80,89,90,68]},{name:"bikash",marks:[82,60,79,60,80]}];
const getTopStudent = (students) => {
//Find the avg of the current student
const formattedStudents = students.map(student => ({...student, avg: student.marks.reduce((t, m) => t+m, 0)/student.marks.length}))
return formattedStudents.reduce((res, student) => {
//Check if the avg of the student in res object is less than the avg of the current student, then return current student.
if((res.avg || 0) < student.avg){
return {
...student
}
}
return res;
}, {})
}
console.log(getTopStudent(Students))
.as-console-wrapper {
max-height: 100% !important;
}
Note: In the above example I have not considered if there are more than one student having the same avg.
Below is the example which will return all the students if the average is same
var Students = [{name:"Alin",marks:[87,60,59,70,68]},{name:"Bob",marks:[78,80,89,90,68]},{name:"bikash",marks:[82,60,79,60,80]},{name:"Joey",marks:[78,80,84,90,73]}];
const getTopStudent = (students) => {
const formattedStudents = students.map(student => ({ ...student,
avg: student.marks.reduce((t, m) => t + m, 0) / student.marks.length
}))
const finalRes = formattedStudents.reduce((res, student) => {
//if the res.avg is less than current student avg then update the res object with the new avg and the students
if ((res.avg || 0) < student.avg) {
return {
avg: student.avg,
students: [{ ...student }]
}
} else if ((res.avg || 0) === student.avg) {
//If average of the current student is same as res.avg, then push the current student to the res.students
res.students.push(student);
return res;
}
return res;
}, {});
return finalRes.students;
}
//More than one student with max avg
console.log(getTopStudent(Students));
//One student with max avg
console.log(getTopStudent(Students.slice(0,3)));
.as-console-wrapper {
max-height: 100% !important;
}

Related

Find object in array with closest value

I need to get an object in an array by the closest value. Let me explain it by an example:
const data = [
{ age: 52 },
{ age: 53 },
{ age: 54 },
{ age: 60, some: 'data' },
{ age: 66, something: 'else' },
{ age: 72 },
{ age: 78 },
{ age: 84 }
]
I do get the object by using data.find((d)=> d.age === 60). But I do not get an result if the age is 61.
In this case I would like to get the same object.
For 64 the next object ({ age: 66, something: 'else' }) should be returned.
As you can see the age value is not linear.
You can find the difference between all the numbers and whichever one is closest to zero will be your result, to achieve this I have used .reduce() with Math.abs()
const data = [ { age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 } ];
const getAge = (data, target) =>
data.reduce((acc, obj) =>
Math.abs(target - obj.age) < Math.abs(target - acc.age) ? obj : acc
);
console.log(getAge(data, 61)); // {age: 60}
console.log(getAge(data, 50)); // {age: 52}
console.log(getAge(data, -1)); // {age: 52}
console.log(getAge(data, 90)); // {age: 84}
This will also work for more generalized objects that have additional properties other than just age.
Here is a fully abstract approach to your problem:
// Saves up vertical space
const data = JSON.parse(`[{"age":52},{"age":53},{"age":54},{"age":60},{"age":66},{"age":72},{"age":78},{"age":84}]`);
function getClosestValue(list, getDifference) {
var smallestDiff = Infinity;
return list.reduce(function(closestValue, currentValue, index) {
var newDifference = Math.abs(getDifference(currentValue));
if (!index) return smallestDiff = newDifference, currentValue;
return smallestDiff = Math.min(smallestDiff, newDifference), newDifference === smallestDiff ? currentValue : closestValue;
});
}
function getClosestAge(list, age) {
return getClosestValue(list, function(listValue) {
return listValue.age - age;
});
}
console.log(getClosestAge(data, 65));
If it's always sorted you can instead use some:
// Saves up vertical space
const data = JSON.parse(`[{"age":52},{"age":53},{"age":54},{"age":60},{"age":66},{"age":72},{"age":78},{"age":84}]`);
function getClosestValue(list, getDifference) {
var smallestDiff = Infinity;
var closestValue;
list.some(function(currentValue, index) {
var newDifference = Math.abs(getDifference(currentValue));
if (!index) return smallestDiff = newDifference, closestValue = currentValue, false;
if (smallestDiff > newDifference) return smallestDiff = newDifference, closestValue = currentValue, false;
else if (smallestDiff !== newDifference) return true;
});
return closestValue;
}
function getClosestAge(list, age) {
return getClosestValue(list, function(listValue) {
return listValue.age - age;
});
}
console.log(getClosestAge(data, 65));
Assume, that your list ist not Sorted, and you do not want to sort your list. So you can pick the first object, iterate through your list and check if you get an item, which fits your requiremnt more than your currently picked item. If so, you just replace your item with the better one.
e.g.
var data = [/*...*/];
var find_age = 64; // input
var best_item = data[0]; // pick any item as best item
for (var i = 1; i < data.length; i++) {
// does date[i] match the requirement better than best_item?
if (Math.abs (best_item.age - find_age) > Math.abs (data[i].age - find_age)) {
// it does ... so update best_item
best_item = data[i];
}
}
// best_item stores the item which matches your requirement most.
If your dataset is sorted, you can optimize your runtime.
You can just sort the array by difference to lookup age:
const lookupAge = 61
const data = [
{ age: 52 },
{ age: 53 },
{ age: 54 },
{ age: 60 },
{ age: 66 },
{ age: 72 },
{ age: 78 },
{ age: 84 }
]
const result = data
.map(d => d.age)
.sort((a, b) => Math.abs(a - lookupAge) - Math.abs(b - lookupAge))
console.log('result', result)
const data = [
{ age: 52 },
{ age: 53 },
{ age: 54 },
{ age: 60 },
{ age: 66 },
{ age: 72 },
{ age: 78 },
{ age: 84 }
];
const find = 64;
const result = data.map(({ age }) => age).reduce((best, el, index) => {
if (Math.abs(find - el) < Math.abs(find - best)) {
return el;
}
return best;
}, data[0].age)
console.log(result)
With sorted data, you could take the one with the greatest value as start value an iterate from the beginning and stop the iteration if the delta grows.
var data = [{ age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 }],
result = data[data.length - 1],
age = 61;
data.some((o) => {
if (Math.abs(age - o.age) >= Math.abs(age - result.age)) return true;
result = o;
});
console.log(result);
I made a lil' snippet code to show you the way I would do this. This creates to use a findClosest method on any array of object, that expects an attribute name and a value. The function will then return the element of the array that has the closest value to the given attribute. It could be improved but this works pretty well.
document.addEventListener("DOMContentLoaded", function() {
const listElem = document.getElementById('list');
const closestElem = document.getElementById('closest');
data.forEach(elem => {
const listElemEntry = document.createElement('li');
listElemEntry.innerHTML = elem.age;
listElem.appendChild(listElemEntry);
});
const closest = data.findClosest('age', 80);
closestElem.innerHTML = closest;
});
const data = [
{ age: 52 },
{ age: 53 },
{ age: 54 },
{ age: 60 },
{ age: 66 },
{ age: 72 },
{ age: 78 },
{ age: 84 }
];
Array.prototype.findClosest = function(attr, value) {
const closestElem = { diff: Infinity, index: -1 };
this.forEach((elem, index) => {
const diff = Math.abs(elem[attr] - value);
if (diff < closestElem.diff) {
closestElem.diff = diff;
closestElem.index = index;
}
});
return this[closestElem.index][attr];
}
<h2>Elements list</h2>
<ul id="list"></ul>
<h2>Closest element</h2>
<pre id="closest"></pre>
You can find closest item of array with minimum value of differences like below;
function getClosest(data, x) {
if (data.length == 0) {
return null;
}
var index = 0;
var difference = Number.MAX_SAFE_INTEGER;
for(var i = 0; i<data.length;i++) {
if (i < data.length) {
var differ = Math.abs(data[i].age - x);
if(differ < difference) {
difference = differ;
index = i;
}
}
}
return data[index];
}
Usage:
getClosest(data, 64)
You can find the minimum difference by subtracting the given number from every element and take the absolute value and then do both higher lookup and lower lookup
it will also consider when there are 2 different closest values
const data = [
{ age: 52 },
{ age: 53 },
{ age: 55 },
{ age: 60 },
{ age: 66 },
{ age: 72 },
{ age: 78 },
{ age: 84 }
]
function minimum(given){
//let given=54
//find the mimimun different
let closest_diff=Math.min(...data.map(a=>Math.abs(a.age-given)))
//for lower closest number
let x1=data.find(a=>a.age===given-closest_diff);
//for highter closest number
let x2=data.find(a=>a.age===given+closest_diff);
//filter the number which are in array above
console.log(...new Set([x1,x2].filter(x=>x)));
}
minimum(52); //52
minimum(54); //53 and 55
minimum(63); //60 and 66
minimum(75); //72 and 78
minimum(77); //78
Suppose array isn't sorted. Following function returns result. If it find value that is equal to search value, it stops searching, so it is a small gain in performance.
function minDiff(data, val) {
let res = null;
let n = data.length;
let diffGet = (val1, val2) => Math.abs(val1 - val2);
if (n>0) {
res = data[0];
let diff = diffGet(res.age, val);
let i = 1;
while ( diff>0 && i<n ) {
if (diffGet(data[i].age, val) < diff) {
res = data[i];
diff = diffGet(res.age, val);
}
i++;
}
}
return res;
}
This is a functional approach to your problem with currying:
const data = [
{ age: 52 },
{ age: 53 },
{ age: 54 },
{
age: 60,
some: "data"
},
{
age: 66,
something: "else"
},
{ age: 72 },
{ age: 78 },
{ age: 84 }
];
const indexOfSmallest = (array) => {
if (array.length === 0) {
throw new Error("Empty array, expects at least one element");
}
return array.reduce((lowest, next, index) => {
if (next < array[lowest]) {
return index;
}
return lowest;
}, 0);
};
const getClosestIndex = (numbers, referenceNumber) => {
const diff = numbers.map(n => Math.abs(referenceNumber - n));
return indexOfSmallest(diff);
};
const createGetClosestIndex = (numbers) => (number) => getClosestIndex(numbers, number);
const createGetClosestPerson = (people) => {
return (targetAge) => {
const numbers = people.map(d => d.age);
const index = createGetClosestIndex(numbers)(targetAge);
return people[index];
};
};
const getClosest = createGetClosestPerson(data);
console.log(getClosest(1), getClosest(64));
A general purpose version of #nick-parsons excellent answer...
/**
* Find the closest number in an array.
*
* #param Number needle The number we're looking for.
* #param Array<Number|Object> haystack An array to search.
* #param String [key] We're searching an array of objects.
* Use this key to find the number in each object.
* #return Number|Object
*/
function closest (needle, haystack, key=null) {
if (key==null) {
return haystack.reduce((a, b) => Math.abs(needle - b) < Math.abs(needle - a) ? b : a);
}
return haystack.reduce((a, b) => {
if (b[key] == null) return a;
if (a[key] == null) return b;
return Math.abs(needle - b[key]) < Math.abs(needle - a[key]) ? b : a;
});
}
let arr = [ {speed: 0.1}, {speed: 0.4}, {speed: 1} ]
console.log( closest(0.5, arr, "speed").speed )
// output: 0.4
arr = [ 0.1, 0.4, 1 ]
console.log( closest(0.9, arr) )
// output: 1

How to write a javascript function that takes an array with names and scores, and returns array with letter grades and students?

I've been stuck for days. Please help! new to javascript
first I mapped the students scores, and got an array of just the number scores. Then I wrote a if/else function to take the student score and convert it to a letter grade. But how can I take this array of letter grade and list out all the students that got each grade? and then write this in es6 into the getStudentsByGrade const??
var studentScores = students.map(function (student) {
return student.score;
})
console.log(studentScores);
function toLetterGrade(studentScores) {
var textG = '';
var result = [];
for (i = 0; i < studentScores.length; i++) {
textG = '';
if (studentScores[i] >= 90) {
textG = "A";
} else if (studentScores[i] >= 80) {
textG = "B";
} else if (studentScores[i] >= 60) {
textG = "C";
} else if (studentScores[i] >= 50) {
textG = "D";
} else if (studentScores[i] >= 32) {
textG = "E";
} else {
textG = "F";
}
result.push(textG);
}
return result;
}
console.log(toLetterGrade(studentScores));
Given a list of students with a name and a score, write a function getStudentsByGrade that takes in an array of students and a set of grade boundaries and gives you an gives you an object with a list of grades and the names of students for each grade.
the output should be:
{
A: ['Dan'],
C: ['Emily', 'Daisy'],
E: ['Brendan']
}
And must be written inside the following
const getStudentsByGrade = (students, gradeBoundaries) => {
// solution in here
}
Given:
const students = [{name: 'Daisy', score: 65},
{name: 'Dan', score: 99},
{name: 'Emily', score: 77},
{name: 'Brendan', score: 49}];
const gradeBoundaries = {A: 90, B: 80, C: 60, D: 50, E: 32, F: 0};
const getStudentsByGrade = (students, gradeBoundaries) => {
// How do I do this?
}
You want to transform an input array into an object indexed by grade - the appropriate tool to transform an array into an object (or any sort of single value) is reduce. You might separate out the code that turns the score into a letter grade into its own function, for better readability. For each student, get their appropriate letter grade, and then add that student's name to that grade array in the accumulator. If that grade doesn't exist yet in the accumulator, create it as a new array first:
const makeGetGrade = gradeBoundaries => {
const gradeBoundaryArr = Object.entries(gradeBoundaries)
.map(([grade, cutoff]) => ({ grade, cutoff }))
.sort((a, b) => b.cutoff - a.cutoff);
return findGrade => gradeBoundaryArr
.find(({ grade, cutoff }) => findGrade > cutoff)
.grade;
};
const getStudentsByGrade = (students, gradeBoundaries) => {
const getGrade = makeGetGrade(gradeBoundaries);
return students.reduce((a, { name, score }) => {
const grade = getGrade(score);
if (!a[grade]) a[grade] = [];
a[grade].push(name);
return a;
}, {});
};
const students=[{name:'Daisy',score:65,},{name:'Dan',score:99,},{name:'Emily',score:77},{name:'Brendan',score:49}];
const possibleGradeBoundaries = {
A: 90,
B: 80,
C: 60,
D: 50,
E: 32,
F: 0,
};
console.log(getStudentsByGrade(students, possibleGradeBoundaries));
Try this:
const students = [{
name: 'Daisy',
score: 65,
}, {
name: 'Dan',
score: 99,
}, {
name: 'Emily',
score: 77
}, {
name: 'Brendan',
score: 49
}];
console.log('-----------------------OLD-------------------', students);
setScore(students);
var result = [];
function setScore (students) {
students.forEach(function (student, i) {
if (student.score >= 90) {
student["grade"] = "A";
} else if (student.score >= 80) {
student["grade"] = "B";
} else if (student.score >= 60) {
student["grade"] = "C";
} else if (student.score >= 50) {
student["grade"] = "D";
} else if (student.score >= 32) {
student["grade"] = "E";
} else {
student["grade"] = "F";
}
});
console.log('-----------------------NEW-------------------', students);
}
You would want something like this. With the help of Array.filter and Object.keys, you can easily get your result in the specified format.
const getStudentsByGrade = (students, gradeBoundaries) => {
let result = {};
let letterGrades = Object.keys(gradeBoundaries);
letterGrades.forEach((letterGrade, index) => {
result[letterGrade] = students.filter(student => {
if (student.score >= gradeBoundaries[letterGrade]) {
if (index > 0) {
let higherLetterGrade = letterGrades[index - 1];
if (student.score < gradeBoundaries[higherLetterGrade]) {
return student.name;
}
} else {
return student.name;
}
}
});
});
return result;
}
Then simply invoke the function with data to see the result:
let result = getStudentsByGrade(students, gradeBoundaries);
console.log(result);
This should be the result output:
{
"A":[
{
"name":"Dan",
"score":99
}
],
"B":[
],
"C":[
{
"name":"Daisy",
"score":65
},
{
"name":"Emily",
"score":77
}
],
"D":[
],
"E":[
{
"name":"Brendan",
"score":49
}
],
"F":[
]
}

ES6 Map Reduce array flatten with mapping and padding from Redux state

I am writing a React/Redux app using ES6 and I want an efficient way to map this data:
[
{total: 50, label: "C1"},
{total: 120, label: "C2"},
{total: 220, label: "C4"}
]
To something like below:
[
{50, "Category 1"},
{120, "Category 2"},
{0, "Category 3"},
{220, "Category 4"}
{0, "Category 5"},
]
The key being that it flattens the original array and relabels the label values and also pads out the missing keys.
I can do this with some ugly looking JS iterating over it but I'm struggling to get this to work with a simple and elegant reduce arrow function mechanism which I'm sure is possible.
Use .map().
var foo = [{
total: 50,
label: "C1"
},
{
total: 120,
label: "C2"
},
{
total: 220,
label: "C4"
}
];
var flatFoo = foo.map((obj) => {
return [
obj.total,
`${obj.label.substr(0, 1)}ategory ${obj.label.substr(1, 1)}`
];
});
console.log(flatFoo);
Use Array.prototype.reduce to create a hashtable to pad out the missing keys to an array of arrays - see demo below:
var arr = [{total: 50, label: "C1"},{total: 120, label: "C2"},{total: 220, label: "C4"}];
// create a hash table first
var hash = arr.reduce(function(p,c){
var key = c.label.replace(/[^\d]/g,'');
p[key] = c;
// largest key
p['count'] = (+key > (p['count'] || 0)) ? +key : p['count'];
return p;
}, Object.create(null));
// accumulate the result
var result = [];
for(var i = 0; i < hash['count']; i++) {
var el = hash[i + 1] || {};
result.push([el.total || 0, 'Category '+ (i+1)]);
}
console.log(result);
.as-console-wrapper {top: 0;max-height: 100%!important;}
Here are some options...
const data = [
{total: 50, label: "C1"},
{total: 120, label: "C2"},
{total: 220, label: "C4"}
];
const stripChars = s => (s || '').replace(/[a-z]/i, '');
function fillTo(limit, data) {
return Array
.from({length: limit}, (_, i) => (
data.find(o => stripChars(o.label) == i) ||
{ total: 0, label: `C${i}` }
))
;
}
const toMap = data => data.reduce(
(res, $1) => Object.assign(res, {
[$1.total]: `Category ${stripChars($1.label)}`
}), Object.create(null)
);
const pairwise = data => data.map(
$1 => [$1.total, `Category ${stripChars($1.label)}`]
);
console.log('fillTo', fillTo(6, data));
console.log('toMap', toMap(data));
console.log('pairwise', pairwise(data));
/**
* 1. fill the array
* 2. chose your transformer
*
* const d = fillTo(6, data);
* console.log(pairwise(d));
**/
You can first sort original array to get min and max value and then use loops to add missing elements.
var data = [
{total: 220, label: "C14"},
{total: 50, label: "C3"},
{total: 120, label: "C10"},
{total: 220, label: "C7"}
]
data.sort((a, b) => a.label.slice(1) - b.label.slice(1))
var result = [];
data.forEach(function(e, i) {
var n = e.label.slice(1);
var next = data[i + 1]
result.push([e.total, 'Category ' + n]);
if (next) {
for (var j = n; j < +(next.label.slice(1) - 1); j++) {
result.push([0, 'Category ' + (+j + 1)])
}
}
})
console.log(JSON.stringify(result, 0, 4))

How to find inside an array of objects the object that holds the highest value?

I have an array that holds several objects named student, each object student has several properties, one of which is an array named grades.
I need to create a function that loops through the student's array and finds which student object has the highest grade inside its grades array.
At the moment I am able to find the highest score, but was not able to understand how to trace back to which student it belongs to.
Here is a snippet of how the function looks like:
function bestStudent() {
var bestGrade = 0;
var student;
for(i=0; i < studentArr.length; i++) {
var student = studentArr[i];
grades = student.grades;
for(g = 0; g <grades.length; g++){
if(grades[g] > bestGrade) {
bestGrade = grades[g];
}
}
}
}
The general idea is the following: you can first map your array of students with their grades to an array of students and their highest grade in order to make it convenient to work with and avoid multiple find-max-grade calculations, and then find the largest of students' highest grade.
Just an example:
var students = [
{
name: "Student 1",
grades: [ 65, 61, 67, 70 ]
},
{
name: "Student 2",
grades: [ 50, 51, 53, 90 ]
},
{
name: "Student 3",
grades: [ 0, 20, 40, 60 ]
}
];
var highestGrades = students.map(function(stud, ind) {
// return a student's name and his highest grade (1)
return {
name: stud.name,
highestGrade: Math.max.apply(Math, stud.grades) // get a student's highest grade
};
// or return index and use it to access original value: (2)
// return {
// index: ind,
// highestGrade: Math.max.apply(Math, stud.grades)
// };
// or return the whole student: (3)
// return {
// student: stud,
// highestGrade: Math.max.apply(Math, stud.grades)
// };
// or just add 'highestGrade' property to object without modifying
// if it's ok for you to have intermediate properties in your object: (4)
// stud.highestGrade = Math.max.apply(Math, stud.grades);
// return stud;
});
// this can be done in O(n), not in O(N * logN) if required:
var bestStudent = highestGrades.sort(function(a, b) {
return b.highestGrade - a.highestGrade;
})[0]; // sort by highest grade desc and return the first (the best) one
// Here we have bestStudent with his name according to map function:
console.log(bestStudent.name + " has the highest score of " + bestStudent.highestGrade); // (1)
// console.log(students[bestStudent.index].name + " has the highest score of " + bestStudent.highestGrade); // (2)
// console.log(bestStudent.student.name + " has the highest score of " + bestStudent.highestGrade); // (3)
// console.log(bestStudent.name + " has the highest score of " + bestStudent.highestGrade); // (4)
You can rewrite this code so that it returns the whole student as the result, or its index, or its specific properties. You can also just add highestGrade property to original object if it's ok for your objects to have an additional intermediate property. It is up to you, the idea doesn't change :)
This code is pretty long, but it is readable and makes the idea of algorithm clear, it is very important since you are a beginner.
If you and your team are fans of shorter but more complex code, then you can easily rewrite it.
Just something like this:
var students = [
{
name: "Student 1",
grades: [ 65, 61, 67, 70 ]
},
{
name: "Student 2",
grades: [ 50, 51, 53, 90 ]
},
{
name: "Student 3",
grades: [ 0, 20, 40, 60 ]
}
];
var bestStudent = students.map(function(stud) {
stud.highestGrade = Math.max.apply(Math, stud.grades);
return stud;
}).sort(function(a, b) {
return b.highestGrade - a.highestGrade;
})[0];
console.log(bestStudent);
By using the same function, you can store the position or the appropriate field
function bestStudent() {
var bestStudent = {};
bestStudent.bestGrade = 0;
var student;
for(i=0; i < studentArr.length; i++) {
var student = studentArr[i];
grades = student.grades;
for(g = 0; g <grades.length; g++){
if(grades[g] > bestStudent.bestGrade) {
bestStudent.bestGrade = grades[g];
bestStudent.name = studentArr[i].name;
}
}
}
return bestStudent;
}
use lodash.js to make thing easy :)
var students=[{Grades:[1,2,3]},{Grades:[5,4,3]},{Grades:[7,77,4]}];
var studentWithBestGrade=_.map(students,function(student,position){
return [position,_.max(student.Grades)];
});
console.log(studentWithBestGrade) //[[0,3],[1,5],[2,77]]
find it JSFIDDLE
var students = [
{
name: "Student 1",
grades: [
90, 98, 80
],
getMyHighest: function(){
return Math.max.apply( Math, this.grades );
}
},
{
name: "Student 2",
grades: [
75, 85, 79
],
getMyHighest: function(){
return Math.max.apply( Math, this.grades );
}
}
,
{
name: "Student 3",
grades: [
75, 85, 99
],
getMyHighest: function(){
return Math.max.apply( Math, this.grades );
}
}
];
var student = students.sort(function(f, l) {
return l.getMyHighest() - f.getMyHighest();
})[0];
console.log(student);
You could use an array for the students with the best grade, if you have more than one.
Then check if the maxGrade is the same as the bestGrade, then push the actual student to the result set and countinue the for loop.
If maxGrade is greater than bestGrade, store the value an put the actual student in a new array.
function bestStudents(studentArr) {
var bestGrade = 0,
bestStudents = [],
i,
maxGrade;
for (i = 0; i < studentArr.length; i++) {
maxGrade = Math.max.apply(null, studentArr[i].grades);
if (maxGrade === bestGrade) {
bestStudents.push(studentArr[i]);
continue;
}
if (maxGrade > bestGrade) {
bestGrade = maxGrade;
bestStudents = [studentArr[i]];
}
}
return bestStudents;
}
var students = [{ name: "Student 1", grades: [90, 98, 99] }, { name: "Student 2", grades: [75, 85, 79] }, { name: "Student 3", grades: [75, 85, 99] }];
console.log(bestStudents(students));
.as-console-wrapper { max-height: 100% !important; top: 0; }
My solution would be using reduce in JS
Only get one student:
var bestStudent = students.reduce(function(a, student){
return Math.max.apply(null, student.grades) >
Math.max.apply(null, a.grades) ?
student : a;}, students[0]);
console.log('Best student is: ' + bestStudent.name + ' with score: '
+ Math.max.apply(null, bestStudent.grades));
Example: https://jsfiddle.net/ahx8jh5g/
In case, you want to get all students which have best grade, let's store it in an array:
var bestStudents = students.reduce(function(a, student){
var maxGradeStu = Math.max.apply(null, student.grades),
maxGradeCur = a.length > 0 ? Math.max.apply(null, a[0].grades) : 0;
if (maxGradeStu === maxGradeCur) {
return a.concat(student);
}
return maxGradeStu > maxGradeCur ? [student] : a;
}, [])
bestStudents.forEach( bestStudent => console.log('Best student is: ' bestStudent.name + ' with score: '
+ Math.max.apply(null, bestStudent.grades)));
For more detail, you can see reduce here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

populate array based on value banding

I need to create an array of data based on the "age" of a person. This banding is not linear, but grouped something like
1,2,3,4,5,6,7,8,9,10,11-15,16-20,21-30,31-50,51+
so for any set of data, I need to run through this and put the person into the appropriate age "bucket"
At the end of the loop, I want to see how many people are in each banding.
Now, I have done this by using very crude techniques (aka if this .. then .. else if .. ) ;), but was wondering if I could learn new techniques and if there was any library or function that could take a range and fill the bucket accordingly
That's what I would do:
let buckets = '1,2,3,4,5,6,7,8,9,10,11-15,16-20,21-30,31-50,51+'.split(','),
bucketByAge = {}; // our bucket-age connector where we search later on
// prepare the index
buckets.forEach(function(bucket, index) {
// get the range (+ will be replaced with 1000; for the age it is infinity)
// and transform them to numbers
let range = bucket.replace('+', '-1000').split('-').map(Number);
// if it was not a range, simulate one
range[1] = range[1] || range[0];
// go through the range and fill our connector
while (range[0] <= range[1]--) {
bucketByAge[range[1]] = index;
}
});
// search
function getBucket(age) {
return { age: age, bucketRange: buckets[bucketByAge[age]], bucketIndex: bucketByAge[age] };
}
console.log(getBucket(1));
console.log(getBucket(12));
console.log(getBucket(61));
What's good here: you build the index once and later just take the result. Very performant.
A VERY rough o(n^2) implementation
const buckets = [
{
lowerLim: 1,
upperLim: 1,
persons: 0
},
{
lowerLim: 2,
upperLim: 2,
persons: 0
},
{
lowerLim: 3,
upperLim: 5,
persons: 0
},
{
lowerLim: 6,
upperLim: 10,
persons: 0
},
{
lowerLim: 11,
persons: 0
}
];
const persons = [{name: 'john', age: 1},{name: 'john', age: 2}, {name: 'john', age: 6}, {name: 'john', age: 20}, {name: 'john', age: 40}, {name: 'john', age: 7}, {name: 'john', age: 1}];
persons.forEach(person => {
buckets.forEach(bucket => {
const age = person.age;
const bucketLowerLimit = bucket.lowerLim;
const bucketUpperLimit = bucket.upperLim || Number.MAX_SAFE_INTEGER;
if(age >= bucketLowerLimit && age <= bucketUpperLimit) {
bucket.persons++;
}
})
});
buckets.forEach(bucket => {
console.log(bucket.persons);
});
You could use a reference for all values inbetween and a separate last value.
var slots = '1,2,3,4,5,6,7,8,9,10,11-15,16-20,21-30,31-50,51+'.split(','),
last = slots.pop(),
reference = Object.create(null),
histogram = Object.create(null),
i, value;
slots.forEach(function (a) {
var temp = a.split('-'),
left = temp[0],
right = temp[1] || temp[0];
histogram[a] = 0;
reference[right] = a;
while (left < right) {
reference[left] = a;
left++;
}
});
histogram[last] = 0;
for (i = 0; i < 1000000; i++) {
value = Math.floor(Math.random() * 100);
histogram[reference[value] || last]++;
}
console.log(histogram);
console.log(reference);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources