Temukan objek dalam daftar yang memiliki atribut sama dengan beberapa nilai (yang memenuhi persyaratan apa pun)


221

Saya punya daftar objek. Saya ingin menemukan satu (pertama atau apa pun) objek dalam daftar ini yang memiliki atribut (atau hasil metode - apa pun) sama dengan value.

Apa cara terbaik untuk menemukannya?

Inilah test case:

  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

Saya pikir menggunakan generator dan reduce()tidak akan membuat perbedaan karena masih akan mengulangi daftar.

ps .: Persamaan dengan valuehanyalah sebuah contoh. Tentu saja kami ingin mendapatkan elemen yang memenuhi syarat apa pun.


2
Inilah diskusi yang bagus untuk pertanyaan ini: tomayko.com/writings/cleanest-python-find-in-list-function
Andrew Hare

Posting asli adalah ridiculously keluar dari tanggal, tapi 2 respon cocok saya versi satu baris tepat. Saya tidak yakin itu lebih baik daripada versi loop dasar.
agf

Jawaban:


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

Ini mendapatkan item pertama dari daftar yang sesuai dengan kondisi, dan kembali Nonejika tidak ada item yang cocok. Ini bentuk ekspresi tunggal pilihan saya.

Namun,

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

Versi loop-break naif, sangat Pythonic - singkat, jelas, dan efisien. Untuk membuatnya cocok dengan perilaku one-liner:

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

Ini akan menetapkan Noneuntuk xjika Anda tidak breakkeluar dari loop.


72
+1 untuk meyakinkan "Versi loop-break naif, adalah Pythonic sempurna".
LaundroMat

solusi hebat, tapi bagaimana cara memodifikasi baris Anda sehingga saya bisa membuat x.value sebenarnya berarti x.fieldMemberName di mana nama itu disimpan dalam nilai? field = "name" selanjutnya ((x untuk x dalam test_list jika x.field == nilai), Tidak ada) sehingga dalam kasus ini, saya benar-benar memeriksa x.name, bukan x.field
Stewart Dale

3
@StewartDale Tidak sepenuhnya jelas apa yang Anda minta, tapi saya pikir maksud Anda ... if getattr(x, x.fieldMemberName) == value. Itu akan mengambil atribut dari xdengan nama yang tersimpan fieldMemberName, dan membandingkannya dengan value.
agf

1
@ThatTechGuy - elseKlausa dimaksudkan untuk berada di forloop, bukan if. (Edit yang Ditolak).
agf

1
@agf Wow, saya benar-benar tidak punya ide yang ada .. book.pythontips.com/en/latest/for_-_else.html keren!
ThatTechGuy

25

Karena belum disebutkan hanya untuk penyelesaian. Filter ol yang baik untuk memfilter elemen yang akan disaring.

Ftw pemrograman fungsional.

####### 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.

Saya tahu bahwa umumnya dalam daftar python, pemahaman lebih disukai atau setidaknya itulah yang saya baca, tapi saya tidak melihat masalah ini jujur. Tentu saja Python bukan bahasa FP, tetapi Map / Reduce / Filter dapat dibaca dengan sempurna dan merupakan kasus penggunaan standar paling standar dalam pemrograman fungsional.

Jadi begitulah. Ketahuilah pemrograman fungsional Anda.

daftar kondisi filter

Ini tidak akan menjadi lebih mudah dari ini:

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

Saya cukup suka gaya ini tetapi ada dua masalah potensial. 1 : Hanya bekerja di Python 3; di Python 2, filtermengembalikan daftar yang tidak kompatibel dengannya next. 2 : itu mensyaratkan bahwa ada kecocokan yang pasti, kalau tidak Anda akan mendapatkan StopIterationpengecualian.
freethebees

1
1: Saya tidak mengetahui tentang Python 2. Ketika saya mulai menggunakan Python, Python 3 sudah tersedia. Sayangnya saya tidak mengerti tentang spesifikasi Python 2. 2. @freethebees seperti yang ditunjukkan oleh agf. Anda dapat menggunakan berikutnya (..., Tidak Ada) atau beberapa nilai default lainnya, jika Anda bukan penggemar pengecualian. Saya juga menambahkannya sebagai komentar pada kode saya.
Nima Mousavi

@freethebees Point 2 mungkin sebenarnya bagus. Ketika saya membutuhkan objek tertentu dalam daftar, gagal cepat adalah hal yang baik.
kap

7

Contoh sederhana : Kami memiliki array berikut

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

Sekarang, kami ingin menemukan objek dalam array yang memiliki id sama dengan 1

  1. Gunakan metode nextdengan pemahaman daftar
next(x for x in li if x["id"] == 1 )
  1. Gunakan pemahaman daftar dan kembalikan item pertama
[x for x in li if x["id"] == 1 ][0]
  1. Fungsi Kustom
def find(arr , id):
    for x in arr:
        if x["id"] == id:
            return x
find(li , 1)

Keluarkan semua metode di atas {'id': 1, 'name': 'ronaldo'}


1

Saya baru saja mengalami masalah yang sama dan menemukan optimasi kecil untuk kasus di mana tidak ada objek dalam daftar memenuhi persyaratan. (Untuk kasus penggunaan saya ini menghasilkan peningkatan kinerja besar):

Seiring dengan daftar test_list, saya menyimpan set test_value_set tambahan yang terdiri dari nilai-nilai daftar yang saya perlu filter. Jadi di sini bagian lain dari solusi AGF menjadi sangat cepat.


1

Anda bisa melakukan sesuatu seperti ini

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

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

Itulah yang saya gunakan untuk menemukan objek dalam array objek yang panjang.


Apa bedanya dengan yang dilakukan oleh penanya?
Anum Sheraz

Saya ingin menunjukkan bagaimana dia bisa mendapatkan objek dan berbagai objek dengan cara paling sederhana.
Illud

0

Anda juga bisa menerapkan perbandingan kaya melalui __eq__metode untuk Testkelas Anda dan menggunakan inoperator. Tidak yakin apakah ini cara terbaik yang berdiri sendiri, tetapi jika Anda perlu membandingkan Testcontoh berdasarkan valuetempat lain, ini bisa berguna.

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

Untuk kode di bawah ini, xGen adalah ekspresi generator anonom, yFilt adalah objek filter. Perhatikan bahwa untuk xGen, parameter None tambahan dikembalikan daripada membuang StopIteration saat daftar habis.

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))

Keluaran:

<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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.