Sort by two properties conditionally [duplicate] - javascript

This question already has answers here:
How to sort an array of objects by multiple fields?
(38 answers)
Closed 4 years ago.
Consider the following scenario, where I have to sort a list of students by both name and scores.
[
{
name: 'Max',
score: 94
},
{
name: 'Jerome',
score: 86
},
{
name: 'Susan',
score: 86
},
{
name: 'Abel',
score: 86
},
{
name: 'Kevin',
score: 86
}
]
I want to sort the list by the student who scored the highest, but if two or more students have the same score, then I want to sort those students alphabetically. For the above case, the result should be as below:
[
{
name: 'Max',
score: 94
},
{
name: 'Abel',
score: 86
},
{
name: 'Jerome',
score: 86
},
{
name: 'Kevin',
score: 86
},
{
name: 'Susan',
score: 86
}
]
How can I achieve this? Is there any lodash function that I can use, or is it possible with pure JavaScript?

No library needed, just compare the scores, and if that comes out to 0, compare the names:
const arr=[{name:'Max',score:94},{name:'Jerome',score:86},{name:'Susan',score:86},{name:'Abel',score:86},{name:'Kevin',score:86}]
console.log(
arr.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))
);

Try the following:
var arr = [
{
name: 'Max',
score: 94
},
{
name: 'Jerome',
score: 86
},
{
name: 'Susan',
score: 86
},
{
name: 'Abel',
score: 86
},
{
name: 'Kevin',
score: 86
}
];
arr.sort((a,b)=>{
return (b.score - a.score) || a.name.localeCompare(b.name);
});
console.log(arr);

You could sort by _.sortBy and take a list of propertis and another for the order.
var array = [{ name: 'Max', score: 94 }, { name: 'Jerome', score: 86 }, { name: 'Susan', score: 86 }, { name: 'Abel', score: 86 }, { name: 'Kevin', score: 86 }];
console.log(_.sortBy(array, ['score', 'name'], ['desc', 'asc']))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

First sort the array by score and if both scores are equal then sort it by name. Consider below code for the same:
let obj = [
{
name: 'Max',
score: 94
},
{
name: 'Jerome',
score: 86
},
{
name: 'Susan',
score: 86
},
{
name: 'Abel',
score: 86
},
{
name: 'Kevin',
score: 86
}
];
obj.sort((a,b) => {
if(b.score - a.score === 0){
if(a.name < b.name) return -1;
if(a.name > b.name) return 1;
return 0;
}
else{
return b.score - a.score;
}
});
console.log(obj);

Related

want to add new value in an array but can't getting the code

Given the following JSON-formatted string, write a snippet of code using JSON and array methods, to change the score in Physics to 97, remove the item for Chemistry and add an item for Biology with score 78, and another item for Geography with score 88 in its place.
var scores = `
[
{ subject: 'Math', score: 89 },
{ subject: 'Programming', score: 100 },
{ subject: 'Physics', score: 95 },
{ subject: 'Chemistry', score: 75 },
{ subject: 'History', score: 59 },
]
`;
as above mentioned I have to repalce some values and add value. but I'm not getting right code about how to add the Geography with score
I'm trying the following code to Add the Subject
var score = `[
{ subject: "Math", score:89 },
{ subject: 'Programming', score: 100 },
{ subject: 'Physics', score: 95 },
{ subject: 'Chemistry', score: 75 },
{ subject: 'History', score: 59 }
]`;
score = score.replace(/95/gi,97);
score = score.replace(/75/gi,78);
score = score.replace(/chemistry/gi,"Biology");
const newScore = { subject: 'Geography', score: 88 };
const returnedScore = Object.assign(score, newScore);
console.log(score);
Also tryied this formula to add but doesn't work
var score = [
{ subject: "Math", score:89 },
{ subject: 'Programming', score: 100 },
{ subject: 'Physics', score: 95 },
{ subject: 'Chemistry', score: 75 },
{ subject: 'History', score: 59 }
];
delete score['3'];
console.log(score);
var newScore =`[
{ subject: 'Geography', score: 88 },
]`;
score.push(newScore);
console.log(score);
var scoreSetBack = JSON.stringify(score);
console.log(scoreSetBack);
But it shows .push is not a function in console
and few other method Also tried but nothing is worked
can some help and tell what is right way to do and also check is code to replace is right
One way you can do this is by filtering the array, an example could be:
score = score.filter((item) => item.subject !== 'Chemistry')
Which will return the following filtered array:
score 2:
[
{ subject: 'Math', score: 89 },
{ subject: 'Programming', score: 100 },
{ subject: 'Physics', score: 95 },
{ subject: 'History', score: 59 }
]
As for pushing, you need to push an object similar to what's already in the array:
var newScore ={ subject: 'Geography', score: 88 };
So if we run score.push(newScore) now, Score will have { subject: 'Geography', score: 88 } on the last index

