List sorting with multiple attributes and mixed order
If your attributes are numeric, you have this.
def mixed_order( a ):
return ( a.attribute1, -a.attribute2 )
someList.sort( key=mixed_order )
If your attributes includes strings or other more complex objects, you have some choices.The .sort()
method is stable: you can do multiple passes. This is perhaps the simplest. It's also remarkably fast.
def key1( a ): return a.attribute1
def key2( a ): return a.attribute2
someList.sort( key=key2, reverse=True )
someList.sort( key=key1 )
If this is the only sort, you can define your own special-purpose comparison operators. Minimally, you need __eq__
and __lt__
. The other four can be derived from these two by simple logic. Sorting by multiple columns where each column is in different order
You can reverse the sort of one of the columns if either of the columns is numeric, or can be expressed as numbers as well. You can then simply negate the numeric column to reverse the sort for that column.
If the t_l[-1]
column is numeric, use:
sorted(t_l, key=lambda i: (-i[-1], i[1]))
(using different names for the input list and the lambda
argument reduces confusion)If that column is not numeric but the other one is, you can still use the same trick, but need to reverse the whole sort:
sorted(t_l, key=lambda i: (i[-1], -i[1]), reverse=True)
Single-character columns can be made numeric by using the ord()
function; using ord()
will result in the same sorting order, but since it is numeric you can now reverse it:sorted(t_l, key=lambda i: (-ord(i[-1]), i[1]))
sort a python list based on multiple criteria
If the key
returns a tuple, sorted
will consider them in order when sorting:
In [3]: sorted(my_list, key=lambda k: (k['type'], k['val']), reverse=True)
Out[3]:
[{'type': 2, 'val': 6},
{'type': 1, 'val': 2},
{'type': 0, 'val': 9},
{'type': 0, 'val': 5}]
If you want the indices, just throw an enumerate
in there as well:In [7]: sorted(enumerate(my_list), key=lambda k: (k[1]['type'], k[1]['val']), reverse=True)
Out[7]:
[(1, {'type': 2, 'val': 6}),
(2, {'type': 1, 'val': 2}),
(3, {'type': 0, 'val': 9}),
(0, {'type': 0, 'val': 5})]
In [8]: [k for k, v in sorted(enumerate(my_list), key=lambda k: (k[1]['type'], k[1]['val']), reverse=True)]
Out[8]: [1, 2, 3, 0]
How to sort a list with two keys but one in reverse order?
Two keys will be used when we need to sort a list with two constraints: one in ascending order and the other in descending, in the same list or any
In your example,
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
you can sort entire list only in one order.You can try these and check what's happening:
sortedList = sorted(myList, key = lambda y: (y[0].lower(), -y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), -y[1]))
Sort by multiple keys using different orderings
There is no built-in way to handle this. For the general case, you must sort twice: first by the secondary sort, then by the primary sort. As @Mark Ransom mentioned in his comment, in many cases the variables are numeric, and so you can use the negative value to flip the ordering.
If you know the type of the variable you're trying to sort on and how to work with it, you could also write a key function that returns a decreasing value for increasing keys. See this thread for an example for strings. (Basically, you take the negative of the ASCII numerical value of the characters.)
In Python 2, you could also use a cmp
function instead of a key, but this will likely make the sort slower. Whether it will make it too slow depends on how big and unsorted the list is. In Python 3, the cmp
argument is gone, but as @Mark Ransom notes you could use cmp_to_key
.
Collections.sort with multiple fields
Yes. Why are you adding the three fields together before you compare them?Do you see anything wrong with the code?
I would probably do something like this: (assuming the fields are in the order you wish to sort them in)
@Override public int compare(final Report record1, final Report record2) {
int c;
c = record1.getReportKey().compareTo(record2.getReportKey());
if (c == 0)
c = record1.getStudentNumber().compareTo(record2.getStudentNumber());
if (c == 0)
c = record1.getSchool().compareTo(record2.getSchool());
return c;
}
Sort mixed list using subtype specific behaviour Kotlin
Here is something that might serve your needs.
We supply separate comparator for all subtypes, and simply group all by type and sort each group with it's own comparator, after that simply sort groups by their class name and flatten elements from each group in consecutive manner.
Here is code example demonstrating it.
- added utility function
sortedUsing
to perform this sorting - added an utility type
TypeComparator
just to have more convenient way to pass type safe Class + Comparator
fun <T : Any> List<T>.sortedUsing(
vararg typeComparators: TypeComparator<out T>
): List<T> {
@Suppress("UNCHECKED_CAST")
fun <R : T> TypeComparator<R>.sort(list: List<T>): List<R> = (list as List<R>).sortedWith(comparator)
val comparators = typeComparators.associateBy { it.type }
return groupBy { it::class }
.mapValues { (klass, list) ->
val typeComparator = comparators[klass]
?: typeComparators.firstOrNull { klass.isSubclassOf(it.type) }
?: list.firstOrNull()?.tryMakeComparator()
?: throw IllegalArgumentException("Missing comparator for type: $klass")
typeComparator.sort(list)
}
.map { it }
.sortedBy { it.key.qualifiedName }
.flatMap { it.value }
}
@Suppress("UNCHECKED_CAST")
private fun <T : Any> T.tryMakeComparator(): TypeComparator<T>? {
if (this !is Comparable<*>) {
return null
}
return TypeComparator(this::class as KClass<T>, Comparator { o1, o2 ->
val c1 = o1 as Comparable<Comparable<T>>
val c2 = o2 as Comparable<T>
c1.compareTo(c2)
})
}
data class TypeComparator<T : Any>(
val type: KClass<T>,
val comparator: Comparator<T>
)
You could also supply comparator for type if you'd like to, because default in snippet above is to order typed groups by class full name.With some better way of accumulating elements of some type, you could get rid of unchecked list cast.
Usage example:
open class Base
data class SubtypeA(
val foo: Int
) : Base()
data class SubtypeB(
val bar1: String,
val bar2: String
) : Base()
fun main() {
val list = listOf(
SubtypeA(5),
SubtypeB("foo", "x"),
SubtypeA(42),
SubtypeA(2),
SubtypeB("bar", "y"),
SubtypeB("bar", "x")
)
val sorted = list.sortedUsing(
TypeComparator(SubtypeA::class, Comparator.comparing { a: SubtypeA -> a.foo }),
TypeComparator(SubtypeB::class, Comparator.comparing { b: SubtypeB -> b.bar1 }.thenComparing { b: SubtypeB -> b.bar2 })
)
sorted.forEach { println(it) }
// prints:
// SubtypeA(foo=2)
// SubtypeA(foo=5)
// SubtypeA(foo=42)
// SubtypeB(bar1=bar, bar2=x)
// SubtypeB(bar1=bar, bar2=y)
// SubtypeB(bar1=foo, bar2=x)
}
Related Topics
How to Plot Only a Table in Matplotlib
Pandas: Subindexing Dataframes: Copies VS Views
Split Dataframe into Relatively Even Chunks According to Length
Splitting List Based on Missing Numbers in a Sequence
Preventing Python Code from Importing Certain Modules
How to Access Class Member Variables in Python
Numpy Array Dtype Is Coming as Int32 by Default in a Windows 10 64 Bit MAChine
Why Can't You Add Attributes to Object in Python
Loading JSONl File as JSON Objects
Should I Be Adding the Django Migration Files in the .Gitignore File
In Tensorflow, Get the Names of All the Tensors in a Graph
How to Check If Stdin Has Some Data
Does 'Anaconda' Create a Separate Pythonpath Variable for Each New Environment
What Is More 'Pythonic' for 'Not'
How to Convert an Integer to the Shortest Url-Safe String in Python