Ranger une liste de tuples

Résolu
Pirate91gb -  
mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   -
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

  1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     
    bonjour,
    par exemple:
    liste.sort(key= lambda x : x[2])
    2
  2. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 940
     
    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
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      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
      1. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 940 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
         
        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
  3. Pirate91gb
     
    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
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      as-tu essayé:
      tab.sort(key= lambda x : (x[1],x[2]))
      0
  4. Pirate91gb
     
    Bonjour,
    je te remercie pour toute l'aide et les explications (meilleures que mon prof) sur les fonctions de rangement.
    Cordialement
    0
    1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       
      peux-tu alors marquer la discussion comme résolue?
      0
      1. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 940 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
         
        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