4
votes

Existe-t-il une méthode de type `strip` pour une liste?

La méthode buildin strip en python peut supprimer facilement les sous-chaînes de remplissage qui répondent à une condition personnalisée. par exemple

[i for i in input if i != "0"]

coupera le zéro de remplissage des deux côtés de la chaîne, et retournera 11110001111.

Je voudrais trouver une fonction similaire pour une liste. par exemple, pour une liste donnée

output = ["1", "1", "0", "0", "1", "0", "1"]

la sortie attendue sera

input = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]

Les éléments de l'exemple input sont trop simplifiés, il peut s'agir de n'importe quel autre objet python .

list comprehension supprimera tous les éléments, au lieu de ceux de remplissage.

"000011110001111000".strip("0")


0 commentaires

5 Réponses :


5
votes

Utilisez itertools.drop while des deux côtés:

['1', '1', '0', '0', '1', '0', '1']

Sortie:

from itertools import dropwhile

input_data = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]

def predicate(x):
    return x == '0'

result = list(dropwhile(predicate, list(dropwhile(predicate, input_data))[::-1]))[::-1]
result


0 commentaires

0
votes

Il n'y a pas de méthode intégrée. Vous pouvez utiliser itertools.drop while pour dépouiller à gauche. La suppression du droit peut être possible en utilisant une fonction de générateur.

import itertools as it
stripleft = list(it.dropwhile(lambda x: x==myitem, inputlist))


0 commentaires

1
votes

Vous pouvez supprimer à droite avec while / pop.

from collections import deque
input = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]
input = deque(input)
while input and input[-1] == '0': input.pop()
while input and input[0] == '0': input.popleft()

Vous pouvez supprimer à gauche avec itertools.drop while , mais vous devrez peut-être créer une nouvelle liste .

from itertools import dropwhile
input = [*dropwhile(lambda x: x=='0', input)]

Ou vous pouvez tandis que / pop efficacement des deux extrémités en convertissant en un deque.

input = ["0", "0", "1", "1", "0", "0", "1", "0", "1", "0", "0", "0"]
while input and input[-1] == "0": input.pop()

(Aussi input () est déjà une fonction intégrée, il vaut donc mieux ne pas réutiliser ce nom pour les variables.)


0 commentaires

2
votes

Aucune méthode de liste, mais il n'est pas difficile d'implémenter une telle fonction: recherchez les index souhaités, puis découpez-les.

def strip_seq(predicate, xs):
    def scan(xs):
        return next((i for i, x in enumerate(xs) if not predicate(x)), 0)
    return xs[scan(xs) : -scan(reversed(xs)) or None]

xs = ["0", "0", "a", "1", "0", "0", "1", "0", "b", "0", "0", "0"]
print(strip_seq(lambda x: x=='0', xs))  # ['a', '1', '0', '0', '1', '0', 'b']

Cela devrait fonctionner sur tous les types de séquence à découper, y compris des chaînes et des tuples.


3 commentaires

J'ai remarqué qu'il y avait un bug caché dans cette fonction. Si la liste n'est pas des éléments finis qui remplissent la condition requise, une liste vide sera renvoyée. par exemple, xs = ["0", "0", "a", "1", "0", "0", "1", "0", "b"]


@ChangYe essayez la nouvelle version.


ou None est une solution très intelligente. C'est bien mieux que len (xs) - scan (reverse (xs)) . Merci gilch.



0
votes

Une solution "basique" est donnée ci-dessous. Les solutions utilisant drop while inversent l'entrée et cela peut être coûteux dans certains cas.

La solution ci-dessous calcule les indices de début et de fin et renvoie une tranche de l'entrée.

Cette solution fonctionne pour d'autres types de séquences tel que tuple en ce que la valeur de retour est du même type que l'entrée. Cette méthode fonctionne également pour les entrées de chaîne, bien que str.split () serait probablement plus rapide.

# input len = 1,000,000, output len = 0
# predicate = lambda x: x == '0'; input_data = ['0'] * 1000000

# dropwhile solution:
5 loops, best of 5: 98 msec per loop

# basic solution:
2 loops, best of 5: 183 msec per loop

À titre de comparaison (dans cette solution, un une partie de l'entrée est inversée deux fois):

# input len = 1,000,000, output len = 1,000,000
# predicate = lambda x: x == '0'; input_data = ['1'] * 1000000

# dropwhile solution:
10 loops, best of 5: 29.3 msec per loop

# basic solution:
100 loops, best of 5: 3.58 msec per loop

Voici quelques nombres utilisant timeit . Notez que pour le cas pathologique où l'entrée est grande et il n'y a pas d'éléments à dépouillée, la solution goutte-à-goutte est environ 8 fois plus lente que la solution de base. Dans le cas où l'entrée est grande et tous les éléments sont dépouillés, la solution de base est d'environ 1,9 fois plus lent que la solution droptime.

# input len = 12, output len = 7
# predicate = lambda x: x == '0'; input_data = list('001100101000')

# dropwhile solution:
200000 loops, best of 5: 1.84 usec per loop

# basic solution:
200000 loops, best of 5: 1.51 usec per loop
def strip_dropwhile(input_data, predicate):
    return list(dropwhile(predicate, list(dropwhile(predicate, input_data))[::-1]))[::-1]
def strip_basic(input_data, predicate):
    N = len(input_data)
    if not N:
        return input_data[0:0]      # empty sequence, new copy for mutables
    i = 0
    while i < N and predicate(input_data[i]):
        i = i + 1
    if i == N:                      # optimization
        return input_data[0:0]      # empty sequence
    j = N - 1
    while j and predicate(input_data[j]):
        j = j - 1
    j = j + 1
    return input_data[i:j]


0 commentaires