كائن `Set`
تشرح هذه المقالة كائن الـSet۔
سنشرح كائن الـSet بأمثلة عملية۔
YouTube Video
كائن Set
Set هو كائن مدمج يُستخدم لمعالجة مجموعات من القيم الفريدة غير المكررة۔ يتيح لك التخلص من التكرارات والتحقق من الوجود بشكل أبسط من المصفوفات، ويجعل عمليات المجموعات مثل الاتحاد والتقاطع أسهل في التنفيذ۔
الأساسيات: إنشاء واستخدام الـSet
أولاً، دعونا نرى كيفية إنشاء كائن Set، وإضافة وحذف العناصر، والتحقق من وجودها، ومعرفة حجمه۔
فيما يلي نمط أساسي لإنشاء كائن Set جديد ويوضح طرق add وhas وdelete وsize۔
1// Create a Set and demonstrate add, has, delete, and size
2const s = new Set();
3
4s.add(1);
5s.add(2);
6s.add(2); // duplicate, ignored
7
8console.log(s.has(1)); // true
9console.log(s.has(3)); // false
10
11s.delete(2);
12console.log(s.size); // 1
13
14console.log([...s]); // [1]
- كما هو موضح في هذا الكود، يقوم
Setتلقائيًا بإزالة القيم المكررة من الأنواع البدائية، ويمكنك الحصول على عدد العناصر باستخدامsize۔
طرق التكرار على العناصر
Set قابل للتكرار، لذا يمكنك التنقل خلاله باستخدام for...of أو forEach۔ الترتيب هو ترتيـب الإدخال۔
فيما يلي طرق شائعة لاستخدام for...of و forEach۔
1// Iterate a Set with for...of and forEach
2const s = new Set(['a', 'b', 'c']);
3
4for (const v of s) {
5 console.log('for...of:', v);
6}
7
8s.forEach((value, sameValue, setRef) => {
9 // Note: second arg is same as first for Set API to match Map signature
10 console.log('forEach:', value);
11});- دالة الاستدعاء العكسي لـ
forEachهي:value, value, set(من أجل التوافق مع Map)، ولكن في الواقع غالباً ما تحتاج فقط إلى قيمة العنصر الأول۔
التحويل بين المصفوفات وSet (مفيد لإزالة التكرارات)
هنا نوضح تقنية بسيطة لإزالة التكرارات من مصفوفة، وكيفية تحويل كائن Set مرة أخرى إلى مصفوفة۔
فيما يلي مثال على إزالة التكرارات من مصفوفة عن طريق تمريرها عبر كائن Set۔
1// Deduplicate an array using Set
2const arr = [1, 2, 2, 3, 3, 3];
3const deduped = [...new Set(arr)];
4console.log(deduped); // [1, 2, 3]
5
6// Convert a Set to an array using Array.from
7const s = new Set([4, 5, 6]);
8const arrFromSet = Array.from(s);
9console.log(arrFromSet); // [4, 5, 6]
- هذا النمط قصير وسريع، ولهذا كثيراً ما يُستخدم لإزالة التكرارات من المصفوفات۔ ويكون فعالاً بشكل خاص مع القيم الأولية۔
الكائنات والتعامل مع المراجع
يتم مقارنة الكائنات في الـSet بناءً على المرجع، لذا فإن النسخ المختلفة التي تحتوي نفس المحتوى تعتبر عناصر منفصلة۔
يوضح الكود التالي ما يحدث عند إضافة كائنات إلى الـSet۔
1// Objects are compared by reference in a Set
2const obj1 = { x: 1 };
3const obj2 = { x: 1 };
4
5const s = new Set();
6s.add(obj1);
7s.add(obj2);
8
9console.log(s.size); // 2 (different references)
10console.log(s.has(obj1)); // true
11console.log(s.has({ x: 1 })); // false (different object)
- اكتشاف التكرار للكائنات يعتمد على هوية المرجع، لذلك إذا أردت إزالة التكرار بناءً على محتوى الكائن فقط، ستحتاج إلى تسلسلها أو معالجتها بطريقة أخرى۔
القيم الخاصة: التعامل مع NaN و-0 / +0
يستخدم Set قاعدة المقارنة Same-value-zero لتحديد تساوي القيم۔ طريقة المقارنة هذه لها المزايا التالية فيما يخص الأرقام:۔
- يعتبر
NaNمساويًا لـNaN. - لا يتم التفريق بين
+0الموجب و-0السالب ويُعتبران نفس القيمة.
لذلك عند إضافة هذه القيم إلى كائن Set يحدث السلوك التالي:۔
1// NaN and zero behavior in Set
2const s = new Set();
3
4s.add(NaN);
5s.add(NaN);
6console.log(s.size); // 1 (NaN considered the same)
7
8s.add(+0);
9s.add(-0);
10console.log(s.size); // still 2 (NaN + 0)
11console.log([...s]); // [NaN, 0] (order may vary but only one zero)
- في المقارنة العادية (
NaN === NaN) تكون النتيجةfalse، ولكن داخل الـSet، تعتبر جميع قيمNaN'نفس القيمة'۔ +0و-0يمكن تمييزهما رياضيًا، ولكن في كائن الـSet، يتم التعامل معهما ببساطة كـ0۔- وبالتالي، يبقى فقط عنصر واحد من
NaNوعنصر واحد من0في كائن الـSet۔ - قاعدة المقارنة في
Setتشبه قاعدةObject.is، لكنها ليست متطابقة تمامًا۔Object.is(+0, -0)تُرجعfalse، لكن في الـSetيعتبران متطابقين. الرجاء الانتباه لهذا الفرق۔
الاستخدامات الشائعة: عمليات المجموعات (الاتحاد، التقاطع، الفرق)
يمكن كتابة عمليات المجموعات بشكل أوضح باستخدام كائن Set۔ فيما يلي أمثلة للتنفيذ الشائع۔
إليكم أمثلة لدوال الاتحاد والتقاطع والفرق۔
1// Set operations: union, intersection, difference
2function union(a, b) {
3 return new Set([...a, ...b]);
4}
5
6function intersection(a, b) {
7 return new Set([...a].filter(x => b.has(x)));
8}
9
10function difference(a, b) {
11 return new Set([...a].filter(x => !b.has(x)));
12}
13
14// Demo
15const A = new Set([1, 2, 3]);
16const B = new Set([3, 4, 5]);
17
18console.log('union', [...union(A, B)]); // [1,2,3,4,5]
19console.log('intersection', [...intersection(A, B)]); // [3]
20console.log('difference A\\B', [...difference(A, B)]); // [1,2]
- يمكن كتابة عمليات المجموعات ببساطة عن طريق استخدام عوامل التصفية بالجمع بين
Setوالمصفوفات۔ عند التعامل مع مجموعات بيانات كبيرة، تجعل خاصية الأداء O(1) لدالةhasالعمليات أسرع۔
مثال عملي: إيجاد الاختلافات بين المصفوفات (الكشف عن العناصر المضافة أو المحذوفة)
يوضح المثال التالي كيفية استخدام كائن Set لإيجاد الفرق بين مصفوفتين (قائمة قديمة وقائمة جديدة)۔ يمكّنك هذا من معرفة العناصر التي تمت إضافتها أو حذفها۔
1// Find added and removed items between two arrays
2function diffArrays(oldArr, newArr) {
3 const oldSet = new Set(oldArr);
4 const newSet = new Set(newArr);
5
6 const added = [...newSet].filter(x => !oldSet.has(x));
7 const removed = [...oldSet].filter(x => !newSet.has(x));
8
9 return { added, removed };
10}
11
12const oldList = [1, 2, 3];
13const newList = [2, 3, 4, 5];
14
15console.log(diffArrays(oldList, newList));
16// { added: [4,5], removed: [1] }
- هذه الطريقة مفيدة جدًا للكشف عن الاختلافات في قوائم المعرفات أو الوسوم وما شابهها۔ ويكون أسهل في الاستخدام مع القيم الأولية۔
الاختلافات بين WeakSet وSet (إدارة الذاكرة)
WeakSet مشابه لـ Set، ولكنه يستخدم مراجع ضعيفة، مما يسمح بجمع عناصره عند عدم الحاجة إليها (garbage collection)۔ يبين ما يلي بعض الاستخدامات الأساسية لـWeakSet۔
1// WeakSet basics (objects only, not iterable)
2const ws = new WeakSet();
3let obj = { id: 1 };
4ws.add(obj);
5
6console.log(ws.has(obj)); // true
7
8obj = null; // Now the object is eligible for GC; WeakSet won't prevent collection
يمكن لـWeakSet احتواء الكائنات فقط ولا يمكن التكرار على عناصره۔ فيما يلي أمثلة على القيود الخاصة بـWeakSet — فهو يحتفظ بالكائنات فقط، ولا يمكن التكرار عليه۔
1// WeakSet basics (objects only, not iterable)
2const ws = new WeakSet();
3
4// --- Only objects can be added ---
5try {
6 ws.add(1); // number
7} catch (e) {
8 console.log("Error: WeakSet can only store objects. Adding a number is not allowed.");
9}
10
11try {
12 ws.add("text"); // string
13} catch (e) {
14 console.log("Error: WeakSet can only store objects. Adding a string is not allowed.");
15}
16
17// --- WeakSet is not iterable ---
18try {
19 for (const value of ws) {
20 console.log(value);
21 }
22} catch (e) {
23 console.log("Error: WeakSet is not iterable. You cannot use for...of to loop over its elements.");
24}
25
26// --- Cannot convert to array ---
27try {
28 console.log([...ws]);
29} catch (e) {
30 console.log("Error: WeakSet cannot be converted to an array because it does not support iteration.");
31}
32
33// The object becomes eligible for garbage collection
34let obj = { id: 1 };
35ws.add(obj);
36obj = null;WeakSetمفيد لتتبع وجود الكائنات بشكل مؤقت، لكن لا يمكنك تعداد عناصره أو معرفة حجمه۔
الأداء واختيار الوقت المناسب للاستخدام
عند اتخاذ قرار باستخدام Set، من المهم فهم خصائص أدائه وطبيعة بياناتك۔
- تعمل دوال
hasوaddوdeleteعادةً بأداء يقارب O(1) في المتوسط۔ لذا في الحالات التي تتحقق فيها كثيرًا من وجود عنصر أو تزيل التكرارات، يكون استخدام الـSetأكثر فائدة من المصفوفات غالبًا۔ - كن حذراً إذا كنت ترغب في إزالة تكرار الكائنات بناءً على محتواها (قيمها)۔ بما أن
Setيقارن عن طريق المرجع، فيمكنك استخدام المعرفات أو المفاتيح أو تسلسل الكائنات إلى قيم أولية قبل استخدامها إذا تطلب الأمر المقارنة بحسب القيمة۔ Setمفيد بشكل خاص لتحسين وضوح الكود عند التعامل مع مجموعات صغيرة إلى متوسطة الحجم۔ من ناحية أخرى، إذا كنت تتعامل مع عدد كبير جدًا من العناصر أو تقوم كثيرًا بالتحويل بين المصفوفات و Sets، فمن المستحسن إجراء اختبارات أداء حقيقية۔
الأخطاء الشائعة
Set مريح وسهل الاستخدام، لكن إذا لم تكن على دراية بمواصفاته فقد تتفاجأ بسلوكيات غير متوقعة۔ فيما يلي بعض النقاط التي يجب الانتباه إليها:۔
- يتم مقارنة الكائنات عن طريق المرجع، حتى لو كان محتواها متطابقاً، لا تعتبر الكائنات المختلفة مكررة۔
Setيحافظ على ترتيب الإدخال، لكن لا يمكنك الوصول للعناصر حسب الفهرس كما هو الحال مع المصفوفات۔ إذا أردت الوصول إلى عناصره باستخدام الفهرسة، قم بتحويل الـSetإلى مصفوفة أولاً۔WeakSetلا يمكن تعداده، و يمكنه فقط تخزين الكائنات۔ يرجى ملاحظة أن استخداماته محدودة۔NaNيتم التعامل معه كقيمة واحدة، كما لا يتم التمييز بين+0و-0۔ ويرجع ذلك إلى قاعدة المقارنة Same-value-zero۔
الملخص
Set هو بنية بيانات مريحة تتيح لك التعامل مع مجموعات من القيم الفريدة بسهولة۔ يمكنك استخدامه لإزالة التكرارات من المصفوفات، وإجراء تحقق سريع من الوجود، أو تنفيذ عمليات المجموعات كالانحاد والتقاطع باستخدام كود بسيط وواضح۔
من ناحية أخرى، وبما أن الكائنات تتم مقارنتها حسب المرجع، فستحتاج لاتخاذ تدابير إضافية إذا أردت الحكم على التساوي بناءً على محتواها۔
من خلال فهم هذه الخصائص واستخدامها بشكل مناسب، يصبح Set اختيارًا قويًا لتحسين وضوح الكود وسهولة صيانته۔
يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