特定の値に等しい属性を持つ(任意の条件を満たす)リスト内のオブジェクトを検索します


221

オブジェクトのリストを持っています。このリストで、属性(またはメソッドの結果-何でも)がに等しい1つ(最初のオブジェクト)のオブジェクトを見つけたいですvalue

それを見つけるための最良の方法は何ですか?

ここにテストケースがあります:

  class Test:
      def __init__(self, value):
          self.value = value

  import random

  value = 5

  test_list = [Test(random.randint(0,100)) for x in range(1000)]

  # that I would do in Pascal, I don't believe isn't anywhere near 'Pythonic'
  for x in test_list:
      if x.value == value:
          print "i found it!"
          break

ジェネレータを使用すると思いますreduce()が、リストを繰り返し処理するため、違いはありません。

ps .:方程式valueは単なる例です。もちろん、任意の条件を満たす要素を取得したいです。


2
これがこの質問の良い議論です:tomayko.com/writings/cleanest-python-find-in-list-function
Andrew Hare

元の投稿は途方もなく古くなっていますが、2番目の応答は私の1行のバージョンと完全に一致しています。基本的なループバージョンよりも優れているとは思いません。
agf '19

回答:


433
next((x for x in test_list if x.value == value), None)

これは、条件に一致するリストから最初の項目を取得し、一致するNone項目がない場合に戻ります。それは私の好みの単一式フォームです。

しかしながら、

for x in test_list:
    if x.value == value:
        print "i found it!"
        break

素朴なループブレークバージョンは完全にPythonicです-簡潔で、明確で、効率的です。ワンライナーの動作と一致させるには:

for x in test_list:
    if x.value == value:
        print "i found it!"
        break
else:
    x = None

これは、ループから抜け出さない場合に割り当てNoneられます。xbreak


72
安心感を与えるための+1「素朴なループブレークバージョンは完全にPythonicです」。
LaundroMat 2011

素晴らしい解決策ですが、x.valueが実際にその名前が値に格納されているx.fieldMemberNameを意味するように、行を変更するにはどうすればよいですか?field = "name" next((x.field == valueの場合、test_listのxのx)、None)なので、この場合、実際にはx.fieldではなくx.nameに対してチェックしています
Stewart Dale

3
@StewartDaleあなたが何を求めているのかは完全に明確ではありませんが、私が意味して... if getattr(x, x.fieldMemberName) == valueいると思います。これxにより、に保存されている名前の属性が取得されfieldMemberName、と比較されvalueます。
agf 2015

1
@ThatTechGuy-このelse節は、forループではなく、ループ上にあることを意図していますif。(拒否された編集)。
agf 2018

1
@agfうわー私は文字通り存在するという考えを持っていなかった.. book.pythontips.com/en/latest/for_-_else.htmlクール!
ThatTechGuy 2018

25

完了のためだけに言及されていないので。フィルタリングされる要素をフィルタリングするための良いol 'フィルター。

関数型プログラミングftw。

####### Set Up #######
class X:

    def __init__(self, val):
        self.val = val

elem = 5

my_unfiltered_list = [X(1), X(2), X(3), X(4), X(5), X(5), X(6)]

####### Set Up #######

### Filter one liner ### filter(lambda x: condition(x), some_list)
my_filter_iter = filter(lambda x: x.val == elem, my_unfiltered_list)
### Returns a flippin' iterator at least in Python 3.5 and that's what I'm on

print(next(my_filter_iter).val)
print(next(my_filter_iter).val)
print(next(my_filter_iter).val)

### [1, 2, 3, 4, 5, 5, 6] Will Return: ###
# 5
# 5
# Traceback (most recent call last):
#   File "C:\Users\mousavin\workspace\Scripts\test.py", line 22, in <module>
#     print(next(my_filter_iter).value)
# StopIteration


# You can do that None stuff or whatever at this point, if you don't like exceptions.

私は一般にpythonリスト内の理解が優先されるか、少なくともそれを読んだことを知っていますが、問題が正直であるとは思いません。もちろん、PythonはFP言語ではありませんが、Map / Reduce / Filterは完全に読み取り可能であり、関数型プログラミングにおける標準的な使用事例の最も標準的なものです。

さあ、行きます。関数型プログラミングを知ってください。

フィルター条件一覧

