How classify numeric values inputs in categories? - javascript

I want to create an optimized way to classify a value in certain labels.
Example
Input: 12.2, 61, 77.7
Output: "bad", "poor", "good"
I create a simple if, but maybe exist a better way
let output = null;
if (rating <= 60){ output = 'bad'}
if (rating > 60){ output = 'poor'}
if (rating > 70){ output = 'good'}
if (rating > 90){ output = 'excellent'}

You could use Array#some and iterate through an array of objects for the rating. The advantage is a good maintainable object.
ratings = [
{ value: 60, label: 'bad' },
{ value: 70, label: 'poor' },
{ value: 90, label: 'good' },
{ value: Infinity, label: 'excellent' }
]
function rating(v) {
var ratings = [{ value: 60, label: 'bad' }, { value: 70, label: 'poor' }, { value: 90, label: 'good' }, { value: Infinity, label: 'excellent' }],
label;
ratings.some(function (a) {
if (v <= a.value) {
label = a.label;
return true;
}
});
return label;
}
console.log([12.2, 61, 77.7].map(rating));
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES6 with Array#find
var ratings = [{ value: 60, label: 'bad' }, { value: 70, label: 'poor' }, { value: 90, label: 'good' }, { value: Infinity, label: 'excellent' }],
rating = v => ratings.find(a => v <= a.value).label;
console.log([12.2, 61, 77.7].map(rating));
.as-console-wrapper { max-height: 100% !important; top: 0; }

One better way is to creatively use switch:
var output = null;
var rating = parseInt(prompt("Rating?"));
switch (true) {
case (rating <= 60):
output = 'bad';
break;
case (rating > 90):
output = 'excellent';
break;
case (rating > 70):
output = 'good';
break;
case (rating > 60):
output = 'poor';
break;
}
console.log(output);
Here, the correct organisation of the lines are very important.

One fast way of doing this if you are fine your memory.
var limits = [60,70,90,100],
rates = ["bad","poor","good","excellent"],
grades = limits.reduce((g,c,i,a) => i ? g.concat(Array(c-a[i-1]).fill(rates[i]))
: g.concat(Array(c).fill(rates[0])),[]),
notes = [12.2, 61, 77.7, 89.5];
notes.forEach(n => console.log(grades[Math.round(n)]));

Related

Javascript get steps index based on array lenth

I have an array of items for which I want to create steps based on their index and the length of the array.
The minimum value should be 0 and the maximum 100.
I don't know if I'm explaining it right, but for example. I have this array:
const RAMmarks = [
{
value: 0,
label: ">1024 MB",
},
{
value: 25,
label: ">2048 MB",
},
{
value: 50,
label: ">4096 MB",
},
{
value: 75,
label: ">6144 MB",
},
{
value: 100,
label: ">8192 MB",
},
];
...but then the other one has less items:
const diskMarks = [
{
value: 0,
label: ">20 GB",
},
{
value: 33,
label: ">45 GB",
},
{
value: 66,
label: ">75 GB",
},
{
value: 99,
label: ">100 GB",
},
];
How do I get the values dynamically based on how many steps the array has?
You can do this easily with Array.from() and a map function. Due to how arrays are stored, {length: steps} acts as an array-like object with steps empty elements, then the map function transforms each empty element into its value. Try using this code:
function stepArray(min, max, steps) {
return Array.from({length: steps}, (_, i) => ((max-min)/(steps-1))*i + min);
}
console.log(stepArray(0, 100, 5));
The idea is to get the difference between max and min, divide that difference into step-1 steps (because we need to include both of the endpoints), multiply that step by the current step i, then add back the min that was subtracted in the beginning.
Note that if you are using uneven intervals, you might want to use Math.floor() to get nicer numbers. Expand the snippet below to see an example:
function stepArray(min, max, steps) {
return Array.from({length: steps}, (_, i) => Math.floor(((max-min)/(steps-1))*i + min));
}
// without Math.floor(), these would all be repeating decimals
console.log(stepArray(0, 100, 10));
Once you have this basic array of values, you can then easily transform it into your more complex array of marks.
I would do like this. Had to add the floor to make it work for your second example.
const RAMmarks = [
{
value: 0,
label: ">1024 MB",
},
{
value: 25,
label: ">2048 MB",
},
{
value: 50,
label: ">4096 MB",
},
{
value: 75,
label: ">6144 MB",
},
{
value: 100,
label: ">8192 MB",
},
];
const diskMarks = [
{
value: 0,
label: ">20 GB",
},
{
value: 33,
label: ">45 GB",
},
{
value: 66,
label: ">75 GB",
},
{
value: 99,
label: ">100 GB",
},
];
Array.prototype.addStep = function () {
return this.map((mark, index) => {
return {
label: mark.label,
value: index * Math.floor(100 / (this.length - 1)),
};
});
};
const RAMmarksStep = RAMmarks.addStep();
const diskMarksStep = diskMarks.addStep();
console.log(RAMmarksStep);
console.log(diskMarksStep);
You can use the following function to add the values to your items:
function addMarks(items) {
let steps = items.length - 1;
let step = 100 / steps;
for(let i in items) {
items[i].value = Math.round(i * step);
}
return items;
}
Based on the example you provided, you might want to swap Math.round for Math.floor

