بررسی کاربرد Map و Set در جاوااسکریپت (پیشرفته)

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

تا الان با دو نوع ساختار داده پیچیده آشنا شدیم:

  1. آبجکت ها (Objects): برای ذخیره مجموعه های کلید-آیتم
  2. آرایه ها (Arrays): برای ذخیره مجموعه های مرتب

اما این دو نوع پاسخگوی تمام نیازهای یک برنامه نویس نمی باشند. به همین منظور Map و Set معرفی شدند.

نوع داده ای Map:

Map هم مثل آبجکت ها بصورت “کلید-آیتم” است. با این تفاوت که کلید (key) می تواند هر نوع داده ای داشته باشد.

لیست متدها و property های MAP:

متدها و property های Map به شرح زیر است:

  • new Map: برای ایجاد یک مپ
  • map.set(key, value) : برای ذخیره مقدار با کلید
  • map.get(key) : برای دریافت مقدار توسط کلید. اگر کلید در map وجود نداشته باشد undefined برمی گردد.
  • map.has(key) : اگر کلید موجود باشد true بر می گردد وگرنه false .
  • map.delete(key) : مقدار را با کلید مذکور بر می گرداند.
  • ()map.clear : تمام المان ها را از map پاک می کند.
  • map.size : تعداد المان های جاری را بر می گرداند.

بعنوان مثال:

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: &quotJohn&quot };

// 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: &quotJohn&quot };

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[&quot[object Object]&quot] ); // 123

از آنجا که متغیر visitsCountObj یک آبجکت است، تمام کلیدهای تعریف شده برای آن (مانند John) بطور اتوماتیک به رشته تبدیل خواهند شد. بنابراین کلید آبجکت مذکور بصورت “[object Object]” تعریف می شود.

تعریف زنجیره ای متد set در Map:

map.set('1', 'str1')
  .set(1, 'num1')
  .set(true, 'bool1');

در کد بالا، مشاهده می شود که می توانید متدهای set را بصورت زنجیروار تعریف کنید. زیرا هر یک از دستورات set یک map را برمیگرداند.

پیمایش و اجرای حلقه در Map:

برای اجرای حلقه روی آیتم های یک map می توان به یکی از سه روش زیر اقدام کرد:

  • ()map.keys: امکان لوپ زدن روی کلیدهای Map را فراهم می کند.
  • ()map.values: امکان لوپ زدن روی مقادیر Map را فراهم می کند.
  • ()map.entries: جفت کلید-آیتم را بر می گرداند. یعنی [key, item] . معمولا در حلقه for..of استفاده می شود.


مثالی از 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
});


تبدیل Object به Map توسط متد Object.entries:

وقتی 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: &quotJohn&quot,
  age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John

در این مثال Object.entries یک آرایه ای از جفت key/value بر می گرداند که بصورت زیر خواهد بود:

[ [&quotname&quot,&quotJohn&quot], [&quotage&quot, 30] ]


تبدیل Map به Object توسط متد Object.fromEntries:

اگر بخواهیم آرایه ای از [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:

در کد زیر، داده 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:

متدهای اصلی Set عبارتند از:

  • new Set(iterable): یک Set ایجاد می کند و اگر یک داده iterable مثل آرایه به آن پاس داده شده باشد آیتم های آن را برای Set در نظر می گیرد.
  • set.add(value): یک مقدار جدید به set می افزاید و خود set را بر می گرداند.
  • set.delete(value): آیتم value را از set حذف می کند. اگر value وجود داشته باشد true بر می گرداند وگرنه false
  • set.has(value): اگر value در set وجود داشته باشد true وگرنه false بر می گردد.
  • ()set.clear: تمام مقادیر داخل set را پاک می کند.
  • set.size: تعداد آیتم های داخل set را بر می گرداند.

نکته: اجرای مکرر set.add(value) هیچکاری انجام نمی دهد. به این دلیل که نمی توان یک مقدار ثابت را چندین بار در Set درج کرد (Set آیتم تکراری ندارد)

بعنوان مثال فرض کنید می خواهیم اسامی بازدیدکنندگان یک وب سایت را در طول روز ذخیره کنیم و نمی خواهیم نام تکراری در لیست ما درج شود. داریم:

let set = new Set();

let john = { name: &quotJohn&quot };
let pete = { name: &quotPete&quot };
let mary = { name: &quotMary&quot };

// 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) اسکریپت کاهش می یابد و سرعت اجرای برنامه نیز کند می شود.


لوپ زدن روی Set:

توسط دستورات for..of و forEach می توان روی مقادیر داخل Set حلقه اجرا کرد.

بعنوان مثال:

let set = new Set([&quotoranges&quot, &quotapples&quot, &quotbananas&quot]);

for (let value of set) alert(value);

// the same with forEach:
set.forEach((value, valueAgain, set) => {
  alert(value);
});

متدهای Set:

Set هم مانند Map متدهای زیر را برای کار با آیتم ها دارد:

  • ()set.keys: یک شیء iterable از مقادیر را بر می گرداند.
  • ()set.values: یک شیء iterable از مقادیر را بر می گرداند. (مانند متد keys)
  • ()set.entries: یک شیء iterable از مقادیر را بر می گرداند. بصورت [value, value]




همچنین بخوانید:

اسکوپ (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) با عملگرهای منطقی AND (&&) و OR (||) در جاوااسکریپت

اتصال کوتاه (Short Circuiting) با عملگر منطقی Nullish coalescing (??) در جاوااسکریپت

حلقه for ... of در جاوااسکریپت

بررسی Enhanced Object literals در جاوااسکریپ

بررسی Optional Chaining (_.) در جاوااسکریپت

بررسی کاربرد Map و Set در جاوااسکریپت (مقدماتی)