วิธีค้นหารายการทูเปิลใน Python


91

ดังนั้นฉันจึงมีรายการสิ่งที่ดึงดูดเช่นนี้:

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

ฉันต้องการรายการนี้สำหรับทูเปิลที่มีค่าตัวเลขเท่ากับอะไรบางอย่าง

ดังนั้นถ้าฉันทำsearch(53)มันจะส่งกลับค่าดัชนีของ2

มีวิธีง่ายๆในการทำเช่นนี้หรือไม่?

คำตอบ:


96
[i for i, v in enumerate(L) if v[0] == 53]

17
อธิบายเป็นคำ: สำหรับแต่ละ i, v ในรายการที่แจกแจงของ L (ซึ่งทำให้ฉันเป็นตำแหน่งขององค์ประกอบในรายการที่แจกแจงและ v ของทูเปิลดั้งเดิม) ตรวจสอบว่าองค์ประกอบแรกของทูเปิลคือ 53 หรือไม่ถ้าเป็นเช่นนั้นให้ต่อท้ายผลลัพธ์ของโค้ด ก่อน "for" ไปยังรายการที่สร้างขึ้นใหม่ที่นี่: i. นอกจากนี้ยังอาจเป็น my_function (i, v) หรือความเข้าใจรายการอื่น เนื่องจากรายการทูเปิลของคุณมีเพียงทูเปิลเดียวที่มี 53 เป็นค่าแรกคุณจะได้รับรายการที่มีองค์ประกอบเดียว
djangonaut

6
ฉันแค่เพิ่ม [i สำหรับ i, v ในการแจงนับ (L) ถ้า v [0] == 53] .pop () มีค่า int
alemol

51

tl; dr

แสดงออกกำเนิดน่าจะเป็นทางออก performant และง่ายที่สุดในการแก้ไขปัญหาของคุณ:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

คำอธิบาย

มีคำตอบหลายประการที่ให้คำตอบง่ายๆสำหรับคำถามนี้พร้อมความเข้าใจในรายการ แม้ว่าคำตอบเหล่านี้จะถูกต้องสมบูรณ์ แต่ก็ไม่เหมาะสม ขึ้นอยู่กับกรณีการใช้งานของคุณอาจมีประโยชน์อย่างมากในการปรับเปลี่ยนง่ายๆบางอย่าง

ปัญหาหลักที่ฉันเห็นกับการใช้ความเข้าใจรายการสำหรับใช้กรณีนี้ก็คือรายชื่อทั้งหมดจะถูกประมวลผลถึงแม้ว่าคุณเพียงต้องการที่จะหา1 องค์ประกอบ

Python มีโครงสร้างง่ายๆซึ่งเหมาะสำหรับที่นี่ มันถูกเรียกว่าการแสดงออกของเครื่องกำเนิดไฟฟ้า นี่คือตัวอย่าง:

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

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

มาดูกันว่าวิธีการเหล่านี้ทำงานแตกต่างกันอย่างไรในชุดข้อมูลขนาดใหญ่บางชุด รายการเหล่านี้เป็นรายการขนาดใหญ่ที่สร้างจาก 10,000,000 + 1 องค์ประกอบโดยมีเป้าหมายของเราที่จุดเริ่มต้น (ดีที่สุด) หรือตอนท้าย (แย่ที่สุด) เราสามารถตรวจสอบได้ว่าทั้งสองรายการนี้จะทำงานอย่างเท่าเทียมกันโดยใช้ความเข้าใจของรายการต่อไปนี้:

แสดงรายการความเข้าใจ

"กรณีที่เลวร้ายที่สุด"

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

"กรณีที่ดีที่สุด"

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

นิพจน์ตัวสร้าง

นี่คือสมมติฐานของฉันสำหรับเครื่องกำเนิดไฟฟ้า: เราจะเห็นว่าเครื่องกำเนิดไฟฟ้าจะทำงานได้ดีขึ้นอย่างมากในกรณีที่ดีที่สุด แต่ในกรณีที่เลวร้ายที่สุด การเพิ่มประสิทธิภาพนี้ส่วนใหญ่เกิดจากการที่เครื่องกำเนิดไฟฟ้าได้รับการประเมินอย่างเฉื่อยชาซึ่งหมายความว่าจะคำนวณเฉพาะสิ่งที่จำเป็นเพื่อให้ได้ค่าเท่านั้น

กรณีที่เลวร้ายที่สุด

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}

กรณีที่ดีที่สุด

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}

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

