JavaScript المتقدم (ES6+)

Sets - مجموعات القيم الفريدة

13 دقيقة الدرس 21 من 40

Sets - مجموعات القيم الفريدة

قدمت ES6 كائن Set، وهو هيكل بيانات قوي لتخزين قيم فريدة من أي نوع. تضمن Sets تلقائياً أن كل قيمة تظهر مرة واحدة فقط، مما يجعلها مثالية لإزالة التكرارات وإجراء عمليات المجموعات الرياضية.

ما هي Set؟

Set هي مجموعة من القيم حيث يجب أن تكون كل قيمة فريدة. على عكس المصفوفات، Sets:

  • تزيل القيم المكررة تلقائياً
  • يمكن أن تحتوي على أي نوع من القيم (أولية أو كائنات)
  • تحافظ على ترتيب الإدراج
  • توفر طرقاً فعالة للتحقق من وجود القيمة
حقيقة مهمة: تستخدم Sets خوارزمية SameValueZero لفحوصات التساوي، والتي تعامل NaN على أنها مساوية لـ NaN (على عكس مقارنة ===).

إنشاء Sets

يمكنك إنشاء Sets بطرق متعددة:

// Set فارغة const emptySet = new Set(); // Set من مصفوفة const numbers = new Set([1, 2, 3, 4, 5]); // Set مع إزالة التكرارات تلقائياً const unique = new Set([1, 2, 2, 3, 3, 3, 4]); console.log(unique); // Set {1, 2, 3, 4} // Set من نص (كل حرف) const letters = new Set('hello'); console.log(letters); // Set {'h', 'e', 'l', 'o'} // Set بأنواع مختلطة const mixed = new Set([1, 'text', true, null, {id: 1}]);

طرق Set

توفر Sets عدة طرق لإضافة القيم والتحقق منها وإزالتها:

const fruits = new Set(); // add() - إضافة قيمة fruits.add('apple'); fruits.add('banana'); fruits.add('orange'); fruits.add('apple'); // مكررة، لن تُضاف console.log(fruits.size); // 3 // has() - التحقق من وجود القيمة console.log(fruits.has('apple')); // true console.log(fruits.has('grape')); // false // delete() - إزالة قيمة fruits.delete('banana'); console.log(fruits.has('banana')); // false // clear() - إزالة جميع القيم fruits.clear(); console.log(fruits.size); // 0
نصيحة: ترجع طريقة add() الـ Set نفسها، مما يسمح بتسلسل الطرق: set.add(1).add(2).add(3)

التكرار على Sets

Sets قابلة للتكرار ويمكن المرور عليها بطرق عديدة:

const colors = new Set(['red', 'green', 'blue']); // حلقة for...of for (const color of colors) { console.log(color); } // طريقة forEach colors.forEach((value, valueAgain, set) => { console.log(value); // ملاحظة: تظهر القيمة مرتين للتوافق مع Map }); // التحويل إلى مصفوفة const colorArray = [...colors]; const colorArray2 = Array.from(colors); // استخدام طرق المكرر console.log(colors.keys()); // SetIterator console.log(colors.values()); // SetIterator console.log(colors.entries()); // SetIterator من [value, value]

إزالة التكرارات من المصفوفات

أحد أكثر الاستخدامات شيوعاً لـ Sets هو إزالة القيم المكررة من المصفوفات:

// إزالة التكرارات const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 5]; const unique = [...new Set(numbers)]; console.log(unique); // [1, 2, 3, 4, 5] // إزالة النصوص المكررة const words = ['hello', 'world', 'hello', 'javascript']; const uniqueWords = [...new Set(words)]; console.log(uniqueWords); // ['hello', 'world', 'javascript'] // إزالة الكائنات المكررة (بالمرجع) const obj1 = {id: 1}; const obj2 = {id: 2}; const objects = [obj1, obj2, obj1, {id: 1}]; // الأخير مرجع مختلف const uniqueObjects = [...new Set(objects)]; console.log(uniqueObjects.length); // 3 (الأخير {id: 1} كائن مختلف)
مهم: تقارن Sets الكائنات بالمرجع، وليس بالقيمة. كائنان لهما خصائص متطابقة يُعتبران مختلفين ما لم يشيرا إلى نفس الكائن.

عمليات Set

Sets مثالية لعمليات المجموعات الرياضية مثل الاتحاد والتقاطع والفرق:

