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

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

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

นั่นคือตรรกะ C จะเป็น:

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

ฉันจินตนาการว่าเวอร์ชันหลามจะเป็นเช่นนั้น

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

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

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


person memo    schedule 26.05.2014    source แหล่งที่มา


คำตอบ (5)


ความเข้าใจตามคำบอกเป็นอย่างไร:

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}
person Jonathon Reinhart    schedule 26.05.2014
comment
ทำไมไม่ filtered_dict = {k:d[k] for k in d if filter_string in k}? - person thefourtheye; 26.05.2014
comment
@thefourtheye ฉันจะเดาว่าของฉันเร็วกว่า เนื่องจากไม่อยู่ในการค้นหา d[k] - person Jonathon Reinhart; 26.05.2014
comment
นอกจากนี้ เขายังระบุ # do something ในความคิดเห็นด้วย แต่เราทิ้งคีย์ไว้บางส่วนที่นี่ - person thefourtheye; 26.05.2014
comment
เรามี iteritems ใน Python 3 หรือไม่ ฉันไม่คิดอย่างนั้น เวอร์ชั่นของฉันจะเข้ากันได้ใช่ไหม? - person thefourtheye; 26.05.2014
comment
ใน Python 3 คุณจะต้องแทนที่ iteritems ด้วย items ซึ่งเหมือนกับ iteritems ของ Python 2.7 - person Jonathon Reinhart; 26.05.2014

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

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

อย่างไรก็ตาม หากคุณต้องการบางสิ่งที่ให้คุณวนซ้ำผ่าน dict ที่กรองแล้ว ฉันจะไม่ทำกระบวนการสองขั้นตอนในการสร้าง dict ที่กรองแล้ววนซ้ำผ่านมัน แต่ใช้ตัวสร้างแทน เพราะอะไรคือ 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

กล่าวโดยย่อ: เครื่องกำเนิดไฟฟ้านั้นยอดเยี่ยม

person Brendan F    schedule 26.05.2014

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

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

ข้อดีคือคุณสามารถใช้กับโครงสร้างข้อมูลต่างๆ ได้

person Pulkit    schedule 19.03.2018
comment
โปรดทราบว่า items: ควรเป็น item: ในคำจำกัดความแลมบ์ดา - person bkribbs; 01.09.2018
comment
ขอบคุณ @bkribbs สำหรับการชี้ให้เห็นข้อผิดพลาด ฉันได้แก้ไขมันแล้ว. - person Pulkit; 03.09.2018

Jonathon ให้แนวทางแก่คุณโดยใช้ dict comprehension ในคำตอบของเขา ต่อไปนี้เป็นแนวทางที่เกี่ยวข้องกับส่วน ทำบางสิ่งบางอย่าง ของคุณ

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

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

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 สามารถเรียกได้ทุกประเภท ดังนั้น lambda ก็ใช้ได้เช่นกัน:

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]
person Burhan Khalid    schedule 26.05.2014
comment
น่าสนใจ. some_function จะถูกกำหนดอย่างไร? ในกรณีแรก (k,v) มันต้องใช้พารามิเตอร์เพียงสองตัวหรือไม่? คีย์แรกแล้วค่า? - person memo; 26.05.2014
comment
ใช่ แค่โทรมาได้ ดังนั้น map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2)) - นี่จะทำให้คุณ [4] - person Burhan Khalid; 26.05.2014
comment
สิ่งนี้ถูกต้อง แต่ pythonic มากกว่าการใช้ map ถือเป็นรายการความเข้าใจ [f(v) for k, v in d.iteritems() if substring in k] ฉันคิดว่าอ่านง่ายกว่าและมีประสิทธิภาพมากกว่ามาก - person Davidmh; 26.05.2014
comment
@memo จะไม่ใช้สองพารามิเตอร์ แต่จะใช้พารามิเตอร์ตัวเดียวที่มีสององค์ประกอบ นอกจากนี้ยังมี starmap ซึ่งจะแยกออกเป็นสองอาร์กิวเมนต์ มันเป็นตัววนซ้ำแบบขี้เกียจ (ต้องวนซ้ำก่อนที่จะดำเนินการ เช่น results = list(starmap(...)) หรือ for result in starmap(...): ...) - person nmclean; 26.05.2014

person    schedule
comment
วิธีการของฉันที่ใช้ iteritems() จะมีประสิทธิภาพมากกว่า items() - person Jonathon Reinhart; 26.05.2014
comment
@Jonathin Reinhart ฉันไม่รู้เกี่ยวกับเรื่องนี้ ขอบคุณ. - person jspurim; 26.05.2014
comment
บน Python 2.7 เท่านั้น ใน Python 3 จะมี เท่านั้น items() ซึ่งทำหน้าที่เหมือนกับ iteritems ของ Python 2.7 - person Jonathon Reinhart; 26.05.2014
comment
คำถามนี้มีความชัดเจนสำหรับ python 2.7 - person Brendan F; 26.05.2014