222 votes

Est-il possible de trier deux listes (qui se référencent) de la même manière?

Bon d'accord, c'est peut-être pas la meilleure idée, mais j'étais un peu curieux de savoir si c'est possible. Dire que j'ai deux listes:

list1 = [3,2,4,1, 1]
list2 = [three, two, four, one, one2]

Si j'exécute list1.sort(), il va les trier à l' [1,1,2,3,4] mais est-il un moyen de conserver la synchronisation de la liste 2(ainsi que je peux dire, le point 4 appartient à "trois")? Mon problème est que j'ai un joli programme complexe qui fonctionne bien avec des listes, mais je sorte de besoin pour commencer le référencement des données. Je sais que c'est une situation parfaite pour les dictionnaires, mais j'essaie d'éviter les dictionnaires dans mon traitement parce que je n'ai besoin de trier les valeurs de clé(si je dois utiliser des dictionnaires, je sais comment les utiliser).

Fondamentalement, la nature de ce programme, les données arrivent dans un ordre aléatoire(comme ci-dessus), j'ai besoin de les trier, les traiter et envoyer les résultats(de l'ordre n'a pas d'importance, mais les utilisateurs doivent connaître le résultat de qui appartient à la clé). J'ai pensé à le mettre dans un dictionnaire en premier, puis le tri de la liste, mais je ne dispose d'aucun moyen de différencier les éléments dans l', avec la même valeur si l'ordre n'est pas maintenu(il peut avoir un impact lors de la communication des résultats auprès des utilisateurs). Donc, idéalement, une fois que je reçois les listes je préfère trouver un moyen de trier les deux listes ensemble. Est-ce possible?

353voto

senderle Points 41607

Une approche classique à ce problème est d'utiliser le "décorer, de tri, undecorate" l'idiome, ce qui est particulièrement simple à l'aide de python intégré dans l' zip fonction de:

>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2 
('one', 'one2', 'two', 'three', 'four')

Ces cours ne sont plus des listes, mais c'est facile de remédier, si c'est important:

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']

Il est intéressant de noter que les sacrifices de la vitesse pour laconisme; la version, ce qui prend 3 lignes, est un peu plus vite sur ma machine:

>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop

47voto

J.F. Sebastian Points 102961

Vous pouvez trier les index en utilisant des valeurs comme clés:

 indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)
 

Pour obtenir des listes triées en fonction d'index triés:

 sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)
 

Dans votre cas, vous ne devriez pas avoir list1 , list2 mais plutôt une seule liste de paires:

 data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]
 

Il est facile à créer; il est facile de trier en Python:

 data.sort() # sort using a pair as a key
 

Trier par la première valeur uniquement:

 data.sort(key=lambda pair: pair[0])
 

31voto

J'ai utilisé la réponse donnée par senderle pendant une longue période jusqu'à ce que je découvre np.argsort . Voici comment cela fonctionne.

 list1 = [3,2,4,1]
list2 = ["three","two","four","one"]
idx   = np.argsort(list1)

list1 = list1[idx]
list2 = list2[idx]
 

Je trouve cette solution plus intuitive et elle fonctionne très bien. La performance:

 def sorting(l1, l2):
    # l1 and l2 has to be numpy arrays
    idx = np.argsort(l1)
    return l1[idx], l2[idx]

# list1 and list2 are np.arrays here...
%timeit sorting(list1, list2)
100000 loops, best of 3: 3.53 us per loop

# This works best when the lists are NOT np.array
%timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 2.41 us per loop

# 0.01us better for np.array (I think this is negligible)
%timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best for 3 loops: 1.96 us per loop
 

Même si np.argsort n'est pas le plus rapide, je le trouve plus facile à utiliser.

16voto

Karl Knechtel Points 24349

Transformation schwartzienne . Le tri Python intégré est stable, donc les deux 1 s ne posent pas de problème.

 >>> l1 = [3, 2, 4, 1, 1]
>>> l2 = ['three', 'two', 'four', 'one', 'second one']
>>> zip(*sorted(zip(l1, l2)))
[(1, 1, 2, 3, 4), ('one', 'second one', 'two', 'three', 'four')]
 

4voto

Artsiom Rudzenka Points 9771

Qu'en est-il de:

 list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

sortedRes = sorted(zip(list1, list2), key=lambda x: x[0]) # use 0 or 1 depending on what you want to sort
>>> [(1, 'one'), (1, 'one2'), (2, 'two'), (3, 'three'), (4, 'four')]
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X