Javascript code reduction on sort with multiple fields

I have an array with data similar to this but with more fields to sort from:
const shirts= [
{
type: "T-shirt",
size: "L",
color: "Black",
},
{
type: "Dress Shirt",
size: "S",
color: "Brown",
},
{
type: "Sweatshirt",
size: "M",
color: "Red",
},
{
type: "Polo",
size: "XS",
color: "Pink",
},
...]
I have a sort function that works differently based on what is selected, for example if the user sorts by size it needs to go from XS to XL but any other option it needs to sort alphabetically. This is what I have:
//sort is the option the user decided to sort by
SortedShirts(sort,shirts){
var newShirtArray=[];
var size1=0;
var size2=0;
if(sort === "type"){
newShirtArray= shirts.sort(function(shirt1,shirt2){
if (shirt1.type> shirt2.type){
return 1;
}
else{
return -1;
}
});
}
else if(sort === "color"){
newShirtArray = shirts.sort(function(shirt1,shirt2){
if (shirt1.color > shirt2.color){
return 1;
}
else{
return -1;
}
});
}
else if(sort === "size"){
newShirtArray = shirts.sort(function(shirt1,shirt2){
if(shirt1.size==="XS"){
size1=0;
}
else if(shirt1.size==="S"){
size1=1;
}
else if(shirt1.size==="M"){
size1=2;
}
else if(shirt1.size==="L"){
size1=3;
}
else if(shirt1.size==="XL"){
size1=4;
}
if(shirt2.size==="XS"){
size2=0;
}
else if(shirt2.size==="S"){
size2=1;
}
else if(shirt2.size==="M"){
size2=2;
}
else if(shirt2.size==="L"){
size2=3;
}
else if(shirt2.size==="XL"){
size2=4;
}
if (size1 > size2){
return 1;
}
else{
return -1;
}
});
}
This seems repetitive to me because type and color sort the same way just with a different field and I feel I can put that into one sort but I am unsure on how to do it. I'm wondering if there is something like this
if(sort === "type" || sort === "color"){
newShirtArray= shirts.sort(function(shirt1,shirt2){
if (shirt1.fieldOf(sort) > shirt2.fieldOf(sort)){
return 1;
}
else{
return -1;
}
});
}
Or another way to reduce my code?
You can use a switch statement:
switch (sort) {
case 'color':
case 'type':
newShirtArray = shirts.sort(/* ... */);
break;
case 'size':
newShirtArray = shirts.sort(/* ... */);
break;
}
For converting you shirt size to number, you can use an object:
const ShirtSizes = {
XS: 0, S: 1, M: 2, L: 3, XL: 4,
};
const shirt1Size = ShirtSizes[shirt1.size];
If your environment allows this, use more concise ES2015 arrow functions:
case 'size':
newShirtArray = shirts.sort((s1, s2) => ShirtSizes[s1.size] - ShirtSizes[s2.size]);
break;
For type and color, as you guessed, you can use this:
case 'color':
case 'type':
newShirtArray = shirts.sort((s1, s2) => {
if (s1[sort] > s2[sort]) {
return 1;
} else if (s1[sort] < s2[sort]) {
return -1;
} else {
return 0;
}
});
I hope this will help a bit.
You can access JSON object's key using bracket.
const shirts = [
{
type: "T-shirt",
size: "L",
color: "Black",
},
{
type: "Dress Shirt",
size: "S",
color: "Brown",
},
{
type: "Sweatshirt",
size: "M",
color: "Red",
},
{
type: "Polo",
size: "XS",
color: "Pink",
},
]
function SortedShirts(sort, shirts) {
var newShirtArray = [];
var size1 = 0;
var size2 = 0;
if (sort === "type" || sort === "color") {
newShirtArray = shirts.sort(function (shirt1, shirt2) {
if (shirt1[sort] > shirt2[sort]) {
return 1;
}
else {
return -1;
}
});
}
else if (sort === "size") {
const sizes = ['XS', 'S', 'M', 'L', 'XL']
newShirtArray = shirts.sort(function (shirt1, shirt2) {
const size1 = sizes.indexOf(shirt1.size)
const size2 = sizes.indexOf(shirt2.size)
if (size1 > size2) {
return 1;
}
else {
return -1;
}
});
}
}

Average of array inside an object

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;
}

