กรองรายการในพจนานุกรม python ที่คีย์มีสตริงเฉพาะ


97

ฉันเป็น C coder ที่กำลังพัฒนาบางอย่างใน python ฉันรู้วิธีทำสิ่งต่อไปนี้ใน C (และด้วยเหตุนี้ในตรรกะแบบ C จึงใช้กับ python) แต่ฉันสงสัยว่าวิธีการทำ 'Python' คืออะไร

ฉันมีพจนานุกรม d และฉันต้องการดำเนินการกับชุดย่อยของรายการเฉพาะผู้ที่มีคีย์ (สตริง) เท่านั้นที่มีสตริงย่อยเฉพาะ

กล่าวคือตรรกะ C จะเป็น:

for key in d:
    if filter_string in key:
        # do something
    else
        # do nothing, continue

ฉันกำลังจินตนาการว่าเวอร์ชัน python จะเป็นอย่างไร

filtered_dict = crazy_python_syntax(d, substring)
for key,value in filtered_dict.iteritems():
    # do something

ฉันพบโพสต์มากมายเกี่ยวกับการกรองพจนานุกรม แต่ไม่พบบทความที่เกี่ยวข้องกับเรื่องนี้

พจนานุกรมของฉันไม่ซ้อนกันและฉันใช้ python 2.7



คำตอบ:


187

ความเข้าใจในการเขียนตามคำบอก :

filtered_dict = {k:v for k,v in d.iteritems() if filter_string in k}

สิ่งที่คุณเห็นมันควรจะอธิบายตัวเองได้เพราะมันอ่านเหมือนภาษาอังกฤษได้ดี

ไวยากรณ์นี้ต้องการ Python 2.7 หรือสูงกว่า

ใน Python 3 มีเพียงอย่างเดียวdict.items()ไม่iteritems()ให้คุณใช้:

filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}

1
ทำไมไม่filtered_dict = {k:d[k] for k in d if filter_string in k}?
thefourtheye

5
@thefourtheye ฉันจะเดาว่าของฉันเร็วกว่าเพราะมันไม่เกิดการd[k]ค้นหา
Jonathon Reinhart

นอกจากนี้เขายังกล่าว# do somethingในความคิดเห็น แต่เราวางคีย์ไว้สองสามอันที่นี่
thefourtheye

เรามีiteritemsใน Python 3 หรือไม่? ฉันไม่คิดอย่างนั้น ดังนั้นเวอร์ชันของฉันจะเข้ากันได้หรือไม่?
thefourtheye

1
ในหลาม 3 คุณจะเข้ามาแทนที่iteritemsด้วยitemsซึ่งเป็นเช่นเดียวกับงูหลาม iteritems2.7
Jonathon Reinhart

18

เลือกสิ่งที่อ่านง่ายที่สุดและบำรุงรักษาได้ง่ายที่สุด เพียงเพราะคุณสามารถเขียนเป็นบรรทัดเดียวไม่ได้หมายความว่าคุณควร โซลูชันที่มีอยู่ของคุณใกล้เคียงกับสิ่งที่ฉันจะใช้นอกเหนือจากที่ฉันจะใช้ iteritems เพื่อข้ามการค้นหาค่าและฉันเกลียด ifs ที่ซ้อนกันหากฉันสามารถหลีกเลี่ยงได้:

for key, val in d.iteritems():
    if filter_string not in key:
        continue
    # do something

อย่างไรก็ตามหากคุณต้องการบางสิ่งบางอย่างเพื่อให้คุณวนซ้ำผ่านคำสั่งที่กรองแล้วฉันจะไม่ทำกระบวนการสองขั้นตอนในการสร้างคำสั่งที่กรองแล้วจากนั้นทำซ้ำผ่านมัน แต่ใช้เครื่องกำเนิดไฟฟ้าแทนเพราะอะไรที่เป็น pythonic (และน่ากลัว) มากกว่า เครื่องกำเนิดไฟฟ้า?

ก่อนอื่นเราต้องสร้างเครื่องกำเนิดไฟฟ้าของเราและการออกแบบที่ดีจะกำหนดให้เราทำให้เป็นนามธรรมมากพอที่จะนำมาใช้ซ้ำได้:

# The implementation of my generator may look vaguely familiar, no?
def filter_dict(d, filter_string):
    for key, val in d.iteritems():
        if filter_string not in key:
            continue
        yield key, val

