57

Python でコードを書いているとき、いくつかの基準に基づいて、リストまたはその他のシーケンス タイプから項目を削除する必要があることがよくあります。現在反復しているリストからアイテムを削除するのは悪いことなので、エレガントで効率的な解決策は見つかりませんでした。たとえば、次のことはできません。

for name in names:
    if name[-5:] == 'Smith':
        names.remove(name)

私は通常、次のようなことをすることになります:

toremove = []
for name in names:
    if name[-5:] == 'Smith':
        toremove.append(name)
for name in toremove:
    names.remove(name)
del toremove

これは非効率的で、かなり醜く、おそらくバグがあります (複数の 'John Smith' エントリをどのように処理しますか?)。よりエレガントなソリューション、または少なくともより効率的なソリューションを持っている人はいますか?

辞書で動作するものはどうですか?

4

14 に答える 14

56

フィルタリングだけを行う簡単な方法は次の 2 つです。

  1. 使用filter:

    names = filter(lambda name: name[-5:] != "Smith", names)

  2. リスト内包表記の使用:

    names = [name for name in names if name[-5:] != "Smith"]

どちらの場合も、述語関数が に評価される値を保持することに注意してください。そのためTrue、論理を逆にする必要があります (つまり、「姓を持つ人々を削除する」の代わりに、「Smith という姓を持たない人々を保持する」と言います)。スミス」)。

面白い編集... 2人が、私が投稿したときに提案した両方の回答を個別に投稿しました。

于 2008-08-20T17:50:47.003 に答える
37

リストを逆方向に反復することもできます。

for name in reversed(names):
    if name[-5:] == 'Smith':
        names.remove(name)

これには、新しいリストを作成せず (filterまたはリスト内包表記のように)、リストのコピーの代わりに反復子を使用するという利点があります (のように[:])。

逆方向に反復しながら要素を削除することは安全ですが、それらを挿入することはやや難しいことに注意してください。

于 2008-10-08T01:24:09.233 に答える
29

明らかな答えは、ジョンと他の数人が与えたものです。

>>> names = [name for name in names if name[-5:] != "Smith"]       # <-- slower

ただし、元のオブジェクトを再利用するのではなく、新しいリスト オブジェクトを作成するという欠点があります。私はいくつかのプロファイリングと実験を行いましたが、私が思いついた最も効率的な方法は次のとおりです。

>>> names[:] = (name for name in names if name[-5:] != "Smith")    # <-- faster

「names[:]」への割り当ては、基本的に「名前リストの内容を次の値に置き換える」ことを意味します。新しいリスト オブジェクトを作成しないという点で、単に名前を割り当てるのとは異なります。代入の右側はジェネレーター式です (角括弧ではなく括弧を使用していることに注意してください)。これにより、Python はリスト全体を反復処理します。

いくつかの簡単なプロファイリングは、これがリスト理解アプローチよりも約 30% 速く、フィルター アプローチよりも約 40% 速いことを示唆しています。

警告: このソリューションは明白なソリューションよりも高速ですが、よりあいまいであり、より高度な Python 手法に依存しています。使用する場合は、コメントを付けることをお勧めします。おそらく、この特定の操作のパフォーマンスを本当に気にする場合にのみ使用する価値があります (これは何があっても非常に高速です)。(これを使用した場合、A* ビーム探索を行っていて、これを使用して探索ビームから探索ポイントを削除しました。)

于 2011-01-09T14:41:40.307 に答える
10

リスト内包表記の使用

list = [x for x in list if x[-5:] != "smith"]
于 2008-08-20T17:49:29.783 に答える
4

フィルタリング(フィルターまたはリスト内包表記のいずれかを使用)が機能しない場合があります。これは、変更しているリストへの参照を他のオブジェクトが保持していて、その場でリストを変更する必要がある場合に発生します。

for name in names[:]:
    if name[-5:] == 'Smith':
        names.remove(name)

元のコードとの唯一の違いは、forループのnames[:]代わりにを使用することです。namesこのようにして、コードはリストの(浅い)コピーを繰り返し処理し、削除は期待どおりに機能します。リストのコピーは浅いので、かなり速いです。

于 2008-10-05T11:48:45.387 に答える
3

これにはフィルターが最適です。簡単な例:

names = ['mike', 'dave', 'jim']
filter(lambda x: x != 'mike', names)
['dave', 'jim']

編集: Corey のリストの理解も素晴らしいです。

于 2008-08-20T17:49:10.427 に答える
2

辞書の操作に関する質問に答えるには、Python3.0にdictの内包表記が含まれることに注意してください。

>>> {i : chr(65+i) for i in range(4)}

それまでの間、次のように準辞書理解を行うことができます。

>>> dict([(i, chr(65+i)) for i in range(4)])

または、より直接的な答えとして:

dict([(key, name) for key, name in some_dictionary.iteritems if name[-5:] != 'Smith'])
于 2008-10-07T14:33:49.420 に答える
2

