Reputation: 7555
I have below array
of objects
in javascript
[
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'},
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'}]
Now I am in need to apply nested sorting.
A user could sort the above collection by name, age, city.
Suppose User asks to sort by name, the sorted array would look alike.
[
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'}
{'name' : 'Mohan', 'age': 50 , 'city' : 'b'}
{'name' : 'Ram', 'age': 10 , 'city' : 'a'}]
Now User clicks on to sort by age (asc), but it should not disturb the above array.
It should look alike
[
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'}
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'}]
But if suppose user asks to sort the above sort desc by age so it should look alike
[
{'name' : 'Aditya', 'age': 10 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 5 , 'city' : 'b'}
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'}
This is how my current function to sort looks alike.
let compareObjects = (key, order = 'asc') => {
return function (a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
// property doesn't exist on either object
return 0;
}
const varA = (typeof a[key] === 'string') ?
a[key].toUpperCase() : a[key];
const varB = (typeof b[key] === 'string') ?
b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return (
(order == 'desc') ? (comparison * -1) : comparison
);
}}
arrOfObjects.sort(compareObjects('age'));
But I have no clue how can I apply nested sorting?
Thanks!
Upvotes: 3
Views: 95
Reputation: 191946
The method compareObjects
can accept an array of { key: order }
objects (sortBy
). Then loop the sortBy
array with for...of
, extract the key and order, and compare untill the result is not 0
, or return 0
if they're all equal.
const compareStr = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase())
const compareNumber = (a, b) => a - b
const compareBy = new Map([
['name', compareStr],
['age', compareNumber],
['city', compareStr],
])
const compareObjects = sortBy => (a, b) => {
for (const srt of sortBy) { // iterate sortBy
const [key, order] = Object.entries(srt)[0]; // extract key|order pairs
const sorter = compareBy.get(key) // get the sorter from the Map
if (!sorter || !(key in a) || !(key in b)) continue // if no sorter or if key doesn't exist in either object continue to the next sorter
const score = sorter(a[key], b[key]) // sort the current values
if (score === 0) continue; // if score is 0 continue to next sorter
return score * (order === 'asc' ? 1 : -1) // return the score multiplied by -1 for non asc values
}
return 0;
}
const data = [{"name":"Ram","age":10,"city":"a"},{"name":"Shyam","age":5,"city":"a"},{"name":"Aditya","age":10,"city":"b"},{"name":"Aditya","age":5,"city":"a"}]
const result = data.sort(compareObjects([
{ age: 'desc' },
{ name: 'asc' }
]))
console.log(result)
Upvotes: 1
Reputation: 386550
You need to hand over all sorting criteria at one and get the sorted array back.
This approach uses an array of arrays with keys and direction. If the direction is not given, i sorts ascending.
function sortBy(columns) {
var cols = columns.map(([key, d]) => ({ key, dir: -(d === 'desc') || 1 }));
return function (a, b) {
var v;
cols.some(({ key, dir }) => {
var aVal = a[key],
bVal = b[key];
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return v = 0;
if (typeof aVal === 'string' && typeof bVal === 'string') {
aVal = aVal.toUpperCase();
bVal = bVal.toUpperCase();
}
return v = dir * (aVal > bVal || -(aVal < bVal));
})
return v;
};
}
var data = [{ name: "Ram", age: 10, city: "a" }, { name: "Shyam", age: 5, city: "a" }, { name: "Aditya", age: 10, city: "b" }, { name: "Aditya", age: 5, city: "a" }]
console.log(data.sort(sortBy([['name'], ['age', 'desc']])));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0
Reputation: 1779
You can also use sortBy
from lodash.
const arr = [
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'},
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'}
]
console.log('sortBy Name:', _.sortBy(arr, ['name']))
console.log('sortBy age:', _.sortBy(arr, ['age']))
console.log('sortBy City:', _.sortBy(arr, ['city']))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Upvotes: 0
Reputation: 36564
Array.prototype.sort()
modifies the original array. It doesnot copy the array.
Return Value
The sorted array. Note that the array is sorted in place, and no copy is made.
You can create a clone using spread operator.Spread operator will make shallow copy of array.
Another way you can make the code better is use 1
and -1
instead of asc
and desc
and multiply it with the result of sort()
callback.
Note: Spread Operator will only make a shallow copy means that objects will still have the reference. If you want to deep copy use JSON.parse(JSON.stringify())
const arr = [
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'},
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'}
]
function sortBy(arr,key,order=1){
arr = [...arr]
return arr.sort((a,b) => (a[key] > b[key] ? 1 : -1) * order)
}
console.log(sortBy(arr,'name'))
console.log(sortBy(arr,'age',-1))
Upvotes: 1