web-dev-qa-db-fra.com

Suppression de plusieurs éléments d'une liste

Est-il possible de supprimer plusieurs éléments d'une liste en même temps? Si je veux supprimer des éléments d'index 0 et 2 et essayer quelque chose comme del somelist [0], suivi de del somelist [2], la deuxième instruction supprimera en fait somelist [3].

Je suppose que je pourrais toujours supprimer d’abord les éléments dont le numéro est le plus élevé, mais j’espère qu’il existe un meilleur moyen.

138
Joe Calimari

Probablement pas la meilleure solution à ce problème:

indices = 0, 2
somelist = [i for j, i in enumerate(somelist) if j not in indices]
96
SilentGhost

Pour une raison quelconque, je n'aime aucune des réponses ici. Oui, ils fonctionnent, mais à proprement parler, la plupart d'entre eux ne suppriment pas les éléments d'une liste, n'est-ce pas? (Mais faire une copie puis remplacer l’original par la copie modifiée).

Pourquoi ne pas simplement supprimer d'abord l'indice le plus élevé?

Y a-t-il une raison à cela? Je voudrais juste faire:

for i in sorted(indices, reverse=True):
    del somelist[i]

Si vous ne voulez vraiment pas supprimer les éléments à l'envers, alors vous devez tout simplement désincrémenter les valeurs d'index supérieures au dernier index supprimé (vous ne pouvez pas utiliser le même index car vous avez une liste différente) ou utiliser une copie de la liste (qui ne consisterait pas à supprimer mais à remplacer l'original par une copie modifiée).

Est-ce que je manque quelque chose ici, une raison de ne pas supprimer dans l'ordre inverse?

141
tglaria

Si vous supprimez plusieurs éléments non adjacents, alors ce que vous décrivez est le meilleur moyen (et oui, assurez-vous de commencer à partir de l'index le plus élevé).

Si vos éléments sont adjacents, vous pouvez utiliser la syntaxe d'attribution de tranche:

a[2:10] = []
101
Greg Hewgill

En tant que fonction:

def multi_delete(list_, *args):
    indexes = sorted(list(args), reverse=True)
    for index in indexes:
        del list_[index]
    return list_

S'exécute en n log (n) temps, ce qui devrait en faire la solution correcte la plus rapide à ce jour.

18
Nikhil Chelliah

En tant que spécialisation de la réponse de Greg, vous pouvez même utiliser la syntaxe de tranche étendue. par exemple. Si vous souhaitez supprimer les éléments 0 et 2:

>>> a= [0, 1, 2, 3, 4]
>>> del a[0:3:2]
>>> a
[1, 3, 4]

Bien sûr, cela ne couvre aucune sélection arbitraire, mais cela peut certainement fonctionner pour supprimer deux éléments.

17
bobince

Vous pouvez utiliser numpy.delete comme suit:

import numpy as np
a = ['a', 'l', 3.14, 42, 'u']
I = [0, 2]
np.delete(a, I).tolist()
# Returns: ['l', '42', 'u']

Si cela ne vous dérange pas de vous retrouver avec un tableau numpy à la fin, vous pouvez laisser de côté la .tolist(). Vous devriez également constater des améliorations assez importantes en termes de vitesse, ce qui en fait une solution plus évolutive. Je ne l'ai pas évalué, mais les opérations numpy sont du code compilé écrit en C ou en Fortran.

17
philE

Donc, vous voulez essentiellement supprimer plusieurs éléments en un seul passage? Dans ce cas, la position du prochain élément à supprimer sera compensée par le nombre de ceux précédemment supprimés.

Notre objectif est de supprimer toutes les voyelles, qui sont précalculées aux indices 1, 4 et 7. Notez qu’il est important que les index to_delete soient dans l’ordre croissant, sinon cela ne fonctionnera pas.

to_delete = [1, 4, 7]
target = list("hello world")
for offset, index in enumerate(to_delete):
  index -= offset
  del target[index]

Ce serait plus compliqué si vous vouliez supprimer les éléments dans n'importe quel ordre. IMO, trier to_delete pourrait être plus facile que de déterminer quand vous devriez ou non soustraire à index.

11
Richard Levasseur

Je suis un débutant en Python et, pour le moins, ma programmation est rude et sale, mais ma solution consistait à utiliser une combinaison des commandes de base que j'avais apprises dans les premiers tutoriels:

SomeList = [1,2,3,4,5,6,7,8,10]
Rem = [0,5,7]

for i in Rem:
    SomeList[i]='!' # mark for deletion

for i in range(0,SomeList.count('!')):
    SomeList.remove('!') # remove
print SomeList

Évidemment, le fait de devoir choisir un caractère de "marque de suppression" a ses limites.

En ce qui concerne les performances à mesure que la taille de la liste évolue, je suis sûr que ma solution est sous-optimale. Cependant, c'est simple, ce qui, je l'espère, plaira à d'autres débutants, et fonctionnera dans les cas simples où SomeList est d'un format bien connu, par exemple, toujours numérique ...

6
Paul

Voici une alternative, qui n'utilise pas enumerate () pour créer des nuplets (comme dans la réponse originale de SilentGhost).

Cela me semble plus lisible. (Peut-être que je me sentirais différemment si j'avais l'habitude d'utiliser énumérer.) CAVEAT: Je n'ai pas testé les performances des deux approches.

# Returns a new list. "lst" is not modified.
def delete_by_indices(lst, indices):
    indices_as_set = set(indices)
    return [ lst[i] for i in xrange(len(lst)) if i not in indices_as_set ]

REMARQUE: Python 2.7 syntaxe. Pour Python 3, xrange => range.

Usage:

lst = [ 11*x for x in xrange(10) ]
somelist = delete_by_indices( lst, [0, 4, 5])

somelist:

[11, 22, 33, 66, 77, 88, 99]

--- BONUS ---

Supprimer plusieurs valeurs d'une liste. Autrement dit, nous avons les valeurs que nous voulons supprimer:

# Returns a new list. "lst" is not modified.
def delete__by_values(lst, values):
    values_as_set = set(values)
    return [ x for x in lst if x not in values_as_set ]

Usage:

somelist = delete__by_values( lst, [0, 44, 55] )

somelist:

[11, 22, 33, 66, 77, 88, 99]

La réponse est la même que précédemment, mais cette fois nous avons fourni les VALEURS à supprimer [0, 44, 55].

5
ToolmakerSteve

Une autre méthode de compréhension de liste qui utilise les valeurs d'index de liste:

stuff = ['a', 'b', 'c', 'd', 'e', 'f', 'woof']
index = [0, 3, 6]
new = [i for i in stuff if stuff.index(i) not in index]

Cela retourne:

['b', 'c', 'e', 'f']
4
Meow

voici une autre méthode qui supprime les éléments en place. de plus, si votre liste est vraiment longue, elle est plus rapide.

>>> a = range(10)
>>> remove = [0,4,5]
>>> from collections import deque
>>> deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)

>>> timeit.timeit('[i for j, i in enumerate(a) if j not in remove]', setup='import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1)
0.1704120635986328

>>> timeit.timeit('deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)', setup='from collections import deque;import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1)
0.004853963851928711
4
user545424
l = ['a','b','a','c','a','d']
to_remove = [1, 3]
[l[i] for i in range(0, len(l)) if i not in to_remove])

C'est fondamentalement la même chose que la réponse la plus votée, mais une manière différente de l'écrire. Notez que l'utilisation de l.index () n'est pas une bonne idée, car elle ne peut pas gérer les éléments dupliqués dans une liste.

3
zinc

Cela a été mentionné, mais d’une manière ou d’une autre, personne n’a réussi à bien faire les choses.

Sur O(n) la solution serait:

indices = {0, 2}
somelist = [i for j, i in enumerate(somelist) if j not in indices]

Ceci est vraiment proche de version de SilentGhost , mais ajoute deux accolades.

3
Veedrac

Je cherchais un moyen de comparer les différentes solutions facilitant la rotation des boutons.

J'ai d'abord généré mes données:

import random

N = 16 * 1024
x = range(N)
random.shuffle(x)
y = random.sample(range(N), N / 10)

Puis j'ai défini mes fonctions:

def list_set(value_list, index_list):
    index_list = set(index_list)
    result = [value for index, value in enumerate(value_list) if index not in index_list]
    return result

def list_del(value_list, index_list):
    for index in sorted(index_list, reverse=True):
        del(value_list[index])

def list_pop(value_list, index_list):
    for index in sorted(index_list, reverse=True):
        value_list.pop(index)

Ensuite, j'ai utilisé timeit pour comparer les solutions:

import timeit
from collections import OrderedDict

M = 1000
setup = 'from __main__ import x, y, list_set, list_del, list_pop'
statement_dict = OrderedDict([
    ('overhead',  'a = x[:]'),
    ('set', 'a = x[:]; list_set(a, y)'),
    ('del', 'a = x[:]; list_del(a, y)'),
    ('pop', 'a = x[:]; list_pop(a, y)'),
])

overhead = None
result_dict = OrderedDict()
for name, statement in statement_dict.iteritems():
    result = timeit.timeit(statement, number=M, setup=setup)
    if overhead is None:
        overhead = result
    else:
        result = result - overhead
        result_dict[name] = result

for name, result in result_dict.iteritems():
    print "%s = %7.3f" % (name, result)

sortie

set =   1.711
del =   3.450
pop =   3.618

Donc, le générateur avec les indices dans un set était le gagnant. Et del est légèrement plus rapide que pop.

2
David Cullen

Supprimer la méthode entraînera de nombreux décalages d'éléments de liste. Je pense qu'il est préférable de faire une copie:

...
new_list = []
for el in obj.my_list:
   if condition_is_true(el):
      new_list.append(el)
del obj.my_list
obj.my_list = new_list
...
2
luca

nous pouvons le faire en utilisant une boucle for itérant sur les index après avoir trié la liste des index par ordre décroissant

mylist=[66.25, 333, 1, 4, 6, 7, 8, 56, 8769, 65]
indexes = 4,6
indexes = sorted(indexes, reverse=True)
for i in index:
    mylist.pop(i)
print mylist
2
Gourav Singla

techniquement, la réponse est NON, il n'est pas possible de supprimer deux objets AT THE MEME HEURE. Cependant, il est IS possible de supprimer deux objets dans une ligne de beau python.

del (foo['bar'],foo['baz'])

supprime de manière récurrente foo['bar'], puis foo['baz']

2
David Brilliant

Pour les indices 0 et 2 de listA:

for x in (2,0): listA.pop(x)

Pour certains indices aléatoires à retirer de listA:

indices=(5,3,2,7,0) 
for x in sorted(indices)[::-1]: listA.pop(x)
2
jam

Vous pouvez utiliser cette logique:

my_list = ['Word','yes','no','Nice']

c=[b for i,b in enumerate(my_list) if not i in (0,2,3)]

print c
2
raghu

Généraliser le commentaire de @ sth . La suppression d'élément dans n'importe quelle classe, qui implémente abc.MutableSequence , et dans list en particulier, est effectuée via la méthode magique __delitem__. Cette méthode fonctionne de manière similaire à __getitem__, ce qui signifie qu’elle peut accepter un entier ou une tranche. Voici un exemple:

class MyList(list):
    def __delitem__(self, item):
        if isinstance(item, slice):
            for i in range(*item.indices(len(self))):
                self[i] = 'null'
        else:
            self[item] = 'null'


l = MyList(range(10))
print(l)
del l[5:8]
print(l)

Cela produira

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 'null', 'null', 'null', 8, 9]
1
Alexander Zhukov

Vous pouvez le faire de cette manière sur un dict, pas sur une liste. Dans une liste, les éléments sont en séquence. Dans un dict, ils ne dépendent que de l'index.

Code simple juste pour l'expliquer en faisant:

>>> lst = ['a','b','c']
>>> dct = {0: 'a', 1: 'b', 2:'c'}
>>> lst[0]
'a'
>>> dct[0]
'a'
>>> del lst[0]
>>> del dct[0]
>>> lst[0]
'b'
>>> dct[0]
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    dct[0]
KeyError: 0
>>> dct[1]
'b'
>>> lst[1]
'c'

Un moyen de "convertir" une liste dans un dict est:

>>> dct = {}
>>> for i in xrange(0,len(lst)): dct[i] = lst[i]

L'inverse est:

lst = [dct[i] for i in sorted(dct.keys())] 

Quoi qu'il en soit, je pense qu'il est préférable de commencer à supprimer de l'indice supérieur, comme vous l'avez dit.

1
Andrea Ambu

Je peux réellement penser à deux façons de le faire:

  1. découpe la liste de la manière suivante (ceci supprime les 1er, 3ème et 8ème éléments)

    somelist = somelist [1: 2] + somelist [3: 7] + somelist [8:]

  2. faites-le en place, mais un à la fois:

    somelist.pop (2) somelist.pop (0)

1

L'importer uniquement pour cette raison est peut-être excessif, mais s'il vous arrive d'utiliser pandas de toute façon, la solution est simple et directe:

import pandas as pd
stuff = pd.Series(['a','b','a','c','a','d'])
less_stuff = stuff[stuff != 'a']  # define any condition here
# results ['b','c','d']
1
Lorinc Nyitrai

Une autre implémentation de l'idée de retirer de l'indice le plus élevé.

for i in range(len(yourlist)-1, -1, -1):
    del yourlist(i)
1
bakka
some_list.remove(some_list[max(i, j)])

Évite les coûts de tri et la copie explicite de la liste.

1
Chester

Qu'en est-il de l’un d’eux (je suis très nouveau en Python, mais ils semblent ok):

ocean_basin = ['a', 'Atlantic', 'Pacific', 'Indian', 'a', 'a', 'a']
for i in range(1, (ocean_basin.count('a') + 1)):
    ocean_basin.remove('a')
print(ocean_basin)

['Atlantique', 'Pacifique', 'Indien']

ob = ['a', 'b', 4, 5,'Atlantic', 'Pacific', 'Indian', 'a', 'a', 4, 'a']
remove = ('a', 'b', 4, 5)
ob = [i for i in ob if i not in (remove)]
print(ob)

['Atlantique', 'Pacifique', 'Indien']

0
user12001090