אובייקט `Set`
מאמר זה מסביר את אובייקט ה-Set.
נסביר את אובייקט ה-Set עם דוגמאות מעשיות.
YouTube Video
אובייקט Set
Set הוא אובייקט מובנה שמטרתו לטפל באוספים של ערכים ייחודיים ללא כפילויות. הוא מאפשר לבצע הסרת כפילויות ובדיקות קיום בצורה פשוטה יותר ממערכים, ומקל לכתוב פעולות קבוצתיות כמו איחוד וחיתוך.
יסודות: יצירה ושימוש ב-Sets
ראשית, נבחן כיצד ליצור 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), אך בפועל בדרך כלל יש צורך רק בפרמטר הראשון -value.
המרה בין מערכים ל-Sets (שימושי להסרת כפילויות)
כאן נציג שיטה פשוטה להסרת כפילויות ממערך, וכיצד להמיר 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 נבדקים לפי הפניה (reference), לכן מופעים שונים עם תוכן זהה ייחשבו כאיברים נפרדים.
הקוד הבא מדגים מה קורה כאשר מוסיפים אובייקטים ל-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, אך הוא משתמש בהפניות חלשות, המאפשרות לאלמנטים שבו להיאסף על ידי מנגנון איסוף האשפה. הדוגמה הבאה ממחישה שימושים בסיסיים ב-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משווה לפי הפניה, הדרך הפרקטית היא להשתמש ב-מזהים (IDs) או מפתחות, או לסדר/להמיר אובייקטים לערכים פרימיטיביים כאשר יש צורך בהשוואה לפי ערך. Setשימושי במיוחד לשיפור הבהירות של קוד עבור אוספים קטנים עד בינוניים. מצד שני, אם אתה מטפל בכמות גדולה מאוד של נתונים או ממיר לעיתים קרובות בין מערכים ל-Sets, מומלץ למדוד ביצועים בפועל.
מכשולים נפוצים
Set הוא נוח, אך אם אינך בקיא במאפייניו, ייתכן שתיתקל בהתנהגויות מפתיעות. להלן נקודות שכדאי לשים לב אליהן:.
- אובייקטים נבדקים לפי הפניה, ולכן גם אם תוכנם זהה, לא נחשבים ככפולים.
Setשומר על סדר ההכנסה, אך אי אפשר לגשת לאיברים לפי אינדקס כמו במערכים. אם ברצונך לגשת לפי אינדקס, המרה של ה-Setלמערך הכרחית.WeakSetלא ניתן למניה/לאיטרציה, ו-ניתן לאחסן בו רק אובייקטים. שים לב ששימושיו מוגבלים.NaNנחשב לאותו ערך, ו-+0ו--0לא מובדלים זה מזה. זה נובע מכלל ההשוואה Same-value-zero.
סיכום
Set הוא מבנה נתונים נוח המאפשר לך לטפל באוספים של ערכים ייחודיים בצורה אינטואיטיבית. באמצעותו ניתן להסיר כפילויות ממערכים, לבצע בדיקות קיום במהירות, או ליישם פעולות קבוצתיות כמו איחוד וחיתוך בקוד פשוט וקריא.
מן הצד השני, מאחר שאובייקטים משווים לפי הפניה, יש לנקוט אמצעים נוספים כדי להשוות לפי תוכן בלבד.
בהבנת התכונות האלו ושימוש נכון בהן, Set הופך לכלי עוצמתי לשיפור הקריאות והתחזוקה של הקוד.
תוכלו לעקוב אחר המאמר שלמעלה באמצעות Visual Studio Code בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.