Ranger une liste de tuples [Résolu]

Signaler
-
Messages postés
29800
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
6 mai 2021
-
Bonjour,
Je voudrais ranger dans l'ordre croissant une liste de tuple sous cette forme
liste = [("Bleu",3,4),("Rouge",5,2),("Orange",8,9)]

par rapport à
liste[i][1]
ou
liste[i][2]
.
Merci d'avance,

4 réponses

Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
bonjour,
par exemple:
liste.sort(key= lambda x : x[2])
Messages postés
29800
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
6 mai 2021
7 088
Bonjour,

Explications préliminaires

Par défaut, les éléments itérables et indexés (e.g.
list
et
tuple
) exposent l'opérateur
<
de sorte à implémenter un ordre lexicographique :
  • on prend le premier élément des deux tuples comparés
  • si l'un est plus petit il permet de d'élire le plus petit élément
  • sinon on passe au second élément et ainsi de suite, jusqu'à élire un plus petit élément.


Exemple : dans cet exemple,
sort
compare d'abord la partie entière des tuples, et si ça ne suffit pas on regarde la partie caractère :

l = [(1,'b'), (2, 'a'), (1, 'a'), (0, 'z')]
l.sort()
print(l)
#[(0, 'z'), (1, 'a'), (1, 'b'), (2, 'a')]


Contrairement à d'autre langages, la fonction
sorted
de python (ou la méthode
list.sort
), il n'est pas possible de passer en paramètre la relation d'ordre
<
de son choix, ce qui signifie qu'on pourrait croire qu'on est bloqué si le comportement par défaut de
<
n'est pas celui escompté.

Le choix qui a été fait dans python c'est qu'on utilise toujours
<
quitte à convertir l'élément de la liste à trier par une représentation sur laquelle
<
fera ce qu'il faut.

Conséquences
  • Si les éléments de ta liste sont des tuples/listes tel qu'ils doivent être ordonnés autrement que selon l'ordre lexicographique, il faut passer en paramètre la callback key adéquate.
  • Si tu tries une liste de tuples et que ces tuples ne sont pas triés selon l'ordre lexicographique (e.g. comparer le 2e attribut puis le 1er), la callback
    key
    doit les mettre dans le bon ordre.


Exemple : Ici chaque tuple (entier, caractère) et converti sous la forme (caractère, entier) et donc on trie en priorité sur la partie caractère et si ça ne suffit pas sur la partie entière.

l = [(1,'b'), (2, 'a'), (1, 'a'), (0, 'z')]
l.sort(key=lambda tup: (tup[1], tup[0]))
print(l)
#[(1, 'a'), (2, 'a'), (1, 'b'), (0, 'z')]
  • Si l'un des attributs confronté ne doit pas être trié selon <, mais selon une autre relation d'ordre (e.g. >), charge à la callback key d'adapter la représentation en conséquence. Par exemple en prenant la valeur opposée.


Exemple : Ici on trie selon (-entier, caractère) donc on trie selon la partie entière dans l'ordre décroissant, et si ça ne suffit pas, selon la partie caractère

l = [(1,'b'), (2, 'a'), (1, 'a'), (0, 'z')]
l.sort(key=lambda tup: (-tup[0], tup[1])
print(l)
#[(2, 'a'), (1, 'a'), (1, 'b'), (0, 'z')]
  • Si une partie des éléments à trier n'est pas pertinente, il suffit de l'omettre dans la valeur retournée par la callback
    key
    (c'est la solution proposée par yg_be : il ne fait la comparaison que sur le troisième attribut du tuple).


Retour à ton cas

Dans ton cas tu sembles trier les tuples en ignorant le premier élément (la partie chaîne), puis selon l'ordre lexicographique en comparant d'abord le second élément et si besoin le troisième élément. Ce qui donne :

tab = [
    ["Bleu", 2, 3],
    ["Orange", 1, 5],
    ["Vert", 2, 4],
    ["Rose", 1, 3]
]
tab.sort(key=lambda tup: (tup[1], tup[2]))
print(tab)
#[['Rose', 1, 3], ['Orange', 1, 5], ['Bleu', 2, 3], ['Vert', 2, 4]]


Bonne chance
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
En Python, la fonction sorted de python (ou la méthode list.sort) permet de passer en paramètre la relation d'ordre < de son choix, exemple:
import functools
def jecompare (a,b):
    if a[1]<b[1]:
        return(-1)
    elif a[1]>b[1]:
        return(1)
    elif a[2]<b[2]:
        return(-1)
    elif a[2]>b[2]:
        return(1)
    else:
        return(0)
tab = [["Bleu",2,3],["Orange",1,5],["Vert",2,4],["Rose",1,3]]
print(tab)
tab.sort(key=functools.cmp_to_key(jecompare))
print(tab)

plus compact:
def jecompare (a,b):
    if a[1]!=b[1]:
        return(a[1]-b[1])
    else:
        return(a[2]-b[2])
Messages postés
29800
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
6 mai 2021
7 088 >
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021

Intéressant je ne connaissais pas
cmp_to_key
, merci :-)

Ceci dit ce n'est pas exactement ce que j'entendais dans mon message précédent. Si tu prends un préordre (donc une relation qui prend en paramètre deux éléments et retourne un booléen), tu ne pourras pas directement utiliser
functools.cmp_to_key
.

En effet,
cmp_to_key
attend que la fonction enveloppée retourne un entier et prend une décision en fonction de son signe, comme le montre son implémentation :

def cmp_to_key(mycmp):
    """Convert a cmp= function into a key= function"""
    class K(object):
        __slots__ = ['obj']
        def __init__(self, obj):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        __hash__ = None
    return K


Cela signifie que si tu définis un préordre sur un objet
Toto
(sous-entendu qui n'est pas
Toto.__lt__
)
cmp_to_key
ne répond pas exactement à la question. Il va falloir au préalable avoir recours à adapter le pré-ordre pour que sa valeur de retour soit une valeur signée (comme le montre la fonction
preorder_to_cmp
dans l'exemple ci-dessous) :

#!/usr/bin/env python3

from functools import cmp_to_key, partial

def my_preorder(a, b):
    return a >= b

def preorder_to_cmp(a, b, preorder=None):
    return (             
        -1 if preorder(a, b) else
        1  if preorder(b, a) else
        0
    )

l = [1, 3, 4, 2]
l.sort(key=cmp_to_key(partial(preorder_to_cmp, preorder=my_preorder)))
print(l) # [4, 3, 2, 1]
Bonjour,
Merci, de plus j'aimerais (je ne l'ai pas précisé avant) trié un tableau comme celui ci
tab = [["Bleu",2,3],["Orange",1,5],["Vert",2,4],["Rose",1,3]]
en
tab= [["Rose",1,3],["Orange",1,5],["Bleu",2,3],["Vert",2,4]]
. La différence est que tab[i][2] est comparé avec tab[i+1][2] quand tab[i][1] et tab[i+1][1] sont égaux.
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
as-tu essayé:
tab.sort(key= lambda x : (x[1],x[2]))
Bonjour,
je te remercie pour toute l'aide et les explications (meilleures que mon prof) sur les fonctions de rangement.
Cordialement
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021
850
peux-tu alors marquer la discussion comme résolue?
Messages postés
29800
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
6 mai 2021
7 088 >
Messages postés
15564
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
11 mai 2021

Comme son compte n'est pas enregistré, je ne pense pas qu'il puisse le faire, je m'en occupe. Bonne journée à vous deux.