ใช้ทั้งหมดนี้กับเกลือเม็ดฉันไม่ได้เรียกใช้การทำโปรไฟล์ที่แข็งแกร่งใด ๆ ที่นี่เป็นเพียงการทดสอบขั้นพื้นฐานบางอย่าง สิ่งนี้น่าจะเพียงพอที่จะชื่นชมว่านิพจน์ตัวสร้างมีประสิทธิภาพมากกว่าสำหรับการค้นหารายการประเภทนี้

โปรดทราบว่านี่เป็น python พื้นฐานในตัวทั้งหมด เราไม่จำเป็นต้องนำเข้าอะไรหรือใช้ไลบรารีใด ๆ

ครั้งแรกที่ฉันเห็นเทคนิคนี้ในการค้นหาในหลักสูตรUdacity cs212กับ Peter Norvig


2
น่าสนใจฉันทดสอบและพบว่ามันเร็วมาก
Grijesh Chauhan

3
นี่ควรเป็นคำตอบที่ได้รับการยอมรับ นิพจน์ตัวสร้างไม่ได้ทำให้ลำดับเอาต์พุตทั้งหมดเป็นจริงเมื่อรัน แต่จะประเมินเป็นตัววนซ้ำที่ให้ทีละรายการจากนิพจน์
BoltzmannBrain

2
นี่เป็นเรื่องที่ดีเร็วกว่าความเข้าใจรายการในกรณีของฉันมากขอบคุณ!
mindm49907


29

tuples ของคุณโดยพื้นฐานแล้วเป็นคู่คีย์ - ค่า - python dict--so:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

แก้ไข - aha คุณบอกว่าคุณต้องการค่าดัชนีเป็น (53, "xuxa") หากนี่คือสิ่งที่คุณต้องการจริงๆคุณจะต้องวนซ้ำตามรายการเดิมหรืออาจสร้างพจนานุกรมที่ซับซ้อนขึ้น:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

2
หากเราเพิกเฉยต่อสิ่งที่ OP ขอจริงฉันคิดว่าคำตอบเริ่มต้นของคุณเป็นคำตอบที่ดีที่สุดสำหรับ "วิธีค้นหารายการสิ่งที่มีใน Python"
Rick Westera

คำตอบแรกของคุณมีประโยชน์สำหรับวัตถุประสงค์ของฉัน อาจจะดีกว่าที่จะใช้. get () แม้ว่าในกรณีที่รายการไม่ได้อยู่ใน dict l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941

12

อืม ... วิธีง่ายๆที่ควรคำนึงถึงคือการแปลงเป็นคำสั่ง

d = dict(thelist)

d[53]และการเข้าถึง

แก้ไข : อ๊ะอ่านคำถามของคุณผิดในครั้งแรก ดูเหมือนว่าคุณต้องการรับดัชนีที่เก็บตัวเลขที่กำหนดไว้ ในกรณีนั้นให้ลอง

dict((t[0], i) for i, t in enumerate(thelist))

แทนที่จะเป็นการdictแปลงแบบเก่าธรรมดา จากนั้นd[53]จะเป็น 2.


6

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

ตัวอย่างเช่น:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])

# Get the index of 53:

index = sl.bisect((53,))

# With the index, get the tuple:

tup = sl[index]

วิธีนี้จะทำงานได้เร็วกว่าข้อเสนอแนะเพื่อความเข้าใจในรายการโดยทำการค้นหาแบบไบนารี คำแนะนำพจนานุกรมจะยังเร็วขึ้น แต่จะไม่ทำงานหากอาจมีตัวเลขที่ซ้ำกันซึ่งมีสตริงต่างกัน

หากมีตัวเลขที่ซ้ำกันกับสตริงที่แตกต่างกันคุณต้องดำเนินการอีกขั้นตอนหนึ่ง:

end = sl.bisect((53 + 1,))

results = sl[index:end]

โดยการแบ่งครึ่งเป็น 54 เราจะพบดัชนีสิ้นสุดของชิ้นส่วนของเรา สิ่งนี้จะเร็วกว่าอย่างมากในรายการยาวเมื่อเทียบกับคำตอบที่ยอมรับ



-2

[k สำหรับ k, v ใน l ถ้า v == ' delicia ']

นี่คือรายการทูเปิล - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]

และแทนที่จะแปลงเป็นคำสั่งเราใช้ llist comp understandion

*Key* in Key,Value in list, where value = **delicia**


แน่นอน. ขอบคุณ @cosmoonot.
Mantej Singh

นี่คือรายการสิ่งที่เพิ่มขึ้น - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")] และแทนที่จะแปลงเป็นคำสั่ง เรากำลังใช้ความเข้าใจ llist ` สำคัญ ในคีย์มูลค่าในรายการที่มีค่า = เดลิ `
Mantej ซิงห์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.