จากนั้นเราสามารถใช้เครื่องกำเนิดไฟฟ้าเพื่อแก้ปัญหาของคุณได้ดีและหมดจดด้วยรหัสที่เข้าใจง่าย:

for key, val in filter_dict(d, some_string):
    # do something

ในระยะสั้น: เครื่องกำเนิดไฟฟ้านั้นยอดเยี่ยม


11

คุณสามารถใช้ฟังก์ชันตัวกรองในตัวเพื่อกรองพจนานุกรมรายการและอื่น ๆ ตามเงื่อนไขเฉพาะ

filtered_dict = dict(filter(lambda item: filter_str in item[0], d.items()))

ข้อดีคือคุณสามารถใช้สำหรับโครงสร้างข้อมูลที่แตกต่างกัน


โปรดทราบว่าitems:ควรอยู่item:ในนิยามแลมบ์ดา
bkribbs

ขอบคุณ @bkribbs ที่ชี้ให้เห็นข้อผิดพลาด ตอนนี้ฉันได้แก้ไขแล้ว
Pulkit

8
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}

3
วิธีการใช้ของฉันเป็นไปได้ที่มีประสิทธิภาพมากกว่าiteritems() items()
Jonathon Reinhart

@Jonathin Reinhart ไม่รู้เรื่องเลย ขอบคุณ.
jspurim

2
บน Python 2.7 เท่านั้น ในหลาม 3 มีเพียง items()ซึ่งทำหน้าที่เหมือนงูหลาม iteritems2.7
Jonathon Reinhart

1
คำถามนี้ชัดเจนสำหรับ python 2.7
Brendan F

7

Jonathon ให้แนวทางแก่คุณโดยใช้การเข้าใจตามคำบอกในคำตอบของเขา นี่คือแนวทางที่เกี่ยวข้องกับการทำบางสิ่งบางอย่างของคุณ

หากคุณต้องการทำอะไรบางอย่างกับค่าของพจนานุกรมคุณไม่จำเป็นต้องมีความเข้าใจในพจนานุกรมเลย:

ฉันใช้iteritems() เนื่องจากคุณติดแท็กคำถามของคุณด้วย

results = map(some_function, [(k,v) for k,v in a_dict.iteritems() if 'foo' in k])

ตอนนี้ผลลัพธ์จะอยู่ในรายการที่some_functionใช้กับคู่คีย์ / ค่าแต่ละคู่ของพจนานุกรมที่มีfooอยู่ในคีย์

หากคุณแค่ต้องการจัดการกับค่าต่างๆและละเว้นคีย์เพียงแค่เปลี่ยนความเข้าใจของรายการ:

results = map(some_function, [v for k,v in a_dict.iteritems() if 'foo' in k])

some_function สามารถเรียกได้ดังนั้นแลมด้าก็ใช้ได้เช่นกัน:

results = map(lambda x: x*2, [v for k,v in a_dict.iteritems() if 'foo' in k])

ไม่จำเป็นต้องมีรายการด้านในเนื่องจากคุณสามารถส่งนิพจน์ตัวสร้างไปยังแมปได้เช่นกัน:

>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]

น่าสนใจ. จะกำหนด some_function อย่างไร ในกรณีแรก (k, v) ใช้พารามิเตอร์สองตัวหรือไม่? คีย์แรกแล้วค่า?
บันทึก

ใช่เพียงโทรได้ ดังนั้นmap(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))- [4]นี้จะทำให้คุณ
Burhan Khalid

สิ่งนี้ถูกต้อง แต่ pythonic มากกว่าการใช้ mapคือการเข้าใจรายการ [f(v) for k, v in d.iteritems() if substring in k]ฉันคิดว่ามันอ่านง่ายกว่ามากและมีประสิทธิภาพมากกว่า
Davidmh

@memo มันจะไม่ใช้สองพารามิเตอร์มันจะใช้พารามิเตอร์เดียวกับสององค์ประกอบ นอกจากนี้ยังมีstarmapซึ่งจะแยกออกเป็นสองอาร์กิวเมนต์อย่างไรก็ตามมันเป็นตัวทำซ้ำที่ขี้เกียจ (ต้องทำซ้ำก่อนที่จะดำเนินการเช่นresults = list(starmap(...))หรือfor result in starmap(...): ...)
nmclean
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.