関数matchCondition(x)
がある場合、その条件に一致するPythonリスト内の最初のn
アイテムを削除するにはどうすればよいですか?
1つの解決策は、各アイテムを反復処理し、削除対象としてマークする(たとえば、None
に設定する)ことで、リストを包括的にフィルター処理することです。これには、リストを2回繰り返し、データを変更する必要があります。これを行うためのより慣用的または効率的な方法はありますか?
n = 3
def condition(x):
return x < 5
data = [1, 10, 2, 9, 3, 8, 4, 7]
out = do_remove(data, n, condition)
print(out) # [10, 9, 8, 4, 7] (1, 2, and 3 are removed, 4 remains)
_itertools.filterfalse
_ および _itertools.count
_ を使用する1つの方法:
_from itertools import count, filterfalse
data = [1, 10, 2, 9, 3, 8, 4, 7]
output = filterfalse(lambda L, c=count(): L < 5 and next(c) < 3, data)
_
次にlist(output)
を使用すると、次のことができます。
_[10, 9, 8, 4, 7]
_
反復可能、条件、およびドロップする量を取るジェネレーターを作成します。データを反復処理し、条件を満たさないアイテムを生成します。条件が満たされている場合、カウンターをインクリメントし、値を生成しません。カウンターがドロップする量に達したら、常にアイテムを生成します。
def iter_drop_n(data, condition, drop):
dropped = 0
for item in data:
if dropped >= drop:
yield item
continue
if condition(item):
dropped += 1
continue
yield item
data = [1, 10, 2, 9, 3, 8, 4, 7]
out = list(iter_drop_n(data, lambda x: x < 5, 3))
これはリストの余分なコピーを必要とせず、リストに対して1回だけ反復し、各アイテムに対して条件を1回だけ呼び出します。リスト全体を実際に表示する場合を除き、結果に対するlist
呼び出しを省略し、返されたジェネレーターを直接反復処理します。
受け入れられた答えは、私の好みには少し魔法すぎました。以下は、フローを少しわかりやすく説明したものです。
def matchCondition(x):
return x < 5
def my_gen(L, drop_condition, max_drops=3):
count = 0
iterator = iter(L)
for element in iterator:
if drop_condition(element):
count += 1
if count >= max_drops:
break
else:
yield element
yield from iterator
example = [1, 10, 2, 9, 3, 8, 4, 7]
print(list(my_gen(example, drop_condition=matchCondition)))
davidism answerのロジックと似ていますが、すべてのステップでドロップカウントを超えているかどうかをチェックする代わりに、残りのループを短絡させます。
注:持っていない場合 yield from
が利用可能です。iterator
の残りのアイテムに対する別のforループに置き換えてください。
突然変異が必要な場合:
def do_remove(ls, N, predicate):
i, delete_count, l = 0, 0, len(ls)
while i < l and delete_count < N:
if predicate(ls[i]):
ls.pop(i) # remove item at i
delete_count, l = delete_count + 1, l - 1
else:
i += 1
return ls # for convenience
assert(do_remove(l, N, matchCondition) == [10, 9, 8, 4, 7])
簡単なPython:
N = 3
data = [1, 10, 2, 9, 3, 8, 4, 7]
def matchCondition(x):
return x < 5
c = 1
l = []
for x in data:
if c > N or not matchCondition(x):
l.append(x)
else:
c += 1
print(l)
必要に応じて、これは簡単にジェネレーターに変換できます。
def filter_first(n, func, iterable):
c = 1
for x in iterable:
if c > n or not func(x):
yield x
else:
c += 1
print(list(filter_first(N, matchCondition, data)))
Python 3.8
の開始、および 割り当て式の導入(PEP 572) (:=
演算子)を使用して、リスト内包内の変数をインクリメントできます。
# items = [1, 10, 2, 9, 3, 8, 4, 7]
total = 0
[x for x in items if not (x < 5 and (total := total + 1) <= 3)]
# [10, 9, 8, 4, 7]
この:
total
から0
に初期化します。これは、リスト内包内で以前に一致したオカレンスの数を象徴しますx < 5
)total
(total := total + 1
)のインクリメントtotal
の新しい値を廃棄するアイテムの最大数(3
)と比較しますリスト内包表記の使用:
n = 3
data = [1, 10, 2, 9, 3, 8, 4, 7]
count = 0
def counter(x):
global count
count += 1
return x
def condition(x):
return x < 5
filtered = [counter(x) for x in data if count < n and condition(x)]
これは、ブール短絡のおかげでn要素が見つかった後に条件のチェックも停止します。