これより簡単になることはありません。

next(filter(lambda x: x.val == value,  my_unfiltered_list)) # Optionally: next(..., None) or some other default value to prevent Exceptions

私はこのスタイルがとても好きですが、2つの潜在的な問題があります。1:Python 3でのみ機能します。Python 2では、とfilter互換性のないリストを返しますnext2:確実に一致する必要がありStopIterationます。一致しない場合、例外が発生します。
freethebees

1
1:Python 2を意識していません。Pythonを使い始めたとき、Python 3はすでに利用可能でした。残念ながら、Python 2の仕様については無知です。2。agfが指摘した@freethebees。例外のファンでない場合は、next(...、None)または他のデフォルト値を使用できます。また、コードにコメントとして追加しました。
Nima Mousavi、2018

@freethebeesポイント2は実際には良いかもしれません。リスト内の特定のオブジェクトが必要な場合、すばやく失敗するのは良いことです。
KAP

7

簡単な例:次の配列があります

li = [{"id":1,"name":"ronaldo"},{"id":2,"name":"messi"}]

次に、idが1である配列内のオブジェクトを検索します

  1. nextリスト内包表記でメソッドを使用する
next(x for x in li if x["id"] == 1 )
  1. リスト内包表記を使用して最初のアイテムを返す
[x for x in li if x["id"] == 1 ][0]
  1. カスタム関数
def find(arr , id):
    for x in arr:
        if x["id"] == id:
            return x
find(li , 1)

上記のすべてのメソッドの出力は {'id': 1, 'name': 'ronaldo'}


1

私は同様の問題に遭遇し、リストにオブジェクトが要件を満たしていない場合の小さな最適化を考案しました(私のユースケースでは、これによりパフォーマンスが大幅に向上しました):

リストtest_listとともに、フィルター処理する必要があるリストの値で構成される追加のセットtest_value_setを保持します。したがって、ここでagfのソリューションのその他の部分は非常に高速になります。


1

あなたはこのようなことをすることができます

dict = [{
   "id": 1,
   "name": "Doom Hammer"
 },
 {
    "id": 2,
    "name": "Rings ov Saturn"
 }
]

for x in dict:
  if x["id"] == 2:
    print(x["name"])

これは、オブジェクトの長い配列からオブジェクトを見つけるために使用するものです。


これは、質問者がすでに試したものとどう違うのですか?
Anum Sheraz

私は、彼がオブジェクトのオブジェクトとオブジェクトの配列を最も簡単な方法で取得する方法を示したかったのです。
Illud

0

クラスの__eq__メソッドを介して豊富な比較を実装し、演算子Testを使用することもできますin。これが最適なスタンドアロンの方法であるかどうかはわかりませんが、他の場所にTest基づいてインスタンスを比較する必要がある場合はvalue、これが役立つことがあります。

class Test:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        """To implement 'in' operator"""
        # Comparing with int (assuming "value" is int)
        if isinstance(other, int):
            return self.value == other
        # Comparing with another Test object
        elif isinstance(other, Test):
            return self.value == other.value

import random

value = 5

test_list = [Test(random.randint(0,100)) for x in range(1000)]

if value in test_list:
    print "i found it"

0

以下のコードでは、xGenは無名ジェネレーター式で、yFiltはフィルターオブジェクトです。xGenの場合、リストがすべて使用されたときにStopIterationをスローするのではなく、追加のNoneパラメータが返されることに注意してください。

arr =((10,0), (11,1), (12,2), (13,2), (14,3))

value = 2
xGen = (x for x in arr if x[1] == value)
yFilt = filter(lambda x: x[1] == value, arr)
print(type(xGen))
print(type(yFilt))

for i in range(1,4):
    print('xGen: pass=',i,' result=',next(xGen,None))
    print('yFilt: pass=',i,' result=',next(yFilt))

出力:

<class 'generator'>
<class 'filter'>
xGen: pass= 1  result= (12, 2)
yFilt: pass= 1  result= (12, 2)
xGen: pass= 2  result= (13, 2)
yFilt: pass= 2  result= (13, 2)
xGen: pass= 3  result= None
Traceback (most recent call last):
  File "test.py", line 12, in <module>
    print('yFilt: pass=',i,' result=',next(yFilt))
StopIteration
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.