web-dev-qa-db-fra.com

La conversion d'une liste en un ensemble change l'ordre des éléments

Récemment, j'ai remarqué que lorsque je convertis un list en set l'ordre des éléments est modifié et trié par caractère.

Considérons cet exemple:

x=[1,2,20,6,210]
print x 
# [1, 2, 20, 6, 210] # the order is same as initial order

set(x)
# set([1, 2, 20, 210, 6]) # in the set(x) output order is sorted

Mes questions sont -

  1. Pourquoi cela arrive-t-il?
  2. Comment puis-je définir des opérations (en particulier Set Difference) sans perdre l'ordre initial?
83
d.putto
  1. A set est une structure de données non ordonnée.

  2. N'utilisez pas un set , mais plutôt collections.OrderedDict :

    >>> a = collections.OrderedDict.fromkeys([1, 2, 20, 6, 210])
    >>> b = collections.OrderedDict.fromkeys([6, 20, 1])
    >>> collections.OrderedDict.fromkeys(x for x in a if x not in b)
    OrderedDict([(2, None), (210, None)])
    

    Notez que l'ordre de b n'a pas d'importance, donc ce peut être n'importe quelle valeur, mais ce devrait être une variable qui supporte les tests d'adhésion O(1)).

Edit : La réponse ci-dessus suppose que vous voulez pouvoir effectuer des opérations de réglage (ordonné) sur toutes les collections en cours, en particulier sur le résultat d'un ancien ensemble d'opération. Si cela n'est pas nécessaire, vous pouvez simplement utiliser des listes pour certaines des collections et des ensembles pour d'autres, par exemple.

>>> a = [1, 2, 20, 6, 210]
>>> b = set([6, 20, 1])
>>> [x for x in a if x not in b]
[2, 210]

Ceci perd la commande de b, n'autorise pas les tests d'appartenance rapides sur a et le résultat. Les ensembles permettent des tests rapides d’appartenance et les listes restent en ordre. Si vous avez besoin de ces deux fonctionnalités sur la même collection, utilisez collections.OrderedDict.

82
Sven Marnach

Dans Python 3.6, set() maintenant devrait garder l'ordre, mais il existe une autre solution pour Python 2 et 3:

>>> x = [1, 2, 20, 6, 210]
>>> sorted(set(x), key=x.index)
[1, 2, 20, 6, 210]
34
Tiger-222

Répondant à votre première question, un ensemble est une structure de données optimisée pour les opérations sur les ensembles. Comme un ensemble mathématique, il n’impose ni ne maintient aucun ordre particulier des éléments. Le concept abstrait d'un ensemble n'impose pas l'ordre et la mise en œuvre n'est donc pas requise. Lorsque vous créez un ensemble à partir d'une liste, Python est libre de modifier l'ordre des éléments en fonction des besoins de l'implémentation interne qu'il utilise pour un ensemble, ce qui permet d'effectuer des opérations d'ensemble efficacement. .

14
lvella

Comme indiqué dans d'autres réponses, les ensembles sont des structures de données (et des concepts mathématiques) qui ne préservent pas l'ordre des éléments -

Cependant, en utilisant une combinaison d'ensembles et de dictionnaires, il est possible d'obtenir ce que vous voulez - essayez d'utiliser ces extraits:

# save the element order in a dict:
x_dict = dict(x,y for y, x in enumerate(my_list) )
x_set = set(my_list)
#perform desired set operations
...
#retrieve ordered list from the set:
new_list = [None] * len(new_set)
for element in new_set:
   new_list[x_dict[element]] = element
3
jsbueno

Une implémentation du concept de score le plus élevé ci-dessus qui le ramène à une liste:

def SetOfListInOrder(incominglist):
    from collections import OrderedDict
    outtemp = OrderedDict()
    for item in incominglist:
        outtemp[item] = None
    return(list(outtemp))

Testé (brièvement) sur Python 3.6 et Python 2.7.

1
Mike Stucka

En me basant sur la réponse de Sven, j'ai trouvé en utilisant des collections.

import collections

x=[1,2,20,6,210]
z=collections.OrderedDict.fromkeys(x)
z
OrderedDict([(1, None), (2, None), (20, None), (6, None), (210, None)])

Si vous souhaitez ajouter des éléments mais que vous le traitez toujours comme un ensemble, vous pouvez simplement faire:

z['nextitem']=None

Et vous pouvez effectuer une opération comme z.keys () sur le dict et obtenir l'ensemble:

z.keys()
[1, 2, 20, 6, 210]
1
jimh

Si vous avez un petit nombre d’éléments dans vos deux listes initiales sur lesquels vous voulez effectuer l’opération de définition de différence, au lieu d’utiliser collections.OrderedDict qui complique la mise en oeuvre et la rend moins lisible, vous pouvez utiliser:

# initial lists on which you want to do set difference
>>> nums = [1,2,2,3,3,4,4,5]
>>> evens = [2,4,4,6]
>>> evens_set = set(evens)
>>> result = []
>>> for n in nums:
...   if not n in evens_set and not n in result:
...     result.append(n)
... 
>>> result
[1, 3, 5]

Sa complexité temporelle n’est pas très bonne mais elle est nette et facile à lire.

0
Ultrablendz

C'est parce que l'ensemble est une structure de données non ordonnée.

Pour maintenir l'ordre, vous pouvez le faire comme suit:

x=[1,2,3,20,6,210,50,20];
print(sorted(set(x),key=x.index));

Comme vous le verrez de cette manière, vous pouvez effectuer des opérations définies sans perdre l’ordre initial.

0
Naman Nimmo