grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});
This code sorts the array by gsize, smallest to largest.
How would I change it to sort first by gsize and then by glow?
grouperArray.sort(function (a, b) {
return a.gsize - b.gsize || a.glow - b.glow;
});
shorter version
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
if(aSize == bSize)
{
return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
}
else
{
return (aSize < bSize) ? -1 : 1;
}
});
grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);
Even shorter version using arrow syntax!
I realize this was asked some time ago, but I thought I would add my solution.
This function generates sort methods dynamically. simply supply each sortable child property name, prepended with +/- to indicate ascending or descending order. Super re-usable, and it doesn't need to know anything about the data structure you've put together. Could be made idiot proof - but doesn't seem necessary.
function getSortMethod(){
var _args = Array.prototype.slice.call(arguments);
return function(a, b){
for(var x in _args){
var ax = a[_args[x].substring(1)];
var bx = b[_args[x].substring(1)];
var cx;
ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;
if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
if(ax != bx){return ax < bx ? -1 : 1;}
}
}
}
example usage:
items.sort(getSortMethod('-price', '+priority', '+name'));
this would sort items with lowest price first, with ties going to the item with the highest priority. further ties are broken by the item name
where items is an array like:
var items = [
{ name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
{ name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
{ name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
{ name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
{ name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];
live demo: http://gregtaff.com/misc/multi_field_sort/
EDIT: Fixed issue with Chrome.
I expect the ternary operator ((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) has you confused. You should check out the link to understand it better.
Until then, here's your code blown out into full if/else.
grouperArray.sort(function (a, b) {
if (a.gsize < b.gsize)
{
return -1;
}
else if (a.gsize > b.gsize)
{
return 1;
}
else
{
if (a.glow < b.glow)
{
return -1;
}
else if (a.glow > b.glow)
{
return 1;
}
return 0;
}
});
Here's an implementation for those who may want something more generic that would work with any number of fields.
Array.prototype.sortBy = function (propertyName, sortDirection) {
var sortArguments = arguments;
this.sort(function (objA, objB) {
var result = 0;
for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {
var propertyName = sortArguments[argIndex];
result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;
//Reverse if sort order is false (DESC)
result *= !sortArguments[argIndex + 1] ? 1 : -1;
}
return result;
});
}
Basically, you may specify any number of property name / sort direction:
var arr = [{
LastName: "Doe",
FirstName: "John",
Age: 28
}, {
LastName: "Doe",
FirstName: "Jane",
Age: 28
}, {
LastName: "Foo",
FirstName: "John",
Age: 30
}];
arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo
arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); });
Here is an implementation that uses recursion to sort by any number of sort fields from 1 to infinite. You pass it a results array which is an array of result objects to sort, and a sorts array which is an array of sort objects defining the sort. Each sort object must have a "select" key for the key name that it sorts by and an "order" key which is a string indicating "ascending" or "descending".
sortMultiCompare = (a, b, sorts) => {
let select = sorts[0].select
let order = sorts[0].order
if (a[select] < b[select]) {
return order == 'ascending' ? -1 : 1
}
if (a[select] > b[select]) {
return order == 'ascending' ? 1 : -1
}
if(sorts.length > 1) {
let remainingSorts = sorts.slice(1)
return this.sortMultiCompare(a, b, remainingSorts)
}
return 0
}
sortResults = (results, sorts) => {
return results.sort((a, b) => {
return this.sortMultiCompare(a, b, sorts)
})
}
// example inputs
const results = [
{
"LastName": "Doe",
"FirstName": "John",
"MiddleName": "Bill"
},
{
"LastName": "Doe",
"FirstName": "Jane",
"MiddleName": "Bill"
},
{
"LastName": "Johnson",
"FirstName": "Kevin",
"MiddleName": "Bill"
}
]
const sorts = [
{
"select": "LastName",
"order": "ascending"
},
{
"select": "FirstName",
"order": "ascending"
},
{
"select": "MiddleName",
"order": "ascending"
}
]
// call the function like this:
let sortedResults = sortResults(results, sorts)
A dynamic way to do that with MULTIPLE keys:
filter unique values from each col/key of sort
put in order or reverse it
add weights width zeropad for each object based on indexOf(value) keys values
sort using caclutated weights
Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) {
sorts.map(sort => {
sort.uniques = Array.from(
new Set(this.map(obj => obj[sort.key]))
);
sort.uniques = sort.uniques.sort((a, b) => {
if (typeof a == 'string') {
return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
}
else if (typeof a == 'number') {
return sort.inverse ? (a < b) : (a > b ? 1 : 0);
}
else if (typeof a == 'boolean') {
let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
return x;
}
return 0;
});
});
const weightOfObject = (obj) => {
let weight = "";
sorts.map(sort => {
let zeropad = `${sort.uniques.length}`.length;
weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
});
//obj.weight = weight; // if you need to see weights
return weight;
}
this.sort((a, b) => {
return weightOfObject(a).localeCompare( weightOfObject(b) );
});
return this;
}
});
Use:
// works with string, number and boolean
let sortered = your_array.orderBy([
{key: "type", inverse: false},
{key: "title", inverse: false},
{key: "spot", inverse: false},
{key: "internal", inverse: true}
]);
This is what I use
function sort(a, b) {
var _a = "".concat(a.size, a.glow);
var _b = "".concat(b.size, b.glow);
return _a < _b;
}
concat the two items as a string and they will be sorted by a string value. If you want you could wrap _a and _b with parseInt to compare them as numbers if you know they will be numerical.
Here is the solution for the case, when you have a priority sort key, which might not exist in some particular items, so you have to sort by fallback keys.
An input data example (id2 is priority sort key):
const arr = [
{id: 1},
{id: 2, id2: 3},
{id: 4},
{id: 3},
{id: 10, id2: 2},
{id: 7},
{id: 6, id2: 1},
{id: 5},
{id: 9, id2: 2},
{id: 8},
];
And the output should be:
[ { id: 6, id2: 1 },
{ id: 9, id2: 2 },
{ id: 10, id2: 2 },
{ id: 2, id2: 3 },
{ id: 1 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 7 },
{ id: 8 } ]
The comparator function will be like:
arr.sort((a,b) => {
if(a.id2 || b.id2) {
if(a.id2 && b.id2) {
if(a.id2 === b.id2) {
return a.id - b.id;
}
return a.id2 - b.id2;
}
return a.id2 ? -1 : 1;
}
return a.id - b.id
});
P.S. In case if .id of .id2 can be zeros, consider to use typeof.
Let's simplify.
Say you have an array of arrays:
let tmp = [
[0, 1],
[2, 1],
[1, 1],
[0, 0],
[2, 0],
[1, 0],
[0, 2],
[2, 2],
[1, 2],
]
Executing:
tmp.sort((a, b) => {
if (a[1] != b[1])
return a[1] - b[1];
else
return a[0] - b[0];
})
Will yield:
[
[0, 0],
[1, 0],
[2, 0],
[0, 1],
[1, 1],
[2, 1],
[0, 2],
[1, 2],
[2, 2]
]
var items = [
{ name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
{ name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
{ name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
{ name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
{ name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 }];
items.sort(function (a, b) {
var nameA = a.name.toUpperCase();
var nameB = b.name.toUpperCase();
var nameC = a.price.toUpperCase();
var nameD = b.price.toUpperCase();
if (nameA < nameB) {
return -1;
}
if (nameA > nameB || nameC > nameD) {
return 1;
}
// names must be equal
return 0;
});`
grouperArray.sort(
function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
if (aSize !== aSize)
return aSize - bSize;
return a.glow - b.glow;
});
not tested, but I think that should work.
In my case, i sort notification list by param 'important' and by 'date'
step 1: i filter notifications by 'important' and unImportant
let importantNotifications = notifications.filter(
(notification) => notification.isImportant);
let unImportantNotifications = notifications.filter(
(notification) => !notification.isImportant);
step 2: i sort them by date
sortByDate = (notifications) => {
return notifications.sort((notificationOne, notificationTwo) => {
return notificationOne.date - notificationTwo.date;
});
};
step 3: merge them
[
...this.sortByDate(importantNotifications),
...this.sortByDate(unImportantNotifications),
];
If you're happy to use the new tidy.js package you can achieve this with
tidy(input_array,
arrange(['var1', desc('var2')])
);
Besides the other answers here I got inconsistent data on my arrays where 1 wanted a primary ASC sort on field x and a secondary DESC sort on field y.
The solution is in giving the primary sort more importance by multiplying the number with lets say 1000000000
arrayOfObjects.sort((a, b) => {
return (
// Multiply by a high number to the most important sort, that makes them heavier than the second sort
// First sort ASC (notice the - minus in the end instead of the || in other answers !)
(a.paramX * 1000000000) -
(b.paramX * 1000000000) -
// Second sort DESC (switch them if you want ASC too)
(a.paramY - b.paramY)
)
})
for sorting on multiple dates on the object it is this:
// param date1 ASC and param date2 DESC
arrayOfObjects.sort((a, b) => {
return (
(a.date1.getTime() * 1000000000) -
(b.date1.getTime() * 1000000000) -
(a.date2.getTime() - b.date2.getTime())
)
})
Related
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});
This code sorts the array by gsize, smallest to largest.
How would I change it to sort first by gsize and then by glow?
grouperArray.sort(function (a, b) {
return a.gsize - b.gsize || a.glow - b.glow;
});
shorter version
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
if(aSize == bSize)
{
return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
}
else
{
return (aSize < bSize) ? -1 : 1;
}
});
grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);
Even shorter version using arrow syntax!
I realize this was asked some time ago, but I thought I would add my solution.
This function generates sort methods dynamically. simply supply each sortable child property name, prepended with +/- to indicate ascending or descending order. Super re-usable, and it doesn't need to know anything about the data structure you've put together. Could be made idiot proof - but doesn't seem necessary.
function getSortMethod(){
var _args = Array.prototype.slice.call(arguments);
return function(a, b){
for(var x in _args){
var ax = a[_args[x].substring(1)];
var bx = b[_args[x].substring(1)];
var cx;
ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;
if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
if(ax != bx){return ax < bx ? -1 : 1;}
}
}
}
example usage:
items.sort(getSortMethod('-price', '+priority', '+name'));
this would sort items with lowest price first, with ties going to the item with the highest priority. further ties are broken by the item name
where items is an array like:
var items = [
{ name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
{ name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
{ name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
{ name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
{ name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];
live demo: http://gregtaff.com/misc/multi_field_sort/
EDIT: Fixed issue with Chrome.
I expect the ternary operator ((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) has you confused. You should check out the link to understand it better.
Until then, here's your code blown out into full if/else.
grouperArray.sort(function (a, b) {
if (a.gsize < b.gsize)
{
return -1;
}
else if (a.gsize > b.gsize)
{
return 1;
}
else
{
if (a.glow < b.glow)
{
return -1;
}
else if (a.glow > b.glow)
{
return 1;
}
return 0;
}
});
Here's an implementation for those who may want something more generic that would work with any number of fields.
Array.prototype.sortBy = function (propertyName, sortDirection) {
var sortArguments = arguments;
this.sort(function (objA, objB) {
var result = 0;
for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {
var propertyName = sortArguments[argIndex];
result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;
//Reverse if sort order is false (DESC)
result *= !sortArguments[argIndex + 1] ? 1 : -1;
}
return result;
});
}
Basically, you may specify any number of property name / sort direction:
var arr = [{
LastName: "Doe",
FirstName: "John",
Age: 28
}, {
LastName: "Doe",
FirstName: "Jane",
Age: 28
}, {
LastName: "Foo",
FirstName: "John",
Age: 30
}];
arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo
arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
var aLow = a.glow;
var bLow = b.glow;
console.log(aLow + " | " + bLow);
return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); });
Here is an implementation that uses recursion to sort by any number of sort fields from 1 to infinite. You pass it a results array which is an array of result objects to sort, and a sorts array which is an array of sort objects defining the sort. Each sort object must have a "select" key for the key name that it sorts by and an "order" key which is a string indicating "ascending" or "descending".
sortMultiCompare = (a, b, sorts) => {
let select = sorts[0].select
let order = sorts[0].order
if (a[select] < b[select]) {
return order == 'ascending' ? -1 : 1
}
if (a[select] > b[select]) {
return order == 'ascending' ? 1 : -1
}
if(sorts.length > 1) {
let remainingSorts = sorts.slice(1)
return this.sortMultiCompare(a, b, remainingSorts)
}
return 0
}
sortResults = (results, sorts) => {
return results.sort((a, b) => {
return this.sortMultiCompare(a, b, sorts)
})
}
// example inputs
const results = [
{
"LastName": "Doe",
"FirstName": "John",
"MiddleName": "Bill"
},
{
"LastName": "Doe",
"FirstName": "Jane",
"MiddleName": "Bill"
},
{
"LastName": "Johnson",
"FirstName": "Kevin",
"MiddleName": "Bill"
}
]
const sorts = [
{
"select": "LastName",
"order": "ascending"
},
{
"select": "FirstName",
"order": "ascending"
},
{
"select": "MiddleName",
"order": "ascending"
}
]
// call the function like this:
let sortedResults = sortResults(results, sorts)
A dynamic way to do that with MULTIPLE keys:
filter unique values from each col/key of sort
put in order or reverse it
add weights width zeropad for each object based on indexOf(value) keys values
sort using caclutated weights
Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) {
sorts.map(sort => {
sort.uniques = Array.from(
new Set(this.map(obj => obj[sort.key]))
);
sort.uniques = sort.uniques.sort((a, b) => {
if (typeof a == 'string') {
return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
}
else if (typeof a == 'number') {
return sort.inverse ? (a < b) : (a > b ? 1 : 0);
}
else if (typeof a == 'boolean') {
let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
return x;
}
return 0;
});
});
const weightOfObject = (obj) => {
let weight = "";
sorts.map(sort => {
let zeropad = `${sort.uniques.length}`.length;
weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
});
//obj.weight = weight; // if you need to see weights
return weight;
}
this.sort((a, b) => {
return weightOfObject(a).localeCompare( weightOfObject(b) );
});
return this;
}
});
Use:
// works with string, number and boolean
let sortered = your_array.orderBy([
{key: "type", inverse: false},
{key: "title", inverse: false},
{key: "spot", inverse: false},
{key: "internal", inverse: true}
]);
This is what I use
function sort(a, b) {
var _a = "".concat(a.size, a.glow);
var _b = "".concat(b.size, b.glow);
return _a < _b;
}
concat the two items as a string and they will be sorted by a string value. If you want you could wrap _a and _b with parseInt to compare them as numbers if you know they will be numerical.
Here is the solution for the case, when you have a priority sort key, which might not exist in some particular items, so you have to sort by fallback keys.
An input data example (id2 is priority sort key):
const arr = [
{id: 1},
{id: 2, id2: 3},
{id: 4},
{id: 3},
{id: 10, id2: 2},
{id: 7},
{id: 6, id2: 1},
{id: 5},
{id: 9, id2: 2},
{id: 8},
];
And the output should be:
[ { id: 6, id2: 1 },
{ id: 9, id2: 2 },
{ id: 10, id2: 2 },
{ id: 2, id2: 3 },
{ id: 1 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 7 },
{ id: 8 } ]
The comparator function will be like:
arr.sort((a,b) => {
if(a.id2 || b.id2) {
if(a.id2 && b.id2) {
if(a.id2 === b.id2) {
return a.id - b.id;
}
return a.id2 - b.id2;
}
return a.id2 ? -1 : 1;
}
return a.id - b.id
});
P.S. In case if .id of .id2 can be zeros, consider to use typeof.
Let's simplify.
Say you have an array of arrays:
let tmp = [
[0, 1],
[2, 1],
[1, 1],
[0, 0],
[2, 0],
[1, 0],
[0, 2],
[2, 2],
[1, 2],
]
Executing:
tmp.sort((a, b) => {
if (a[1] != b[1])
return a[1] - b[1];
else
return a[0] - b[0];
})
Will yield:
[
[0, 0],
[1, 0],
[2, 0],
[0, 1],
[1, 1],
[2, 1],
[0, 2],
[1, 2],
[2, 2]
]
var items = [
{ name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
{ name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
{ name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
{ name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
{ name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 }];
items.sort(function (a, b) {
var nameA = a.name.toUpperCase();
var nameB = b.name.toUpperCase();
var nameC = a.price.toUpperCase();
var nameD = b.price.toUpperCase();
if (nameA < nameB) {
return -1;
}
if (nameA > nameB || nameC > nameD) {
return 1;
}
// names must be equal
return 0;
});`
grouperArray.sort(
function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);
grouperArray.sort(function (a, b) {
var aSize = a.gsize;
var bSize = b.gsize;
if (aSize !== aSize)
return aSize - bSize;
return a.glow - b.glow;
});
not tested, but I think that should work.
In my case, i sort notification list by param 'important' and by 'date'
step 1: i filter notifications by 'important' and unImportant
let importantNotifications = notifications.filter(
(notification) => notification.isImportant);
let unImportantNotifications = notifications.filter(
(notification) => !notification.isImportant);
step 2: i sort them by date
sortByDate = (notifications) => {
return notifications.sort((notificationOne, notificationTwo) => {
return notificationOne.date - notificationTwo.date;
});
};
step 3: merge them
[
...this.sortByDate(importantNotifications),
...this.sortByDate(unImportantNotifications),
];
If you're happy to use the new tidy.js package you can achieve this with
tidy(input_array,
arrange(['var1', desc('var2')])
);
Besides the other answers here I got inconsistent data on my arrays where 1 wanted a primary ASC sort on field x and a secondary DESC sort on field y.
The solution is in giving the primary sort more importance by multiplying the number with lets say 1000000000
arrayOfObjects.sort((a, b) => {
return (
// Multiply by a high number to the most important sort, that makes them heavier than the second sort
// First sort ASC (notice the - minus in the end instead of the || in other answers !)
(a.paramX * 1000000000) -
(b.paramX * 1000000000) -
// Second sort DESC (switch them if you want ASC too)
(a.paramY - b.paramY)
)
})
for sorting on multiple dates on the object it is this:
// param date1 ASC and param date2 DESC
arrayOfObjects.sort((a, b) => {
return (
(a.date1.getTime() * 1000000000) -
(b.date1.getTime() * 1000000000) -
(a.date2.getTime() - b.date2.getTime())
)
})
I have a persons list and I sort it by the column in sortColumn.
const persons = [{
name: "alireza",
family: "seif",
other: {
age: 28,
rate: 30
}
},
{
name: "sara",
family: "niki",
other: {
age: 15,
rate: 15
}
},
{
name: "fateme",
family: "azizy",
other: {
age: 27,
rate: 35
}
}
];
const sortColumn = {
path: "name",
order: "asc"
};
persons.sort((person1, person2) =>
person1[sortColumn.path] > person2[sortColumn.path] ?
sortColumn.order === "asc" ?
1 :
-1 :
person2[sortColumn.path] > person1[sortColumn.path] ?
sortColumn.order === "asc" ?
-1 :
1 :
0
);
console.log(persons);
If sortColumn.path is "name" and order is "asc" (or "desc"), the sort function works correctly. But how can I sort by "other.age"?
Thanks.
You could take a functin which return a sort function, depending on the sort order.
This sorting function uses another function for getting a value from an object by reducing the splitted path to the value.
const
sortBy = ({ order = 'asc', path }) => order === 'asc'
? (a, b) => ((a, b) => a > b || -(a < b))(getValue(a, path), getValue(b, path))
: (a, b) => ((a, b) => b > a || -(b < a))(getValue(a, path), getValue(b, path)),
getValue = (object, keys) => keys.split('.').reduce((o, k) => o[k], object),
persons = [{ name: "sara", family: "niki", other: { age: 15, rate: 15 } }, { name: "alireza", family: "seif", other: { age: 28, rate: 30 } }, { name: "fateme", family: "azizy", other: { age: 27, rate: 35 } }];
console.log(persons.sort(sortBy({ path: "name", order: "asc" })));
console.log(persons.sort(sortBy({ path: "other.rate", order: "desc" })));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you want to do sort by that deep property age, you can't use sortColumn exactly as you have... Instead, one option is to modify it by making it an array of properties, like so:
sortColumn = { path:["other","age"], order:"asc" }
This way, you'd also have to modify the sort function - as seen in the example below:
const persons = [
{name:"alireza",family:"seif",other:{age:28,rate:30}},
{name:"fateme",family:"azizy",other:{age:27,rate:35}},
{name:"sara",family:"niki",other:{age:15,rate:15}}
]
const sortColumn = { path:["other","age"], order:"asc" }
persons.sort((person1, person2) =>
person1[sortColumn.path[0]][sortColumn.path[1]] > person2[sortColumn.path[0]][sortColumn.path[1]]
? sortColumn.order === "asc"
? 1
: -1
: person2[sortColumn.path[0]][sortColumn.path[1]] > person1[sortColumn.path[0]][sortColumn.path[1]]
? sortColumn.order === "asc"
? -1
: 1
: 0
);
console.log(persons)
However, this approach doesn't work to sort by "name" since this sort function sorts your data by some piece of data that is two-layers deep (inside "other", then "age"). Here's a modification you can make to the sort function which lets you sort by any properties, any number of layers deep into your data:
const persons = [
{name:"alireza",family:"seif",other:{age:28,rate:30}},
{name:"fateme",family:"azizy",other:{age:27,rate:35}},
{name:"sara",family:"niki",other:{age:15,rate:15}}
]
const sortColumn = { path:["name"], order:"asc" }
persons.sort((person1, person2) => {
// get array of paths
const sortPaths = sortColumn.path;
// get values to sort by
const val1 = sortPaths.reduce((acc, path) => {
if (acc[path]) return acc[path]
else alert(`can't find prop ${path} in person1`);
}, person1)
const val2 = sortPaths.reduce((acc, path) => {
if (acc[path]) return acc[path]
else alert(`can't find prop ${path} in person2`);
}, person2)
return val1 > val2
? sortColumn.order === "asc"
? 1
: -1
: val2 > val1
? sortColumn.order === "asc"
? -1
: 1
: 0
});
console.log(persons)
https://stackoverflow.com/questions/59792589/sort-object-in-object-by-javascript/59792918#
As you can see in this second snippet, you can now search by a shallow property "name" by using path: ["name"], but if you want to sort by a deep value, just add both properties to the path array like this: path: ["other", "age"]
Hope this helps!
The main thing to do is to make your code expect 'path' be a function that selects a property's value, instead of a string pointing to a property name. That way it's much more flexible.
const sortColumn = {
path: p => p.other.age,
order: 'desc'
};
However, if 'persons' is not the only object you'd like to do this with, you can further abstract such a function for general use with any array, such as this:
function sorter (array, path, order) {
array.sort((a,b) => {
let result =
path(a) > path(b) ? 1
: path(a) < path(b) ? -1
: 0;
if (order === "desc")
result = -result;
return result;
});
}
Use it like this:
sorter(persons, p => p.other.age, 'desc');
Expand and run the snippet below to see it in action:
function sorter (array, path, order) {
array.sort((a,b) => {
let result =
path(a) > path(b) ? 1
: path(a) < path(b) ? -1
: 0;
if (order === "desc")
result = -result;
return result;
});
}
const persons = [
{name: "alireza", family: "seif", other: {age: 28, rate: 30}},
{name: "sara", family: "niki", other: {age: 15, rate: 15}},
{name: "fateme", family: "azizy", other: {age: 27, rate: 35}}
];
// convenience function
let sortAndLog = (array, path, order) => {
sorter(array, path, order);
console.log(array.map(path));
}
sortAndLog(persons, p => p.name, "asc");
sortAndLog(persons, p => p.name, "desc");
sortAndLog(persons, p => p.other.age, "asc");
sortAndLog(persons, p => p.other.age, "desc");
how to return id whose c value is max. It should return id 1 because its c value is max
var array = [{"id":1,"c":5,"f":8},{"id":2,"c":3,"f":9},{"id":3,"c":4,"f":3}];
You could reduce the objects by checking the object's property in a single loop.
var array = [{ id: 1, c: 5, f: 8 }, { id: 2, c: 3, f: 9 }, { id: 3, c: 4, f: 3 }],
maxC = array.reduce((a, b) => a.c > b.c ? a : b);
console.log(maxC);
console.log(maxC.id);
For getting more objects with the same max value, you could use an array as result set.
var array = [{ id: 1, c: 5, f: 8 }, { id: 2, c: 3, f: 9 }, { id: 3, c: 4, f: 3 }, { id: 4, c: 5, f: 3 }],
maxC = array.reduce((r, o) => {
if (!r || o.c > r[0].c) {
return [o];
}
if (o.c === r[0].c) {
r.push(o);
}
return r;
}, undefined);
console.log(maxC);
Sort base on c value, and select first index.
var array = [{"id":1,"c":5,"f":8},{"id":2,"c":3,"f":9},{"id":3,"c":4,"f":3}],
max = array.sort((a,b) => b.c > a.c)[0];
console.log( max );
You can try following
var array = [{"id":1,"c":5,"f":8},{"id":2,"c":3,"f":9},{"id":3,"c":3,"f":3}];
var obj = {};
array.forEach((item) => {
obj = !obj.id ? item : (obj.c > item.c ? obj : item);
});
console.log(obj); // gives object
console.log(obj.id); // gives id
Find the maximum value first.
var maxC = Math.max.apply(null, array.map( s => s.c ));
Now find the item with maxC
var idWithMaxC = array.find( s => s.c === maxC ).id
Demo
var array = [{
"id": 1,
"c": 5,
"f": 8
}, {
"id": 2,
"c": 3,
"f": 9
}, {
"id": 3,
"c": 4,
"f": 3
}];
var maxC = Math.max.apply(null, array.map(s => s.c));
var idWithMaxC = array.find(s => s.c === maxC).id
console.log(idWithMaxC);
If you don't want lambda functions, this should be quite descriptive
function getMaxId ( array ) {
var max_index = 0;
var max = 0;
for ( var i = 0; i < array.length; i++ ) {
if( array[ i ].c > max ) {
max = array[ i ].c;
max_index = i;
}
}
return array[ max_index ].id
}
Look at lodash
var array = [{"id":1,"c":5,"f":8},{"id":2,"c":3,"f":9},{"id":3,"c":4,"f":3}];
console.log(_.maxBy(array, 'c'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
In such kind of problems JavaScript Array methods (map, filter, reduce) are really helpful.
var array = [{"id":1,"c":5,"f":8},{"id":2,"c":3,"f":9},{"id":3,"c":4,"f":3}];
let max = Math.max.apply(null, array.map(el => el.c));
let ID = array.filter(el => el.c === 5)[0].id;
console.log(ID);
You can use array.sort((a,b) => b.c > a.c)[0].id; (extending the existing answer to meet the OP's requirement)
var array = [{"id":1,"c":5,"f":8},{"id":2,"c":3,"f":9},{"id":3,"c":4,"f":3}],
maxCId = array.sort((a,b) => b.c > a.c)[0].id;
console.log( maxCId );
function demo() {
var test = [{
level: 19,
title: "hello1"
}, {
level: 2,
title: "hello2"
},
{
level: 5,
title: "hello5"
}];
I want to sort this array but can't find a way to do so.
You can create a custom sorting function:
// Sort by level
test.sort(function(a, b) {
return a.level - b.level;
});
Resulting object:
[
{"level":2,"title":"hello2"},
{"level":5,"title":"hello5"},
{"level":19,"title":"hello1"}
]
You can create a sort function like:
function sortBy(prop){
return function(a,b){
if( a[prop] > b[prop]){
return 1;
}else if( a[prop] < b[prop] ){
return -1;
}
return 0;
}
}
//Usage
var test = [{
level: 19,
title: "hello1"
}, {
level: 2,
title: "hello2"
},
{
level: 5,
title: "hello5"
}].sort( sortBy("level") );
Please check below code ,
<script type="text/javascript">
var test = [
{ level: 19, title: "hello1" },
{ level: 2, title: "hello2"},
{ level: 5, title: "hello5" }
];
// Before Sorting
document.write("<b>Before Sorting </b><br/>");
for (var n = 0; n < test.length; n++) {
document.write(test[n].level + ' ' + test[n].title+ '<br>');
}
// ascending order
function SortByLevel(x,y) {
return x.level - y.level;
}
function SortByTitle(x,y) {
return ((x.title == y.title) ? 0 : ((x.title> y.title) ? 1 : -1 ));
}
// Call Sort By Name
test.sort(SortByTitle);
document.write("<br/><b>After Sorting </b> <br/>");
for(var n=0;n<test.length;n++){
document.write(test[n].level + ' ' + test[n].title+ '<br>');
}
</script>
This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 12 months ago.
Let's say you have a JavaScript class like this
var DepartmentFactory = function(data) {
this.id = data.Id;
this.name = data.DepartmentName;
this.active = data.Active;
}
Let's say you then create a number of instances of that class and store them in an array
var objArray = [];
objArray.push(DepartmentFactory({Id: 1, DepartmentName: 'Marketing', Active: true}));
objArray.push(DepartmentFactory({Id: 2, DepartmentName: 'Sales', Active: true}));
objArray.push(DepartmentFactory({Id: 3, DepartmentName: 'Development', Active: true}));
objArray.push(DepartmentFactory({Id: 4, DepartmentName: 'Accounting', Active: true}));
So I now would have an array of objects created by DepartmentFactory. How would I go about using the array.sort() method to sort this array of objects by the DepartmentName property of each object?
The array.sort() method works just fine when sorting an array of strings
var myarray=["Bob", "Bully", "Amy"];
myarray.sort(); //Array now becomes ["Amy", "Bob", "Bully"]
But how do I make it work with a list of objects?
you would have to do something like this:
objArray.sort(function(a, b) {
var textA = a.DepartmentName.toUpperCase();
var textB = b.DepartmentName.toUpperCase();
return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
});
note: changing the case (to upper or lower) ensures a case insensitive sort.
To support unicode:
objArray.sort(function(a, b) {
return a.DepartmentName.localeCompare(b.DepartmentName);
});
Shorter code with ES6
objArray.sort((a, b) => a.DepartmentName.toLowerCase().localeCompare(b.DepartmentName.toLowerCase()))
objArray.sort((a, b) => a.DepartmentName.localeCompare(b.DepartmentName))
var DepartmentFactory = function(data) {
this.id = data.Id;
this.name = data.DepartmentName;
this.active = data.Active;
}
// use `new DepartmentFactory` as given below. `new` is imporatant
var objArray = [];
objArray.push(new DepartmentFactory({Id: 1, DepartmentName: 'Marketing', Active: true}));
objArray.push(new DepartmentFactory({Id: 2, DepartmentName: 'Sales', Active: true}));
objArray.push(new DepartmentFactory({Id: 3, DepartmentName: 'Development', Active: true}));
objArray.push(new DepartmentFactory({Id: 4, DepartmentName: 'Accounting', Active: true}));
function sortOn(property){
return function(a, b){
if(a[property] < b[property]){
return -1;
}else if(a[property] > b[property]){
return 1;
}else{
return 0;
}
}
}
//objArray.sort(sortOn("id")); // because `this.id = data.Id;`
objArray.sort(sortOn("name")); // because `this.name = data.DepartmentName;`
console.log(objArray);
demo: http://jsfiddle.net/diode/hdgeH/
objArray.sort( (a, b) => a.id.localeCompare(b.id, 'en', {'sensitivity': 'base'}));
This sorts them alphabetically AND is case insensitive. It's also super clean and easy to read :D
// Sorts an array of objects "in place". (Meaning that the original array will be modified and nothing gets returned.)
function sortOn (arr, prop) {
arr.sort (
function (a, b) {
if (a[prop] < b[prop]){
return -1;
} else if (a[prop] > b[prop]){
return 1;
} else {
return 0;
}
}
);
}
//Usage example:
var cars = [
{make:"AMC", model:"Pacer", year:1978},
{make:"Koenigsegg", model:"CCGT", year:2011},
{make:"Pagani", model:"Zonda", year:2006},
];
// ------- make -------
sortOn(cars, "make");
console.log(cars);
/* OUTPUT:
AMC : Pacer : 1978
Koenigsegg : CCGT : 2011
Pagani : Zonda : 2006
*/
// ------- model -------
sortOn(cars, "model");
console.log(cars);
/* OUTPUT:
Koenigsegg : CCGT : 2011
AMC : Pacer : 1978
Pagani : Zonda : 2006
*/
// ------- year -------
sortOn(cars, "year");
console.log(cars);
/* OUTPUT:
AMC : Pacer : 1978
Pagani : Zonda : 2006
Koenigsegg : CCGT : 2011
*/
DEMO
var DepartmentFactory = function(data) {
this.id = data.Id;
this.name = data.DepartmentName;
this.active = data.Active;
}
var objArray = [];
objArray.push(new DepartmentFactory({Id: 1, DepartmentName: 'Marketing', Active: true}));
objArray.push(new DepartmentFactory({Id: 2, DepartmentName: 'Sales', Active: true}));
objArray.push(new DepartmentFactory({Id: 3, DepartmentName: 'Development', Active: true}));
objArray.push(new DepartmentFactory({Id: 4, DepartmentName: 'Accounting', Active: true}));
console.log(objArray.sort(function(a, b) { return a.name > b.name}));
Because all of the solutions here were presented without null/undefined safe operations, I handle that this way (you can handle nulls as you see fit):
ES5
objArray.sort(
function(a, b) {
var departmentNameA = a.DepartmentName ? a.DepartmentName : '';
var departmentNameB = b.DepartmentName ? b.DepartmentName : '';
departmentNameA.localeCompare(departmentNameB);
}
);
ES6+
objArray.sort(
(a: DepartmentFactory, b: DepartmentFactory): number => {
const departmentNameA = a.DepartmentName ? a.DepartmentName : '';
const departmentNameB = b.DepartmentName ? b.DepartmentName : '';
departmentNameA.localeCompare(departmentNameB);
}
);
I have also removed the toLowerCase that others used since localeCompare is case insensitive. Also I prefer to be a bit more explicit on parameters when using Typescript or ES6+ to make it more explicit for future developers.
do it like this
objArrayy.sort(function(a, b){
var nameA=a.name.toLowerCase(), nameB=b.name.toLowerCase()
if (nameA < nameB) //sort string ascending
return -1
if (nameA > nameB)
return 1
return 0 //default return value (no sorting)
});
console.log(objArray)
Here is a simple function you can use to sort array of objects through their properties; it doesn't matter if the property is a type of string or integer, it will work.
var cars = [
{make:"AMC", model:"Pacer", year:1978},
{make:"Koenigsegg", model:"CCGT", year:2011},
{make:"Pagani", model:"Zonda", year:2006},
];
function sortObjectsByProp(objectsArr, prop, ascending = true) {
let objectsHaveProp = objectsArr.every(object => object.hasOwnProperty(prop));
if(objectsHaveProp) {
let newObjectsArr = objectsArr.slice();
newObjectsArr.sort((a, b) => {
if(isNaN(Number(a[prop]))) {
let textA = a[prop].toUpperCase(),
textB = b[prop].toUpperCase();
if(ascending) {
return textA < textB ? -1 : textA > textB ? 1 : 0;
} else {
return textB < textA ? -1 : textB > textA ? 1 : 0;
}
} else {
return ascending ? a[prop] - b[prop] : b[prop] - a[prop];
}
});
return newObjectsArr;
}
return objectsArr;
}
let sortedByMake = sortObjectsByProp(cars, "make"); // returns ascending order by its make;
let sortedByYear = sortObjectsByProp(cars, "year", false); // returns descending order by its year,since we put false as a third argument;
console.log(sortedByMake);
console.log(sortedByYear);
After try a little bit on this, and trying to make less loops as possible, I ended up with this solution:
Demo on codepen
const items = [
{
name: 'One'
},
{
name: 'Maria is here'
},
{
name: 'Another'
},
{
name: 'Z with a z'
},
{
name: '1 number'
},
{
name: 'Two not a number'
},
{
name: 'Third'
},
{
name: 'Giant'
}
];
const sorted = items.sort((a, b) => {
return a[name] > b[name];
});
let sortedAlphabetically = {};
for(var item in sorted) {
const firstLetter = sorted[item].name[0];
if(sortedAlphabetically[firstLetter]) {
sortedAlphabetically[firstLetter].push(sorted[item]);
} else {
sortedAlphabetically[firstLetter] = [sorted[item]];
}
}
console.log('sorted', sortedAlphabetically);
You have to pass a function that accepts two parameters, compares them, and returns a number, so assuming you wanted to sort them by ID you would write...
objArray.sort(function(a,b) {
return a.id-b.id;
});
// objArray is now sorted by Id
A simple answer:
objArray.sort(function(obj1, obj2) {
return obj1.DepartmentName > obj2.DepartmentName;
});
ES6 way:
objArray.sort((obj1, obj2) => {return obj1.DepartmentName > obj2.DepartmentName};
If you need to make it lowercase/uppercase etc, just do that and store that result in a variable than compare that variable. Example:
objArray.sort((obj1, obj2) => {
var firstObj = obj1.toLowerCase();
var secondObj = obj2.toLowerCase();
return firstObj.DepartmentName > secondObj.DepartmentName;
});