How to Sort Objects by Multiple Keys

How to sort an array of objects by multiple fields?

You could use a chained sorting approach by taking the delta of values until it reaches a value not equal to zero.

var data = [{ h_id: "3", city: "Dallas", state: "TX", zip: "75201", price: "162500" }, { h_id: "4", city: "Bevery Hills", state: "CA", zip: "90210", price: "319250" }, { h_id: "6", city: "Dallas", state: "TX", zip: "75000", price: "556699" }, { h_id: "5", city: "New York", state: "NY", zip: "00010", price: "962500" }];
data.sort(function (a, b) { return a.city.localeCompare(b.city) || b.price - a.price;});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to sort array of objects by multiple keys?

Sort as follows:

arr.sort(function(a, b) {
return a.pricePerUnitPerHour - b.pricePerUnitPerHour ||
-(a.divisible - b.divisible) ||
a.creationDateTime - b.creationDateTime;
});

The subtraction operation will return 0 if both values are equal (doh) in which case the code moves to the next comparison. The true/false case needs to be inverted since you want 1 (true) to sort before 0 (false).

Sort array of objects by multiple properties of string type

The test should be (assuming your date format is dd/mm/yyyy)

if (a.name > b.name) return 1;
if (a.name < b.name) return -1;
let [a_dd, a_mm, a_yy] = a.joindate.split("/");
let [b_dd, b_mm, b_yy] = b.joindate.split("/");
if (a_yy !== b_yy) return a_yy - b_yy;
if (a_mm !== b_mm) return a_mm - b_mm;
if (a_dd !== b_dd) return a_dd - b_dd;
return 0;

How to sort objects by multiple keys?

This answer works for any kind of column in the dictionary -- the negated column need not be a number.

def multikeysort(items, columns):
from operator import itemgetter
comparers = [((itemgetter(col[1:].strip()), -1) if col.startswith('-') else
(itemgetter(col.strip()), 1)) for col in columns]
def comparer(left, right):
for fn, mult in comparers:
result = cmp(fn(left), fn(right))
if result:
return mult * result
else:
return 0
return sorted(items, cmp=comparer)

You can call it like this:

b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0},
{u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0},
{u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0},
{u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0},
{u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0},
{u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0},
{u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0},
{u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0},
{u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0},
{u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0},
{u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0},
{u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0},
{u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0},
{u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0},
{u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0},
{u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}]

a = multikeysort(b, ['-Total_Points', 'TOT_PTS_Misc'])
for item in a:
print item

Try it with either column negated. You will see the sort order reverse.

Next: change it so it does not use extra class....


2016-01-17

Taking my inspiration from this answer What is the best way to get the first item from an iterable matching a condition?, I shortened the code:

from operator import itemgetter as i

