إذا كانت لديك List تحتوي على قواميس Dictionaries، فالبحث لا يكون مثل البحث عن قيمة بسيطة داخل قائمة نصوص أو أرقام. هنا تحتاج غالبًا إلى فحص مفتاح محدد داخل كل قاموس، مثل id أو name، ثم تحديد هل تريد أول سجل مطابق، أو كل السجلات المطابقة، أو مجرد معرفة هل يوجد تطابق.
في هذا الدرس سنركز فقط على البحث داخل List of Dictionaries في Python: البحث حسب id أو name، واستخدام for وnext() وany() وList Comprehension، مع التعامل مع البحث غير الحساس لحالة الأحرف والأخطاء الشائعة عند غياب المفاتيح.
{alertInfo} الخلاصة السريعة: استخدمnext(..., None)عندما تريد أول قاموس مطابق، وList Comprehension عندما تريد كل النتائج، وany()عندما تريد فقط معرفة هل يوجد سجل يطابق الشرط.
{getToc} $title={محتوى المقال}
ما المقصود بـ List of Dictionaries؟
هي قائمة تكون عناصرها قواميس، بحيث يمثل كل قاموس سجلًا مثل طالب أو منتج أو مستخدم:
students = [
{"id": 1, "name": "Ali", "grade": 90},
{"id": 2, "name": "Sara", "grade": 85},
{"id": 3, "name": "Omar", "grade": 70}
]
إذا كان هدفك مجرد معرفة هل قيمة بسيطة موجودة داخل List عادية، فذلك هو موضوع المقال المرجعي: البحث عن عنصر داخل List في Python. هنا سنبحث داخل مفاتيح القواميس الموجودة داخل القائمة.
لماذا لا يكفي استخدام in مباشرة؟
التعبير التالي لا يبحث عن الاسم داخل قيمة المفتاح name:
print("Sara" in students)
السبب أن عناصر students هي قواميس كاملة، وليست النصوص "Ali" و"Sara" نفسها. لذلك يجب أن تحدد المفتاح الذي تريد فحصه.
البحث داخل List of Dictionaries حسب id باستخدام for
الطريقة المباشرة والأوضح للمبتدئ هي المرور على كل قاموس وفحص قيمة id:
target_id = 2
for student in students:
if student["id"] == target_id:
print(student)
break
الناتج:
{'id': 2, 'name': 'Sara', 'grade': 85}
نستخدم break لأن id يفترض غالبًا أن يحدد سجلًا واحدًا فقط. بمجرد العثور عليه لا نحتاج إلى متابعة الحلقة.
البحث حسب name داخل قائمة قواميس
نفس الفكرة تعمل مع الاسم:
target_name = "Omar"
for student in students:
if student["name"] == target_name:
print(student)
break
لكن الأسماء قد تتكرر، لذلك يجب أن تقرر من البداية: هل تريد أول تطابق فقط أم كل السجلات المطابقة؟
الحصول على أول قاموس مطابق باستخدام next()
إذا أردت أول نتيجة فقط، يمكن استخدام next() مع generator expression:
target_id = 2
student = next(
(item for item in students if item["id"] == target_id),
None
)
print(student)
الناتج:
{'id': 2, 'name': 'Sara', 'grade': 85}
القيمة الثانية في next() وهي None مهمة؛ فهي تمنع ظهور StopIteration إذا لم يوجد أي عنصر مطابق.
إذا لم توجد النتيجة
student = next(
(item for item in students if item["id"] == 99),
None
)
if student is None:
print("الطالب غير موجود")
البحث عن كل القواميس المطابقة
إذا كانت النتيجة قد تتكرر، استخدم List Comprehension لإرجاع قائمة بكل السجلات المطابقة. مثلًا، للحصول على كل الطلاب الذين درجتهم 70 أو أكثر:
matched_students = [
student
for student in students
if student["grade"] >= 70
]
print(matched_students)
هذه الطريقة تختلف عن next(): الأخيرة تعيد أول تطابق فقط، أما List Comprehension فتبني قائمة بجميع النتائج.
استخدام any() لمعرفة هل يوجد سجل يحقق الشرط
أحيانًا لا تحتاج إلى السجل نفسه. تريد فقط إجابة True أو False. هنا يكون any() مناسبًا:
has_student = any(
student["id"] == 2
for student in students
)
print(has_student)
الناتج:
True
استخدم any() عندما يهمك وجود تطابق من عدمه، ولا تستخدمه إذا كنت تحتاج بيانات القاموس المطابق.
أول نتيجة مقابل كل النتائج مقابل مجرد التحقق
| الهدف | الطريقة المناسبة | نوع النتيجة |
|---|---|---|
| أول سجل مطابق | next(..., None) |
قاموس واحد أو None |
| كل السجلات المطابقة | List Comprehension | List جديدة |
| هل يوجد أي تطابق؟ | any() |
True أو False |
| منطق مخصص أثناء البحث | for |
حسب الكود الذي تكتبه |
البحث غير الحساس لحالة الأحرف حسب name
إذا كانت الأسماء الإنجليزية قد تأتي بأشكال مثل "Omar" و"OMAR" و"omar"، فطبّع الطرفين قبل المقارنة. يمكن استخدام casefold():
target_name = "omar"
student = next(
(
item
for item in students
if item["name"].casefold() == target_name.casefold()
),
None
)
print(student)
هذا أفضل من تحويل البيانات نفسها داخل القائمة؛ المقارنة تصبح غير حساسة لحالة الأحرف دون تغيير القيم الأصلية.
ماذا لو كان المفتاح غير موجود داخل بعض القواميس؟
استخدام item["name"] يفترض أن المفتاح موجود في كل سجل. إذا كانت البيانات غير متجانسة فقد يظهر KeyError.
عندما يكون غياب المفتاح احتمالًا طبيعيًا، يمكنك التحقق منه أولًا:
matched = [
item
for item in students
if "name" in item
and item["name"] == "Sara"
]
يمكن استخدام dict.get() أيضًا عندما تكون القيمة الافتراضية منطقية، لكن لا تستخدم قيمة افتراضية تخفي بيانات ناقصة يجب إصلاحها. للمزيد راجع شرح Dictionary في Python.
دالة عامة للبحث حسب مفتاح وقيمة
إذا كنت تبحث كثيرًا حسب مفاتيح مختلفة، يمكن كتابة دالة تعيد أول نتيجة:
def find_first_by_key(items, key, value):
return next(
(
item
for item in items
if key in item and item[key] == value
),
None
)
student = find_first_by_key(students, "id", 3)
print(student)
ولإرجاع كل النتائج:
def find_all_by_key(items, key, value):
return [
item
for item in items
if key in item and item[key] == value
]
مثال منتجات: البحث حسب id ثم الاسم
products = [
{"id": 101, "name": "Book", "price": 5},
{"id": 102, "name": "Pen", "price": 2},
{"id": 103, "name": "Bag", "price": 20}
]
product = next(
(item for item in products if item["id"] == 102),
None
)
if product is not None:
print(product["name"], product["price"])
إذا كنت تريد فلترة عدة منتجات بحسب السعر أو أي شرط آخر، فذلك يرتبط مباشرة بموضوع فلترة عناصر List في Python.
أخطاء شائعة عند البحث داخل List of Dictionaries
1. استخدام in وكأن القائمة تحتوي القيم مباشرة
"Ali" in students لا يبحث تلقائيًا داخل student["name"]. حدد المفتاح الذي تريد فحصه.
2. افتراض أن id نص وهو رقم
"2" لا يساوي 2. افحص نوع القيمة القادمة من المستخدم أو الملف قبل المقارنة.
3. استخدام next() بدون قيمة افتراضية
إذا لم يوجد تطابق، فإن next(generator) بدون default قد يرفع StopIteration. عند البحث الذي قد يفشل طبيعيًا استخدم next(generator, None).
4. استخدام any() ثم إعادة البحث مرة ثانية بلا حاجة
إذا كنت تحتاج السجل نفسه، لا تنفذ any() أولًا ثم تبحث مرة ثانية. استخدم next() مباشرة، ثم افحص هل النتيجة None.
5. تجاهل المفاتيح المفقودة
إذا لم تكن بنية البيانات مضمونة، افحص وجود المفتاح أو تعامل مع غيابه بوضوح بدل افتراض أن كل قاموس متطابق.
متى يكون Dictionary أفضل من List للبحث حسب id؟
إذا كنت تنفذ بحثًا متكررًا جدًا حسب معرف فريد، فقد يكون تخزين البيانات من البداية في قاموس مفهرس بالـid أبسط وأسرع من المرور على List في كل مرة:
students_by_id = {
1: {"name": "Ali"},
2: {"name": "Sara"},
3: {"name": "Omar"}
}
print(students_by_id[2])
أما عندما تكون البيانات موجودة أصلًا كـList of Dictionaries أو تحتاج إلى الحفاظ على ترتيب السجلات، فطرق البحث السابقة مناسبة.
خطوات Debugging عندما لا تجد النتيجة
- اطبع
targetوتأكد من نوعه باستخدامtype(). - اطبع أحد القواميس وتحقق من اسم المفتاح الحقيقي:
idأمIDأم اسم آخر. - تأكد أن قيمة المفتاح ونوع الهدف متطابقان، مثل
2مقابل"2". - عند البحث في أسماء إنجليزية، جرّب المقارنة بعد
casefold(). - إذا كانت بعض القواميس لا تحتوي المفتاح، افحصه قبل الوصول إليه.
- حدد هل تريد أول نتيجة أم جميع النتائج؛ اختيار الأداة الخاطئة قد يعطي نتيجة صحيحة لكنها غير مكتملة.
{alertSuccess} قاعدة عملية: لا تبدأ بالسؤال «كيف أبحث في List؟». اسأل أولًا: ما المفتاح الذي أبحث فيه؟ هل أريد أول تطابق، كل التطابقات، أم مجرد معرفة وجود نتيجة؟ اختيار الإجابة يحدد مباشرة هل تستخدمnext()أو List Comprehension أوany().
مصادر رسمية للتوسع
- توثيق Python الرسمي للدالة next
- توثيق Python الرسمي للدالة any
- Data Structures — Python Documentation
الخلاصة
عند البحث داخل List of Dictionaries، لا تبحث عن القيمة كما لو كانت عنصرًا مباشرًا في القائمة؛ ابحث داخل المفتاح المناسب في كل قاموس. استخدم for عندما تحتاج منطقًا واضحًا، وnext(..., None) لأول سجل مطابق، وList Comprehension لكل النتائج، وany() عندما تريد فقط معرفة هل يوجد تطابق.
إذا كان البحث حسب id يتكرر كثيرًا جدًا، فكر في بنية Dictionary مفهرسة بالمعرف. أما في القوائم الصغيرة والمتوسطة أو البيانات المرتبة، فالبحث داخل List of Dictionaries يبقى حلًا عمليًا وواضحًا.
أسئلة شائعة
كيف أبحث داخل List of Dictionaries في Python حسب id؟
مرّ على القواميس وافحص item["id"]، أو استخدم next((item for item in items if item["id"] == target_id), None) إذا كنت تريد أول تطابق فقط.
كيف أبحث حسب name بدون حساسية لحالة الأحرف؟
قارن الاسم والهدف بعد استخدام casefold() على الطرفين، ثم أعد السجل المطابق باستخدام for أو next().
كيف أرجع كل القواميس المطابقة؟
استخدم List Comprehension مثل [item for item in items if item["grade"] >= 70] لتكوين قائمة بكل النتائج.
متى أستخدم next() ومتى أستخدم any()؟
استخدم next() عندما تحتاج السجل نفسه، واستخدم any() عندما تحتاج فقط إلى معرفة هل يوجد سجل يحقق الشرط.
ماذا يحدث إذا لم يجد next() أي نتيجة؟
إذا كتبت next(generator, None) فستحصل على None. أما بدون قيمة افتراضية فقد يظهر StopIteration.