const setA = new Set([1, 2, 3, 4]); const setB = new Set([3, 4, 5, 6]); // الاتحاد - جميع القيم الفريدة من كلا المجموعتين const union = new Set([...setA, ...setB]); console.log(union); // Set {1, 2, 3, 4, 5, 6} // التقاطع - القيم الموجودة في كلا المجموعتين const intersection = new Set([...setA].filter(x => setB.has(x))); console.log(intersection); // Set {3, 4} // الفرق - القيم في setA ولكن ليس في setB const difference = new Set([...setA].filter(x => !setB.has(x))); console.log(difference); // Set {1, 2} // الفرق المتماثل - القيم في أي من المجموعتين ولكن ليس في كليهما const symmetricDiff = new Set([ ...[...setA].filter(x => !setB.has(x)), ...[...setB].filter(x => !setA.has(x)) ]); console.log(symmetricDiff); // Set {1, 2, 5, 6} // المجموعة الفرعية - التحقق من أن setA مجموعة فرعية من setB const isSubset = [...setA].every(x => setB.has(x)); console.log(isSubset); // false

أمثلة عملية

// المثال 1: تتبع الزوار الفريدين const uniqueVisitors = new Set(); function trackVisitor(userId) { uniqueVisitors.add(userId); console.log(`إجمالي الزوار الفريدين: ${uniqueVisitors.size}`); } trackVisitor('user1'); trackVisitor('user2'); trackVisitor('user1'); // مكرر، لن يزيد العدد // المثال 2: العثور على الوسوم الفريدة const articles = [ { tags: ['javascript', 'programming', 'web'] }, { tags: ['css', 'design', 'web'] }, { tags: ['javascript', 'es6', 'programming'] } ]; const allTags = new Set(); articles.forEach(article => { article.tags.forEach(tag => allTags.add(tag)); }); console.log([...allTags]); // ['javascript', 'programming', 'web', 'css', 'design', 'es6'] // المثال 3: إزالة التكرارات مع الحفاظ على الحالة function getUniqueCaseInsensitive(strings) { const seen = new Set(); return strings.filter(str => { const lower = str.toLowerCase(); if (seen.has(lower)) { return false; } seen.add(lower); return true; }); } const names = ['John', 'jane', 'JOHN', 'Jane', 'Bob']; console.log(getUniqueCaseInsensitive(names)); // ['John', 'jane', 'Bob']

خصائص الأداء

فهم أداء Set يساعدك على اختيار هيكل البيانات الصحيح:

تعقيد الوقت: - add(): O(1) - وقت ثابت - has(): O(1) - وقت ثابت - delete(): O(1) - وقت ثابت - clear(): O(n) - وقت خطي - Size: O(1) - وقت ثابت المقارنة مع المصفوفات: - التحقق من وجود القيمة: Set.has() هو O(1) مقابل Array.includes() هو O(n) - إضافة قيم فريدة: Set.add() هو O(1) مقابل Array.push() مع فحص التكرار هو O(n) - إزالة القيم: Set.delete() هو O(1) مقابل Array.splice() هو O(n)
أفضل ممارسة: استخدم Sets عندما تحتاج إلى الحفاظ على قيم فريدة أو التحقق بشكل متكرر من وجود القيمة. استخدم المصفوفات عندما تحتاج إلى الوصول المفهرس أو القيم المكررة.

مقارنة Set مع Array

// متى تستخدم Set const uniqueIds = new Set([1, 2, 3]); // بحاجة لقيم فريدة uniqueIds.has(2); // بحث سريع - O(1) uniqueIds.add(4); // إدراج سريع - O(1) // متى تستخدم Array const scores = [95, 88, 95, 92]; // التكرارات لها معنى scores[0]; // بحاجة للوصول المفهرس scores.map(x => x * 1.1); // بحاجة لطرق المصفوفة

تمرين عملي:

التحدي: أنشئ دالة تجد الأصدقاء المشتركين بين مستخدمين.

function findCommonFriends(user1Friends, user2Friends) { const set1 = new Set(user1Friends); const commonFriends = user2Friends.filter(friend => set1.has(friend)); return commonFriends; } const alice = ['Bob', 'Charlie', 'David', 'Eve']; const john = ['Charlie', 'Eve', 'Frank', 'Grace']; console.log(findCommonFriends(alice, john)); // الناتج: ['Charlie', 'Eve']

جربه بنفسك: قم بتوسيعه للعثور على الأصدقاء الفريدين لكل مستخدم (الفرق المتماثل).

الملخص

في هذا الدرس، تعلمت:

  • Sets تخزن قيماً فريدة وتزيل التكرارات تلقائياً
  • طرق Set: add() و has() و delete() و clear() وخاصية size
  • يمكن التكرار على Sets باستخدام for...of و forEach ومشغل النشر
  • Sets مثالية لإزالة تكرارات المصفوفات والعمليات الرياضية
  • عمليات Set: الاتحاد والتقاطع والفرق وفحوصات المجموعة الفرعية
  • توفر Sets أداء O(1) لعمليات add و has و delete
  • اختر Sets للقيم الفريدة والبحث السريع، والمصفوفات للوصول المفهرس
التالي: في الدرس التالي، سنستكشف Maps - أزواج مفتاح-قيمة بأي نوع من المفاتيح!