Me preguntaba cuál sería una forma Pythonic de clasificar una lista de tuplas por dos claves, por lo que la clasificación con una (y solo una) estaría en orden inverso y la clasificación con la otra sería insensible a las mayúsculas. Más específicamente, tengo una lista que contiene tuplas como:
myList = [(ele1A, ele2A),(ele1B, ele2B),(ele1C, ele2C)]
Puedo usar el siguiente código para clasificarlo con dos claves:
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
Para ordenar en orden inverso puedo usar
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]), reverse = True)
pero esto se ordenaría en orden inverso con dos teclas.
Cualquier consejo muy apreciado.
Se utilizarán dos claves cuando necesitamos ordenar una lista con dos restricciones, una en orden ascendente y otra en forma descendente en la misma lista o cualquiera
En tu ejemplo
sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))
puede ordenar la lista completa solo en un orden
Puedes probar estos y ver qué está pasando
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]))
Espero que lo entiendas después de esto;)
A veces hay pocas alternativas más que usar una función de comparación. Hubo un argumento de cmp
para sorted
desde su introducción a 2.4, pero se eliminó de Python 3 en favor de la función de key
más eficiente. En 3.2, se agregó functools
a functools
; crea claves a partir de los objetos originales al envolverlos en un objeto cuya función de comparación se basa en la función cmp
. (Puede ver una definición simple de cmp_to_key
al final de Cómo ordenar)
En su caso, dado que la carcasa inferior es relativamente costosa, es posible que desee hacer una combinación:
class case_insensitive_and_2nd_reversed: def __init__(self, obj, *args): self.first = obj[0].lower() self.second = obj[1] def __lt__(self, other): return self.first < other.first or self.first == other.first and other.second < self.second def __lt__(self, other): return self.first < other.first or self.first == other.first and other.second < self.second def __gt__(self, other): return self.first > other.first or self.first == other.first and other.second > self.second def __le__(self, other): return self.first < other.first or self.first == other.first and other.second <= self.second def __ge__(self, other): return self.first > other.first or self.first == other.first and other.second >= self.second def __eq__(self, other): return self.first == other.first and self.second == other.second def __ne__(self, other): return self.first != other.first and self.second != other.second sortedList = sorted(myList, key = case_insensitive_and_2nd_reversed)
Una solución simple, pero que no sea la más eficiente, es clasificar dos veces: la primera vez que se usa el segundo elemento, la segunda se usa el primer elemento:
sortedList = sorted(sorted(myList, key=lambda (a,b):b, reverse=True), key=lambda(a,b):a)
O descomponer:
tempList = sorted(myList, key=lambda (a,b):b, reverse=True) sortedList = sorted(tempList, key=lambda(a,b):a))
Si tus elementos son números, puedes engañar un poco:
sorted(myList, key=lambda(a,b):(a,1.0/b))
Otro enfoque es intercambiar los elementos al comparar los elementos:
def compare_func(x, y): tup1 = (x[0], y[1]) tup2 = (x[1], y[0]) if tup1 == tup2: return 0 elif tup1 > tup2: return 1 else: return -1 sortedList = sorted(myList, cmp=compare_func)
O, usando lambda para evitar la función de escritura:
sortedList = sorted( myList, cmd=lambda (a1, b1), (a2, b2): 0 if (a1, b2) == (a2, b1) else 1 if (a1, b2) > (a2, b1) else -1 )
Recomiendo este enfoque, ya que es desordenado y la palabra clave cmd
no está disponible en Python 3