Need help to make validation when two range contradict

I have an object name config in which i have "from" and "to".
const config = {
"0": {
id: 0,
from: 0,
to: 10,
hex: null
},
"1": {
id: 1,
from: 11,
to: 20,
hex: null
},
"2": {
id: 2,
from: 21,
to: 30,
hex: null
},
"3": {
id: 3,
from: 31,
to: 40,
hex: null
},
"4": {
id: 4,
from: 41,
to: 50,
hex: null
}
};
I have to check that now range will contradict with each other eg: form:0 => to:10 and from:5=> to:20
here the from of second is contradict because 5 lies between 0 to 10
i have tried following but doesn't full-fill my requirement
function found(conf) {
let isFound = false;
for (let obj in conf) {
for (let x in conf) {
if (
conf[obj].id !== conf[x].id &&
(conf[x].from >= conf[obj].from && conf[x].to <= conf[obj].from)
) {
console.log(conf[obj], conf[x]);
isFound = true;
break;
}
}
if (isFound) break;
}
return isFound;
}
console.log(found(config));
Create a single array by combining all ranges
const arr = Object.entries(config).map(([a, b]) => b).flatMap(({from, to}) => RANGE(from, to))
where RANGE is method which return array of given ranges:
const RANGE = (a,b) => Array.from((function*(x,y){
while (x <= y) yield x++;
})(a,b));
Then find duplicates in the given arr using the following function:
function findDuplicate(array) {
var object = {};
var result = [];
array.forEach(function(item) {
if (!object[item]) object[item] = 0;
object[item] += 1;
});
for (var prop in object) {
if (object[prop] >= 2) {
result.push(prop);
}
}
return result;
}
const duplicates = findDuplicate(arr)
Then finally check duplicates.length
Try renaming your variables so they make sense.
Your logic is: IDs don't match and inner is after outer, but before outer's from.
There will never be a case where this will return true.
const config = {
"0": { id: 0, from: 0, to: 10, hex: null },
"1": { id: 1, from: 11, to: 20, hex: null },
"2": { id: 2, from: 21, to: 30, hex: null },
"3": { id: 3, from: 31, to: 40, hex: null },
"4": { id: 4, from: 41, to: 50, hex: null }
};
console.log(found(config));
function found(conf) {
for (let outer in conf) {
for (let inner in conf) {
let idsDontMatch = conf[outer].id !== conf[inner].id;
let innerFromGteOuterFrom = conf[inner].from >= conf[outer].from;
let innerToLteOuterFrom = conf[inner].to <= conf[outer].from;
let innerAfterOuterButBeforeOuterFrom = innerFromGteOuterFrom && innerToLteOuterFrom;
if (idsDontMatch && innerAfterOuterButBeforeOuterFrom) {
console.log(conf[outer], conf[inner]);
return true;
}
}
}
return false;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

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":[
]
}

Categories

Resources