フィルター理解の両方のソリューションでは、新しいリストを作成する必要があります。確かにPythonの内部については十分にわかりませんが、より伝統的な(しかしあまりエレガントではない)アプローチの方が効率的だと思います。

names = ['Jones', 'Vai', 'Smith', 'Perez']

item = 0
while item <> len(names):
    name = names [item]
    if name=='Smith':
        names.remove(name)
    else:
        item += 1

print names

とにかく、短いリストについては、私は以前に提案された2つの解決策のいずれかに固執します。

于 2008-08-20T18:20:33.970 に答える
2

リストをインプレースでフィルタリングする必要があり、リストサイズが非常に大きい場合、list.remove()に基づく前の回答で説明したアルゴリズムは、計算の複雑さがO(n ^ 2)であるため、不適切な場合があります。 。この場合、次のno-sopythonic関数を使用できます。

def filter_inplace(func, original_list):
  """ Filters the original_list in-place.

  Removes elements from the original_list for which func() returns False.

  Algrithm's computational complexity is O(N), where N is the size
  of the original_list.
  """

  # Compact the list in-place.
  new_list_size = 0
  for item in original_list:
    if func(item):
      original_list[new_list_size] = item
      new_list_size += 1

  # Remove trailing items from the list.
  tail_size = len(original_list) - new_list_size
  while tail_size:
    original_list.pop()
    tail_size -= 1


a = [1, 2, 3, 4, 5, 6, 7]

# Remove even numbers from a in-place.
filter_inplace(lambda x: x & 1, a)

# Prints [1, 3, 5, 7]
print a

編集:実際には、https ://stackoverflow.com/a/4639748/274937のソリューションは私のソリューションよりも優れています。それはよりpythonicであり、より速く動作します。したがって、ここに新しいfilter_inplace()の実装があります。

def filter_inplace(func, original_list):
  """ Filters the original_list inplace.

  Removes elements from the original_list for which function returns False.

  Algrithm's computational complexity is O(N), where N is the size
  of the original_list.
  """
  original_list[:] = [item for item in original_list if func(item)]
于 2012-04-02T16:20:45.203 に答える
2
names = filter(lambda x: x[-5:] != "Smith", names);
于 2008-08-20T17:48:56.730 に答える
1

セットの場合。

toRemove = set([])  
for item in mySet:  
    if item is unwelcome:  
        toRemove.add(item)  
mySets = mySet - toRemove 
于 2009-12-07T04:01:43.500 に答える
1

これは、リストからアイテムをインプレースでフィルタリングするために使用できる私のfilter_inplace実装です。このページを見つける前に、私は自分でこれを思いつきました。これは PabloG が投稿したものと同じアルゴリズムですが、より一般的なものになっているため、リストを適切にフィルタリングするために使用できます。また、comparisonFuncif reversed is setに基づいてリストから削除することもできTrueます。必要に応じて、一種の逆フィルターです。

def filter_inplace(conditionFunc, list, reversed=False):
    index = 0
    while index < len(list):
        item = list[index]

        shouldRemove = not conditionFunc(item)
        if reversed: shouldRemove = not shouldRemove

        if shouldRemove:
            list.remove(item)
        else:
            index += 1
于 2013-03-15T14:12:56.133 に答える
1

フィルターとリストの理解はあなたの例では問題ありませんが、いくつかの問題があります。

  • リストのコピーを作成して新しいリストを返しますが、元のリストが非常に大きい場合は非効率的です
  • アイテムを選択する基準 (あなたの場合、 name[-5:] == 'Smith' の場合) がより複雑であるか、いくつかの条件がある場合、それらは非常に面倒になる可能性があります。

あなたの元のソリューションは、実際には非常に大きなリストに対してより効率的です。ただし、「John Smith」が複数あるのではないかと心配な場合は、値ではなく位置に基づいて削除することで修正できます。

names = ['Jones', 'Vai', 'Smith', 'Perez', 'Smith']

toremove = []
for pos, name in enumerate(names):
    if name[-5:] == 'Smith':
        toremove.append(pos)
for pos in sorted(toremove, reverse=True):
    del(names[pos])

print names

リストのサイズを考慮せずにソリューションを選択することはできませんが、大きなリストの場合は、フィルターまたはリストの内包表記ではなく、2 パス ソリューションを使用することをお勧めします

于 2008-10-02T18:44:42.613 に答える
-2

これは明らかに、使用しているデータ構造の問題です。たとえば、ハッシュテーブルを使用します。一部の実装では、キーごとに複数のエントリがサポートされているため、最新の要素をポップするか、すべて削除することができます。

しかし、これはアルゴリズムではなく、別のデータ構造によるエレガンスです。ソートされていればもっとうまくできるかもしれませんが、ここではリストの反復が唯一の方法です。

編集:彼が「効率」を求めたことに気づきました...これらの提案されたすべての方法は、彼が提案したものと同じリストを反復するだけです。

于 2008-08-20T17:46:43.630 に答える