Map through array, create a key-value pair for each element, and give key of an alphabet letter to each pair

I have an array of objects (I don't know how many object, maybe 3, maybe 10)
example:
const data = [
{name: 'Jane', age: 25},
{name:'Luke', age: 55},
{name:'Martha', age: '16'},
...and so on
]
now I want to map through this array and create a key-value pair for each, and as a key I want to use a letter of alphabet (starting with c).
expected result is:
{
c:{name: 'Jane', age: 25},
d:{name:'Luke', age: 55},
e:{name:'Martha', age: '16'}
...and so on
}
How to achieve that ?
I am using JS & React
You can easily achieve the result using reduce and String.fromCharCode.
ASCII CODE of c is 99
const data = [
{ name: "Jane", age: 25 },
{ name: "Luke", age: 55 },
{ name: "Martha", age: "16" },
];
const result = data.reduce((acc, curr, i) => {
acc[String.fromCharCode(99 + i)] = curr;
return acc;
}, {});
console.log(result);
Disregarding the why, and the limitations of using letters...
You can map the array, and create a tuples of [letter, object], and then convert them to an object using Object.fromEntries().
To create the letter, add 99 to the current index value (i) to get the current letters ascii code, and then convert it to a letter using String.charFromCode().
const data = [
{name: 'Jane', age: 25},
{name:'Luke', age: 55},
{name:'Martha', age: '16'},
]
const result = Object.fromEntries(
data.map((o, i) => [String.fromCharCode(i + 99), o])
)
console.log(result)
You can do that... (with a simple reduce)
const data =
[ { name: 'Jane', age: 25 }
, { name:'Luke', age: 55 }
, { name:'Martha', age: '16'}
]
let lCod = 'c'.charCodeAt(0)
const res = data.reduce((r,o)=>(r[String.fromCharCode(lCod++)]=o,r),{})
console.log (res)
.as-console-wrapper { max-height: 100% !important; top: 0 }
if your array size is bigger than 24...
with the help of georg's answer
const data =
[ { name: 'nam_0', age: 0 }, { name: 'nam_1', age: 1 }, { name: 'nam_2', age: 2 }, { name: 'nam_3', age: 3 }, { name: 'nam_4', age: 4 }
, { name: 'nam_5', age: 5 }, { name: 'nam_6', age: 6 }, { name: 'nam_7', age: 7 }, { name: 'nam_8', age: 8 }, { name: 'nam_9', age: 9 }
, { name: 'nam_10', age:10 }, { name: 'nam_11', age:11 }, { name: 'nam_12', age:12 }, { name: 'nam_13', age:13 }, { name: 'nam_14', age:14 }
, { name: 'nam_15', age:15 }, { name: 'nam_16', age:16 }, { name: 'nam_17', age:17 }, { name: 'nam_18', age:18 }, { name: 'nam_19', age:19 }
, { name: 'nam_20', age:20 }, { name: 'nam_21', age:21 }, { name: 'nam_22', age:22 }, { name: 'nam_23', age:23 }, { name: 'nam_24', age:24 }
, { name: 'nam_25', age:25 }, { name: 'nam_26', age:26 }, { name: 'nam_27', age:27 }, { name: 'nam_28', age:28 }, { name: 'nam_29', age:29 }
]
, cName = ((lZero = 'c')=>
{
let[a,z,Ln] = [...'az'+lZero].map(c=>c.charCodeAt(0)), mod = z-a+1;
Ln -= a;
return ()=>
{
let n = Ln++, s = '';
while (n>=0)
{
s = String.fromCharCode(n % mod + a ) + s
n = Math.floor(n / mod) - 1;
}
return s
} })()
, res = data.reduce((r,o)=>(r[cName()]=o,r),{})
console.log (res)
.as-console-wrapper { max-height: 100% !important; top: 0 }

How to sort and get max score for each name from 3 array of objects in JavaScript and assign it and the missing keys and values to another array?

How to sort and get max score for each name from 3 array of objects in javascript and assign it and the missing keys and values to another array?
I want to sort the arrays test1, test2, test3 and get the max score for each student and assign it and the missing keys and values to the array results as an object like:
results = [{name: name, age: age score: score}]
With the following code I am able to get the object with the highest score like:
resutls = [
{ name: 'sam', age: 15, score: 30 }
]
const test1 = [
{ name: 'vikash', score: 1 },
{ name: 'krish', score: 2 },
{ name: 'kunz', score: 3 },
];
const test2 = [
{ name: 'kunz', score: 0 },
{ name: 'vikash', score: 5 },
{ name: 'krish', score: 6 },
{ name: 'sam', age: 15, score: 30 },
];
const test3 = [
{ name: 'krish', score: 7 },
{ name: 'kunz', age: 10, score: 8 },
{ name: 'vikash', score: 10 },
{ name: 'sam', score: '' },
];
const topScore = (...arrays) => {
let result = [...arrays].flat().reduce((max, obj) => {
return max.score < obj.score ? obj : max;
});
return [result];
};
console.log(topScore(test1, test2, test3));
The results array should look like this at the end of it ..
resutls = [
{ name: 'vikash', age: '', score: 10 },
{ name: 'sam', age: 15, score: 30 },
{ name: 'krish', age: '', score: 7 },
{ name: 'kunz', age: 10, score: 8 },
]
You could group by name and get all values from the object with the max score objects.
const
test1 = [{ name: 'vikash', score: 1 }, { name: 'krish', score: 2 }, { name: 'kunz', score: 3 }],
test2 = [{ name: 'kunz', score: 0 }, { name: 'vikash', score: 5 }, { name: 'krish', score: 6 }, { name: 'sam', age: 15, score: 30 }],
test3 = [{ name: 'krish', score: 7 }, { name: 'kunz', age: 10, score: 8 }, { name: 'vikash', score: 10 }, { name: 'sam', score: '' }],
result = Object.values([...test1, ...test2, ...test3].reduce((r, o) => {
if (!r[o.name] || r[o.name].score < o.score) {
r[o.name] = { ...(r[o.name] || { name: '', age: '', score: 0 }), ...o };
}
return r;
}, {}));
console.log(result);
You just select the maximum of all the results, to get an array you can build a dictionary of result by name and then select all the maximums:
const test1 = [
{ name: 'vikash', score: 1 },
{ name: 'krish', score: 2 },
{ name: 'kunz', score: 3 },
];
const test2 = [
{ name: 'kunz', score: 0 },
{ name: 'vikash', score: 5 },
{ name: 'krish', score: 6 },
{ name: 'sam', age: 15, score: 30 },
];
const test3 = [
{ name: 'krish', score: 7 },
{ name: 'kunz', age: 10, score: 8 },
{ name: 'vikash', score: 10 },
{ name: 'sam', score: '' },
];
const topScore = (...arrays) => {
let result = [...arrays].flat().reduce((max, obj) => {
//check if current item or maximum item contains an age.
let age = obj.age || (max[obj.name] || {age: ''}).age
return (max[obj.name] || {score: 0}).score < obj.score ? {...max,[obj.name]: {...obj , age}} : {...max }
}, {});
return Object.values(result)
};
console.log(topScore(test1, test2, test3));

Finding the smartest student in students array objects using reduce function

I have an array of objects (students) and I am trying to find the smartest student (student with highest score) using reduce function. This what I have tried but unable to get the correct result.
const students = [{
name: 'Chris',
score: 75
},
{
name: 'James',
score: 54
},
{
name: 'Alex',
score: 32
},
{
name: 'Mary',
score: 43
},
{
name: 'Robert',
score: 87
}
];
const initValue = {
name: '',
maxScore: 0
};
function smartStudentReducer(acc, student) {
return {
name: student.name,
score: Math.max(acc.maxScore, student.score)
}
}
const smartStudent = students.reduce(smartStudentReducer, initValue);
console.log(smartStudent);
The reducer function should only set the name: property to student.name when student.score is higher than acc.maxStore. So you need to use conditionals.
Also, the accumulator needs to return an object with the same properties as initValue. I've changed initValue to be like the students objects, so I can simply return student when the score is better.
const initValue = {
name: '',
score: 0
};
function smartStudentReducer(acc, student) {
return student.score > acc.score ? student : acc;
}
const students = [{
name: 'Chris',
score: 75
},
{
name: 'James',
score: 54
},
{
name: 'Alex',
score: 32
},
{
name: 'Mary',
score: 43
},
{
name: 'Robert',
score: 87
}
];
const smartStudent = students.reduce(smartStudentReducer, initValue);
console.log(smartStudent);
Actually with every cycle of reduce you are overwriting the result. It doesn't really matter which one of the students have highest score, the result will be always the last student of the data array.
Secondly, you are missing some simple conditions to check if currently looped student has higher score than previous or not.
Suggested approach:
const students = [{
name: 'Chris',
score: 75
},
{
name: 'James',
score: 54
},
{
name: 'Alex',
score: 32
},
{
name: 'Mary',
score: 43
},
{
name: 'Robert',
score: 87
}
];
const initValue = {};
function smartStudentReducer(acc, student) {
if (!acc.name) {
acc = student;
} else {
if (acc.score < student.score) {
acc = student;
}
}
return acc;
}
const smartStudent = students.reduce(smartStudentReducer, initValue);
console.log(smartStudent);

Compare two arrays to get count of properties

I have two arrays that I would like to compare and provide a count of the items in the master list.
The master list might look like this:
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
The Filter list gets all possible names / ages from the database. Some might not have any entries. Each of these lists are getting pulled from an API individually. I will combine them into one array:
{ users:
[{ username: 'Jon' },
{ userName: 'Steve' },
{ username: 'Mark' },
{ username: 'Mike' }],
ages:
[{age: 33},
{age: 34},
{age: 35},
{age: 36}]
}
What I would like to do is be able to count how many of each name I have
Jon - 2, Steve - 1, Mark - 1, Mike - 0
33 - 1, 34 - 2, 35 - 1
Here is a generic approach. You provide the data and the field you want to count.
var data = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
function countUnique(items, property) {
return items.reduce(function(map, item) {
if (item.hasOwnProperty(property)) {
map[item[property]] = (map[item[property]] || 0) + 1;
}
return map;
}, {});
}
console.log(countUnique(data, 'name')); // Object {Jon: 2, Steve: 1, Mark: 1}
console.log(countUnique(data, 'age')); // Object {33: 1, 34: 2, 35: 1}
Filtering
If you want to filter a list of users by conditions, you can define an array of filter objects as seen below. When filtering a list of items, you usually will provide a predicate function to execute on the current item in the filter call. This function returns a boolean value which determines whether or not the item meets the conditions of the function.
var users = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
var filters = [{
name: 'users',
predicate : function(user) {
return [ 'Jon', 'Mark', 'Mike' ].indexOf(user.name) > -1;
}
}, {
name: 'ages',
predicate : function(user) {
return user.age >= 34 && user.age <= 36;
}
}];
print(filterFactory(users, getFiltersByName(filters, ['users', 'ages'])));
function getFiltersByName(filters, names) {
return filters.filter(function(filter) {
return names.indexOf(filter.name) > -1;
});
}
function filterFactory(items, filters) {
return items.filter(function(item) {
return filters.some(function(filter) {
try { return filter.predicate.call(undefined, item); }
catch (e) { throw new Error('predicate undefined for filter: ' + filter.name); }
});
});
}
function print(obj) {
document.body.innerHTML = JSON.stringify(obj, undefined, ' ');
}
body { font-family: monospace; white-space: pre }
Something like this would do. Here is a fiddle http://jsfiddle.net/5jkqv6k3/
var data = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
var key = function(obj) {
// Some unique object-dependent key
return obj.name; // Just an example
};
var dict = {};
for (var i = 0; i < data.length; i++) {
if (dict[key(data[i])])
dict[key(data[i])] = dict[key(data[i])] + 1;
else
dict[key(data[i])] = 1;
}
console.log(dict);
Using angularJs (because you're using it as you said) you can do this:
var countNamesList = {};
var countAgesList = {};
angular.forEach(masterList, function(value, index) {
countNamesList[masterList[index].name] =
(!angular.isUndefined(countNamesList[masterList[index].name])) ?
countNamesList[masterList[index].name] + 1 : 1;
countAgesList[masterList[index].age] =
(!angular.isUndefined(countAgesList[masterList[index].age])) ?
countAgesList[masterList[index].age] + 1 : 1;
});
console.log(countNamesList);
console.log(countAgesList);
JSFIDDLE
Mr. Polywhirl's answer is your best option on counting.
Now here's how you can filter:
var master = [
{ name: 'Jon', age: 34 },
{ name: 'Steve', age: 33 },
{ name: 'Mark', age: 34 },
{ name: 'Jon', age: 35 }
];
var filter = {
users: [
{ username: 'Jon' },
{ username: 'Mark' },
{ username: 'Mike' }
], ages: [
{ age: 34 },
{ age: 35 },
{ age: 36 }
]
};
function filterByNameAndAge(obj) {
return filter.users.some(function(user) {
return user.username === obj.name;
}) && filter.ages.some(function(age) {
return age.age === obj.age;
});
}
console.log(master.filter(filterByNameAndAge));
Currently it accepts only objects with matching name and age. Replace the && inside filterByNameAndAge by || if it should accept objects with matching name or age.

Categories