def multikeysort(items, columns):
comparers = [
((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
for col in columns
]
def comparer(left, right):
comparer_iter = (
cmp(fn(left), fn(right)) * mult
for fn, mult in comparers
)
return next((result for result in comparer_iter if result), 0)
return sorted(items, cmp=comparer)

In case you like your code terse.


Later 2016-01-17

This works with python3 (which eliminated the cmp argument to sort):

from operator import itemgetter as i
from functools import cmp_to_key

def cmp(x, y):
"""
Replacement for built-in function cmp that was removed in Python 3

Compare the two objects x and y and return an integer according to
the outcome. The return value is negative if x < y, zero if x == y
and strictly positive if x > y.

https://portingguide.readthedocs.io/en/latest/comparisons.html#the-cmp-function
"""

return (x > y) - (x < y)

def multikeysort(items, columns):
comparers = [
((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
for col in columns
]
def comparer(left, right):
comparer_iter = (
cmp(fn(left), fn(right)) * mult
for fn, mult in comparers
)
return next((result for result in comparer_iter if result), 0)
return sorted(items, key=cmp_to_key(comparer))

Inspired by this answer How should I do custom sort in Python 3?

Sort an array of objects(with multiple properties) in a specified order of Keys with JavaScript

Rather than resorting the original data, you can map it to a new sorted array by indexing it with every element in your sortOrder in order. Something like this:

let result = sortOrder.map(key => sheetCells[0][key]);

While that's a working version of your solution, based on the data structure in your example, this may be closer to what you're looking for:

let result = sheetCells[0].map(cell => sortOrder.map(key => cell[key]));

Sort Array Object with Multiple Keys: Javascript

No need to create Date objects, just reorder the date string into a sortable string, example

This example assumes that your dates are in the same format DD-MM-YYYY and creates YYYYMMDD for the date sort.

Javascript

var arr = [
{ id:1001, date:"20-02-2014", Name: 'demo1' },
{ id:1004, date:"13-02-2014", Name: 'demo0' },
{ id:1000, date:"10-02-2014", Name: 'demo14' },
{ id:1004, date:"16-02-2014", Name: 'demo10' },
{ id:1006, date:"22-02-2014", Name: 'demo111' },
{ id:1003, date:"28-02-2014", Name: 'demo16' },
{ id:1000, date:"28-01-2014", Name: 'demo12' },
{ id:1004, date:"28-01-2014", Name: 'demo01' },
{ id:1000, date:"08-01-2014", Name: 'demo41' },
{ id:1006, date:"08-01-2014", Name: 'demo91' }
];

var sorted = arr.sort(function (a, b) {
return a.id - b.id || a.date.split('-').reverse().join('') - b.date.split('-').reverse().join('');
});

sorted.forEach(function (element) {
console.log(JSON.stringify(element));
});

Output


{"id":1000,"date":"08-01-2014","Name":"demo41"}
{"id":1000,"date":"28-01-2014","Name":"demo12"}
{"id":1000,"date":"10-02-2014","Name":"demo14"}
{"id":1001,"date":"20-02-2014","Name":"demo1"}
{"id":1003,"date":"28-02-2014","Name":"demo16"}
{"id":1004,"date":"28-01-2014","Name":"demo01"}
{"id":1004,"date":"13-02-2014","Name":"demo0"}
{"id":1004,"date":"16-02-2014","Name":"demo10"}
{"id":1006,"date":"08-01-2014","Name":"demo91"}
{"id":1006,"date":"22-02-2014","Name":"demo111"}

On jsFiddle

If there is any concern over mixing date formats, as discussed with @xdazz, then you can improve on this by checking the padding yourself. The following creates the format 'YYYYYYMMDD' when sorting by the date. The extra year padding is not necessary in this example as I am taking the numeric difference of the values, but if you choose to compare the strings then it is important.

function pad(s, n) {
var v = '',
i;

for(i = 0; i < n - s.length; i += 1) {
v += '0';
}

return v + s;
}

var sorted = arr.sort(function (a, b) {
var idDiff = a.id - b.id;

if (idDiff) {
return idDiff;
}

var ordA = a.date.split('-').reverse(),
ordB = b.date.split('-').reverse();

ordA[0] = pad(ordA[0], 6);
ordA[1] = pad(ordA[1], 2);
ordA[2] = pad(ordA[2], 2);
ordA = ordA.join('');
ordB[0] = pad(ordB[0], 6);
ordB[1] = pad(ordB[1], 2);
ordB[2] = pad(ordB[2], 2);
ordB = ordB.join('');
return ordA - ordB;
});

On jsFiddle

If you really want to use Date objects the I would suggest the following.

var sorted = arr.sort(function (a, b) {
var idDiff = a.id - b.id;

if (idDiff) {
return idDiff;
}

var ordA = a.date.split('-').reverse(),
ordB = b.date.split('-').reverse();

ordA[1] -= 1;
ordB[1] -= 1;

return new Date(Date.UTC.apply(undefined, ordA)).valueOf() - new Date(Date.UTC.apply(undefined, ordB)).valueOf();
});

sorted.forEach(function (element) {
console.log(JSON.stringify(element));
});

On jsFiddle

Note: These examples do not handle dates with negative years, again you would need to make further modifications.

Sorting of array of object with multiple key values

You could take a default value of Number.MAX_VALUE for calculating the delta of age.

const users = [{ user: 'fred' }, { user: 'barney', age: 36 }, { user: 'fred', age: 40 }, { user: 'barney', age: 34 }, { user: 'arney', age: 36 }, { user: 'ared' }];

users.sort((a, b) =>
(a.age ?? Number.MAX_VALUE) - (b.age ?? Number.MAX_VALUE) ||
a.user.localeCompare(b.user)
);

console.log(users);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sort a list by multiple attributes?

A key can be a function that returns a tuple:

s = sorted(s, key = lambda x: (x[1], x[2]))

Or you can achieve the same using itemgetter (which is faster and avoids a Python function call):

import operator
s = sorted(s, key = operator.itemgetter(1, 2))

And notice that here you can use sort instead of using sorted and then reassigning:

s.sort(key = operator.itemgetter(1, 2))

Sort javascript array by multiple keys

var sortedItems = sortByKey(itemArray, "name");

function sortByKey(array, key) {
return array.sort(function(a, b) {
if(a[key] == b[key]) {
return (a['flag'] ? -1 : 1);
} else {
var x = a[key];
var y = b[key];
return x.localeCompare(y);
}
});

Can I sort a list of objects by 2 keys?

If you want to sort by epoch and then size, this should work:

sorted(DISKIMAGES, key=lambda x: (x.epoch, x.size), reverse=True)

or as pointed out by @chepner, you can use the operator.attrgetter method

import operator
sorted(DISKIMAGES, key=operator.attrgetter('epoch', 'size'), reverse=True)


Related Topics



Leave a reply



Submit