سلام در این قسمت از آموزش می خواهیم درباره دو ساختار داده پیچیده بنام های Map و Set به صورت پیشرفته تر صحبت کنیم. با ما باشید…

تا الان با دو نوع ساختار داده پیچیده آشنا شدیم:
اما این دو نوع پاسخگوی تمام نیازهای یک برنامه نویس نمی باشند. به همین منظور Map و Set معرفی شدند.
Map هم مثل آبجکت ها بصورت “کلید-آیتم” است. با این تفاوت که کلید (key) می تواند هر نوع داده ای داشته باشد.
متدها و property های Map به شرح زیر است:
بعنوان مثال:
let map = new Map(); map.set('1', 'str1'); // a string key map.set(1, 'num1'); // a numeric key map.set(true, 'bool1'); // a boolean key // remember the regular Object? it would convert keys to string // Map keeps the type, so these two are different: alert( map.get(1) ); // 'num1' alert( map.get('1') ); // 'str1' alert( map.size ); // 3
همانطور که مشاهده می کنید، کلیدها به رشته تبدیل نشده اند و می توانند از هر نوعی باشند.
نکته: استفاده از map با براکت یعنی map[key] روش مناسبی نمی باشد. زیرا در اینصورت جاوا اسکریپت با Map بعنوان یک آبجکت ساده رفتار خواهد کرد و کلید آن نمی تواند از هر نوعی باشد (فقط رشته می تواند باشد). روش صحیح کار با map استفاده از set و get است.
نکته جالب اینجاست که Map می تواند از object بعنوان کلید خود استفاده کند.
بعنوان مثال:
let john = { name: "John" }; // for every user, let's store their visits count let visitsCountMap = new Map(); // john is the key for the map visitsCountMap.set(john, 123); alert( visitsCountMap.get(john) ); // 123
این قابلیت Map (یعنی امکان تعریف یک آبجکت بعنوان کلید) بسیار اهمیت دارد و کاربردی است. مثال:
let john = { name: "John" }; let visitsCountObj = {}; // try to use an object visitsCountObj[john] = 123; // try to use john object as the key // That's what got written! alert( visitsCountObj["[object Object]"] ); // 123
از آنجا که متغیر visitsCountObj یک آبجکت است، تمام کلیدهای تعریف شده برای آن (مانند John) بطور اتوماتیک به رشته تبدیل خواهند شد. بنابراین کلید آبجکت مذکور بصورت “[object Object]” تعریف می شود.
map.set('1', 'str1') .set(1, 'num1') .set(true, 'bool1');
در کد بالا، مشاهده می شود که می توانید متدهای set را بصورت زنجیروار تعریف کنید. زیرا هر یک از دستورات set یک map را برمیگرداند.
برای اجرای حلقه روی آیتم های یک map می توان به یکی از سه روش زیر اقدام کرد:
مثالی از 3 متد فوق:
let recipeMap = new Map([ ['cucumber', 500], ['tomatoes', 350], ['onion', 50] ]); // iterate over keys (vegetables) for (let vegetable of recipeMap.keys()) { alert(vegetable); // cucumber, tomatoes, onion } // iterate over values (amounts) for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 } // iterate over [key, value] entries for (let entry of recipeMap) { // the same as of recipeMap.entries() alert(entry); // cucumber,500 (and so on) }
علاوه بر موارد ذکر شده، Map بطور پیش فرض یک متد forEach درون خود دارد (مانند آرایه ها):
// runs the function for each (key, value) pair recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc });
وقتی Map را ایجاد کردیم می توان یک آرایه (یا هر نوع داده iterable دیگر) به آن پاس داد.
// array of [key, value] pairs let map = new Map([ ['1', 'str1'], [1, 'num1'], [true, 'bool1'] ]); alert( map.get('1') ); // str1
اگر یک آبجکت ساده داشته باشیم و بخواهیم آنرا به Map تبدیل کنیم می توان از متد Object.entries(obj) استفاده کرد.
مثالی از تبدیل object به Map:
let obj = { name: "John", age: 30 }; let map = new Map(Object.entries(obj)); alert( map.get('name') ); // John
در این مثال Object.entries یک آرایه ای از جفت key/value بر می گرداند که بصورت زیر خواهد بود:
[ ["name","John"], ["age", 30] ]
اگر بخواهیم آرایه ای از [key, value] را به یک آبجکت ساده یا plain object تبدیل کنیم باید از متد Object.fromEntries استفاده کنیم:
let prices = Object.fromEntries([ ['banana', 1], ['orange', 2], ['meat', 4] ]); // now prices = { banana: 1, orange: 2, meat: 4 } alert(prices.orange); // 2
بعنوان مثال فرض کنید ما مقادیر موردنظر خود را در یک Map ذخیره کرده ایم و برای تعامل با یک اپلیکیشن 3rd-party نیاز است که این Map به آبجکت ساده تبدیل شود. داریم:
let map = new Map(); map.set('banana', 1); map.set('orange', 2); map.set('meat', 4); let obj = Object.fromEntries(map.entries()); // make a plain object (*) // done! // obj = { banana: 1, orange: 2, meat: 4 } alert(obj.orange); // 2
برای آرگومان ورودی Object.fromEntries عبارت map.entries تعریف شده که مناسب است. همچنین می توان آن را بصورت خلاصه تری تعریف کرد (حذف entries)
let obj = Object.fromEntries(map); // omit .entries()
در کد زیر، داده Map بطور اشتباه مقداردهی شده است:
let wrongMap = new Map(); wrongMap['bla'] = 'blaa'; wrongMap['bla2'] = 'blaaa2'; console.log(wrongMap) // Map { bla: 'blaa', bla2: 'blaaa2' }
و مسلما در هنگام اجرا، نتیجه مورد انتظار را نخواهیم گرفت:
wrongMap.has('bla'); // false wrongMap.delete('bla'); // false console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
و اما روش صحیح استفاده از Map (همانطور که قبلا گفتیم) بصورت زیر است:
let myMap = new Map(); myMap.set('bla','blaa'); myMap.set('bla2','blaa2'); console.log(myMap); // Map { 'bla' => 'blaa', 'bla2' => 'blaa2' }
و خروجی مورد نظر را خواهیم داشت:
myMap.has('bla'); // true myMap.delete('bla'); // true console.log(myMap); // Map { 'bla2' => 'blaa2' }
Set نوعی داده است که فقط شامل مقادیر است و کلید ندارد. هر مقدار نیز باید یکتا و منحصر بفرد باشد و نمی تواند تکراری باشد.
متدهای اصلی Set عبارتند از:
نکته: اجرای مکرر set.add(value) هیچکاری انجام نمی دهد. به این دلیل که نمی توان یک مقدار ثابت را چندین بار در Set درج کرد (Set آیتم تکراری ندارد)
بعنوان مثال فرض کنید می خواهیم اسامی بازدیدکنندگان یک وب سایت را در طول روز ذخیره کنیم و نمی خواهیم نام تکراری در لیست ما درج شود. داریم:
let set = new Set(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; // visits, some users come multiple times set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); // set keeps only unique values alert( set.size ); // 3 for (let user of set) { alert(user.name); // John (then Pete and Mary) }
آرایه می تواند جایگزین Set باشد به شرطی که در هر بار Add کردن یک آیتم جدید توسط متد find بررسی کنیم که آن آیتم از قبل در آرایه وجود نداشته باشد و آیتم تکراری ایجاد نکند. در صورت استفاده از آرایه بدین شکل، کارایی (performance) اسکریپت کاهش می یابد و سرعت اجرای برنامه نیز کند می شود.
توسط دستورات for..of و forEach می توان روی مقادیر داخل Set حلقه اجرا کرد.
بعنوان مثال:
let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); // the same with forEach: set.forEach((value, valueAgain, set) => { alert(value); });
Set هم مانند Map متدهای زیر را برای کار با آیتم ها دارد:
همچنین بخوانید:
اسکوپ (scope) در جاوااسکریپت چیست؟
هویستینگ (Hoisting) در جاوااسکریپت چیست؟
بررسی (let - var - const) در جاوااسکریپت
تفاوت دو مساوی (==) و سه مساوی (===) در جاوااسکریپت
بررسی متدهای تعامل با کاربر در جاوااسکریپت : alert , prompt, confirm
استفاده از عبارت 'use strict' در جاوااسکریپت
کار با توابع یا Functions در جاوااسکریپت
مقایسه توابع Declarations و Expressions در جاوااسکریپت
بررسی Callback Function در جاوااسکریپت
تفاوت عملگرهای Spread و Rest در جاوااسکریپت
پارامتر پیشفرض(Default Parameter) در جاوااسکریپت
بررسی arguments در توابع جاوااسکریپت
بررسی پروتوتایپ (Prototype) در جاوااسکریپت
بررسی Arrow Functions در جاوااسکریپت
بررسی Functions Calling Other Functions در جاوااسکریپت
بررسی آرایه (Array) در جاوااسکریپت
بررسی اشیاء یا آبجکت (objects) در جاوااسکریپت
بررسی حلقهها (Loops) در جاوااسکریپت
بررسی انتخابگرها (Selectors) در جاوااسکریپت
بررسی innerHTML در جاوااسکریپت
بررسی DOM در جاوااسکریپت، آموزش متد querySelector
بررسی نحوه عملکرد جاوا اسکریپت در پشت صحنه
بررسی مفهوم Execution Context در جاوااسکریپت - پشت پرده جاوااسکریپت (بخش اول)
بررسی مفهوم Execution Context در جاوااسکریپت - پشت پرده جاوااسکریپت (بخش دوم)
تعریف API (Application Programming Interface) به زبان ساده!
دِستراکچرینگ (Destructuring) در جاوااسکریپت یعنی چه؟
بررسی عملگر (...) Spread در جاوااسکریپت
بررسی پارامترهای Rest در جاوااسکریپت
اتصال کوتاه (Short Circuiting) با عملگر منطقی Nullish coalescing (??) در جاوااسکریپت
حلقه for ... of در جاوااسکریپت
بررسی Enhanced Object literals در جاوااسکریپ