Ranger une liste de tuples

Résolu/Fermé
Pirate91gb - Modifié le 26 avril 2021 à 10:27
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 27 avril 2021 à 14:05
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

yg_be Messages postés 22717 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 22 avril 2024 1 476
26 avril 2021 à 10:44
bonjour,
par exemple:
liste.sort(key= lambda x : x[2])
2
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
26 avril 2021 à 14:09
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
2
yg_be Messages postés 22717 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 22 avril 2024 1 476
Modifié le 26 avril 2021 à 14:45
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])
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749 > yg_be Messages postés 22717 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 22 avril 2024
Modifié le 29 avril 2021 à 16:18
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]
0
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.
0
yg_be Messages postés 22717 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 22 avril 2024 1 476
26 avril 2021 à 12:45
as-tu essayé:
tab.sort(key= lambda x : (x[1],x[2]))
0
Bonjour,
je te remercie pour toute l'aide et les explications (meilleures que mon prof) sur les fonctions de rangement.
Cordialement
0
yg_be Messages postés 22717 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 22 avril 2024 1 476
26 avril 2021 à 15:15
peux-tu alors marquer la discussion comme résolue?
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749 > yg_be Messages postés 22717 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 22 avril 2024
27 avril 2021 à 14:05
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.
0