لحذف العناصر المكررة من قائمة في بايثون، يمكنك استخدام set() إذا لم يكن ترتيب العناصر مهمًا، أو dict.fromkeys() عندما تريد الاحتفاظ بترتيب أول ظهور لكل عنصر. وهناك طريقة تعليمية واضحة باستخدام حلقة for وشرط not in، وتناسب أيضًا بعض القوائم التي تحتوي على عناصر لا تقبلها المجموعات والقواميس كمفاتيح.
في هذا الدرس الأول من سلسلة بايثون بالمثال سنطبّق الطرق الثلاث على قائمة أسماء، ونقارن بينها، ثم نستخدم الفكرة مع الأرقام والبريد الإلكتروني. سنعرف كذلك لماذا لا يضمن list(set(...)) استعادة الترتيب الأصلي، وكيف تتجنب الخطأ عند وجود قوائم متداخلة.
{alertInfo} الإجابة السريعة: للقيم النصية والرقمية، استخدمlist(dict.fromkeys(items))إذا أردت قائمة بلا تكرار مع الحفاظ على الترتيب، أوset(items)إذا لم تكن بحاجة إلى الترتيب أو إلى ناتج من نوع List.
{getToc} $title={محتوى المقال}
مثال المشكلة: قائمة أسماء تحتوي على تكرار
لنفترض أن لديك قائمة طلاب تكرر فيها اسما Ahmed وSara، وتريد ظهور كل اسم مرة واحدة فقط، بحسب ترتيب أول ظهور له:
students = ["Ahmed", "Sara", "Omar", "Ahmed", "Sara"]
النتيجة المطلوبة:
['Ahmed', 'Sara', 'Omar']
تعرّف إلى الفرق بين الطرق التالية قبل اختيار إحداها. وإذا لم تدرس القوائم بعد، يمكنك البدء من كورس أساسيات بايثون.
ثلاث طرق لإزالة التكرار من List
| الطريقة | هل تحافظ على الترتيب؟ | نوع الناتج | ملاحظة |
|---|---|---|---|
set(items) | لا | set | تتطلب عناصر قابلة للتجزئة Hashable. |
حلقة for مع not in | نعم | list | واضحة للتعلّم وتتعامل أيضًا مع القوائم المتداخلة. |
list(dict.fromkeys(items)) | نعم، في بايثون 3.7 والأحدث | list | مختصرة، وتتطلب عناصر صالحة كمفاتيح للقاموس. |
الطريقة الأولى: حذف التكرار باستخدام set()
المجموعة set تحتفظ بالعناصر الفريدة. هذه طريقة مناسبة عندما لا تحتاج إلى ترتيب أول ظهور الأسماء:
students = ["Ahmed", "Sara", "Omar", "Ahmed", "Sara"]
unique_students = set(students)
print(unique_students)
قد يظهر الناتج بالشكل الآتي، لكن ترتيب الأسماء قد يختلف:
{'Sara', 'Ahmed', 'Omar'}
إذا كنت تريد أن يكون نوع الناتج قائمة دون اشتراط ترتيبها، حوّل المجموعة مرة أخرى إلى list:
students = ["Ahmed", "Sara", "Omar", "Ahmed", "Sara"]
unique_students = list(set(students))
print(unique_students)
{alertWarning} تحويل المجموعة إلى قائمة باستخدام list(set(students)) لا يعيد ترتيب القائمة الأصلية. لا تعتمد على ترتيب المجموعات حتى لو بدا ثابتًا في تجربة واحدة.
يمكنك التوسع في خصائص المجموعات من خلال شرح Set في بايثون.
الطريقة الثانية: استخدام for للحفاظ على الترتيب
إذا أردت فهم الحل خطوة بخطوة، أنشئ قائمة فارغة، ثم مرّ على العناصر الأصلية، وأضف العنصر فقط عندما لا يكون موجودًا في القائمة الجديدة:
students = ["Ahmed", "Sara", "Omar", "Ahmed", "Sara"]
unique_students = []
for student in students:
if student not in unique_students:
unique_students.append(student)
print(unique_students)
الناتج:
['Ahmed', 'Sara', 'Omar']
كيف تعمل الحلقة؟
- نبدأ بالقائمة الفارغة
unique_students. - نقرأ الأسماء بالترتيب باستخدام
for. - نفحص كل اسم بواسطة
not in: هل أُضيف سابقًا؟ - إذا لم يكن موجودًا، نضيفه باستخدام
append(). أما الاسم المكرر فنتجاوزه.
النتيجة قائمة جديدة تحافظ على ترتيب أول ظهور، دون تعديل القائمة الأصلية. هذه الطريقة مفهومة للمبتدئ، لكن البحث المتكرر داخل القائمة الجديدة قد يجعلها أبطأ عند التعامل مع قوائم كبيرة.
الطريقة الثالثة: إزالة التكرار باستخدام dict.fromkeys()
عندما تكون عناصر القائمة نصوصًا أو أعدادًا أو قيمًا أخرى صالحة كمفاتيح للقاموس، تستطيع إزالة التكرار والحفاظ على الترتيب في سطر واحد. فمنذ بايثون 3.7، تضمن القواميس الحفاظ على ترتيب إدخال المفاتيح.
students = ["Ahmed", "Sara", "Omar", "Ahmed", "Sara"]
unique_students = list(dict.fromkeys(students))
print(unique_students)
الناتج:
['Ahmed', 'Sara', 'Omar']
تستخدم dict.fromkeys(students) الأسماء كمفاتيح، فلا تكرر الاسم إذا ظهر مرة ثانية، ثم يحوّل list() تلك المفاتيح إلى قائمة. هذه الطريقة لا تقبل العناصر غير القابلة للتجزئة مثل القوائم المتداخلة.
إذا أردت فهم سبب اختلاف دور List وSet وDictionary، اقرأ الفرق بين List وTuple وSet وDictionary. هذا الدرس الحالي يركّز على تطبيق إزالة التكرار، لا إعادة شرح جميع هياكل البيانات.
مثال عملي: إزالة الأرقام المكررة مع الحفاظ على ترتيبها
تنطبق الفكرة نفسها على قائمة أرقام نريد فيها الاحتفاظ بأول ظهور لكل قيمة:
numbers = [5, 2, 5, 8, 2, 9]
unique_numbers = list(dict.fromkeys(numbers))
print(unique_numbers)
الناتج:
[5, 2, 8, 9]
مثال واقعي: حذف عناوين البريد الإلكتروني المتكررة
عند تجميع عناوين البريد الإلكتروني، قد يظهر العنوان نفسه أكثر من مرة. إذا كانت العناوين مكتوبة بالطريقة نفسها تمامًا، يمكن استخدام الطريقة المختصرة:
emails = [
"ali@example.com",
"sara@example.com",
"ali@example.com",
"omar@example.com"
]
unique_emails = list(dict.fromkeys(emails))
print(unique_emails)
الناتج:
['ali@example.com', 'sara@example.com', 'omar@example.com']
هذا المثال يزيل التكرار النصي المتطابق فقط. لا يفحص ملكية البريد الإلكتروني، ولا يوحّد الاختلافات في الكتابة أو يعالج سياسة أحرف البريد الكبيرة والصغيرة.
ماذا لو احتوت القائمة على قوائم متداخلة؟
لا تعمل set(items) أو dict.fromkeys(items) مباشرة إذا كانت العناصر نفسها قوائم؛ لأن القوائم غير قابلة للتجزئة ولا يمكن استخدامها كعناصر Set أو كمفاتيح Dictionary. في هذا المثال البسيط، استخدم الحلقة والمقارنة:
records = [[1, 2], [3, 4], [1, 2]]
unique_records = []
for record in records:
if record not in unique_records:
unique_records.append(record)
print(unique_records)
الناتج:
[[1, 2], [3, 4]]
{alertInfo} هذه الطريقة تعتمد على المقارنة بين العناصر. وعند التعامل مع بيانات معقدة جدًا أو كبيرة الحجم، قد تحتاج إلى أسلوب مختلف يناسب شكل البيانات.
أخطاء شائعة عند حذف العناصر المكررة
- الاعتقاد أن
list(set(items))تحفظ الترتيب: لا يوجد هذا الضمان؛ استخدمdict.fromkeys()للعناصر القابلة للتجزئة. - استخدام Set أو Dictionary مع عناصر من نوع List: قد تحصل على
TypeError: unhashable type: 'list'؛ جرّب الحلقة كما في المثال السابق. - الاعتقاد أن الطريقتين تعدّلان القائمة الأصلية: جميع الأمثلة هنا تُنشئ ناتجًا جديدًا، ولا تحذف العناصر من القائمة الأصلية مباشرة.
- الاعتماد على تنسيق طباعة Set: عرض عناصر المجموعة لا يعبّر عن ترتيب إدخال موثوق.
تمرين: احذف التكرار من قائمة لغات
جرّب هذه القائمة في محرر بايثون العرب، واكتب الحل أولًا باستخدام for، ثم اختصره باستخدام dict.fromkeys():
languages = ["Python", "Java", "Python", "C++", "Java"]
# أكمل الحل باستخدام dict.fromkeys()
unique_languages = list(dict.fromkeys(languages))
print(unique_languages)
الناتج المتوقع:
['Python', 'Java', 'C++']
بعد تجربة هذه الفكرة، انتقل إلى بايثون بالمثال 2: معرفة العدد الزوجي والفردي، أو تصفح سلسلة بايثون بالمثال.
أسئلة شائعة
كيف أحذف العناصر المكررة من List في بايثون؟
استخدم set() إذا لم يكن الترتيب مهمًا. وإذا أردت قائمة تحافظ على ترتيب أول ظهور، استخدم list(dict.fromkeys(items)) للعناصر المناسبة، أو حلقة for.
هل set() تحافظ على ترتيب القائمة؟
لا. تتعامل Set مع العناصر الفريدة، لكنها لا تسجل ترتيب إدخالها ولا تدعم الفهرسة مثل List.
هل dict.fromkeys() تزيل التكرار دون تغيير الترتيب؟
نعم، عند استخدام بايثون 3.7 أو أحدث ومع عناصر تصلح كمفاتيح للقاموس، ستبقى العناصر حسب ترتيب ظهورها الأول.
هل تعمل الطرق الثلاث مع أي نوع من العناصر؟
لا. تحتاج Set وDictionary إلى عناصر قابلة للتجزئة، مثل النصوص والأعداد. أما الحلقة المعروضة فتتعامل أيضًا مع القوائم المتداخلة البسيطة عن طريق المقارنة.
الخلاصة
تعلمت في هذا المثال ثلاث طرق لإزالة التكرار من قائمة في بايثون: set() للحصول على القيم الفريدة عندما لا يهم الترتيب، وحلقة for للشرح التدريجي والحفاظ على الترتيب، وdict.fromkeys() للحصول على قائمة مرتبة ومختصرة عندما تكون العناصر مناسبة.
{alertSuccess} القاعدة العملية: إذا كان ترتيب القائمة مهمًا والعناصر قابلة للتجزئة، فاستخدم list(dict.fromkeys(items)). وإذا كانت العناصر قوائم متداخلة بسيطة، فاستخدم الحلقة مع المقارنة.


