Map و Set در جاوااسکریپت
27 تیر
اکثر برنامهنویسان جاوااسکریپت با ساختارهای دادهی زیر آشنایی دارند:
- آبجکتها (Object) برای ذخیره کردن داده با استفاده از کلید (key).
- آرایهها (Array) برای ذخیره کردن داده به شکل ترتیبی.
اما در عمل این دو نوع ساختار کافی نبودهاند. برای همین ساختارهای Map و Set در جاوااسکریپت معرفی شدهاند که در ادامه آنها را بررسی میکنیم. بنابراین با ما همراه باشید.
ساختار دادهی Map
Map مجموعهای از دادههای دارای کلید (key) است. دقیقا مشابه با آبجکتها. اما تفاوت اساسی در این است که کلیدها در Map میتوانند از هر نوعی باشند. مثلا میتوانند عدد، رشته، بولن یا حتی آبجکت باشند.
مهمترین متدها و پراپرتیهایی که در هنگام کار با Map با آنها سر و کار داریم عبارتند از:
new Map()
– برای درست کردن یا تعریف کردن یک Map استفاده میشود.map.set(key, value)
– یک مقدار را با کلید مشخصی داخل map ذخیره میکند.map.get(key)
– با دادن کلید، دادهی متناظر با آن را برمیگرداند. اگر دادهای با آن کلید وجود نداشته باشد، مقدار undefined
برگردانده میشود.map.has(key)
– اگر کلید داده شده، در map وجود داشته باشد مقدار true و در غیر این صورت مقدار false را برمیگرداند.map.delete(key)
– دادهی متناظر با کلید را پاک میکند.map.clear()
– همه چیز را داخل map پاک میکند.map.size
– تعداد دادههای داخل map را برمیگرداند.
برای مثال:
let map = new Map();
map.set('1', 'str1'); // a string key (kilide reshte)
map.set(1, 'num1'); // a numeric key (kilide addadi)
map.set(true, 'bool1'); // a boolean key (kilid az no-e boolean)
// dar object-ha, key-ha tabdil be reshteh (string) mishavand.
// amma dar Map-ha kilide 1 ba '1' motafavet ast:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
console.log( map.size ); // 3
در مثال بالا میتوانیم ببینیم که بر خلاف آبجکتها، در Mapها کلیدها صرفا رشته نیستند و استفاده از هر نوع داده به عنوان کلید امکانپذیر خواهد بود (کلید از نوع عددی، بولن، آبجکت و …).
نکته: استفاده از ترکیب [map[key راه درستی برای استفاده از Map ها نیست.
درست است که map[key]
کار میکند اما استفاده از این ترکیب همانند استفاده از آبجکتهای معمولی در جاوااسکریپت است و محدودیتهای آن را نیز دارد. مثلا ما نمیتوانیم از آبجکتها به عنوان کلید استفاده کنیم. به جای استفاده از این ترکیب، ما باید از متدهای get
و set
استفاده کنیم.
استفاده از آبجکتها به عنوان کلید
به مثال زیر توجه کنید:
let john = { name: "John" };
// baraye har karbar, tedade bazdid raa zakhireh mikonim:
let visitsCountMap = new Map();
// john be onvane kilid baraye map estefadeh mishavad:
visitsCountMap.set(john, 123);
console.log( visitsCountMap.get(john) ); // 123
استفاده از آبجکتها به عنوان کلید برای map، از مهمترین ویژگیهای Mapها محسوب میشود. برای کلیدهای رشتهای، آبجکتها قابل استفاده هستند اما برای کلیدهای غیر رشتهای نه! بیایید امتحان کنیم:
let john = { name: "John" };
// az object estefadeh mikonim:
let visitsCountObj = {};
// az object be onvane kilid estefadeh mikonim:
visitsCountObj[john] = 123;
// dar vaqe chizi ke be onvane kilid neveshtim be surate zir ast:
console.log( visitsCountObj["[object Object]"] ); // 123
چون visitsCountObj
یک آبجکت است، همه کلیدها را به رشته تبدیل میکند. وقتی john
به رشته تبدیل شود، رشتهی "[object Object]"
را خواهیم داشت که مسلما چیزی نیست که ما میخواهیم.
چگونه Map کلیدها را مقایسه میکند؟
برای بررسی برابری کلیدها، Map از الگوریتم SameValueZero استفاده میکند. این الگوریتم تقریبا مشابه همان اوپراتور برابری ===
است. با این تفاوت که NaN
با NaN
برابر در نظر گرفته میشود. بنابراین NaN
هم میتواند به عنوان کلید مورد استفاده قرار گیرد. این الگوریتم قابل تغییر یا ویرایش نیست.
زنجیربافی
هر map.set
خود map را برمیگرداند. بنابراین میتوانیم setها را به شکل زنجیرهای به کار بگیریم:
map.set('1', 'str1' )
.set( 1 , 'num1' )
.set(true, 'bool1');
ایتراسیون Map
برای ایتراسیون (Iteration) یا استفاده از فرآیندهای For … loop در Mapها سه متد وجود دارد:
map.keys()
– یک ایتریبل (Iterable) از کلیدها میسازد. ایتریبل به آیتمهایی اطلاق میشود که میتوان آن را با استفاده از حلقههای for
یا map
کردن یا با فراخوانی متد next
و امثال این موارد پیمایش کرد. map.values()
– یک ایتریبل از مقادیر میسازد.map.entries()
– یک ایتریبل به شکل [key, value]
میسازد که فرمت پیشفرض برای استفاده در for...of
است.
برای مثال:
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// iterate kardane kilid-ha:
for (let vegetable of recipeMap.keys()) {
console.log(vegetable); // cucumber, tomatoes, onion
}
// iterate kardane maghadir:
for (let amount of recipeMap.values()) {
console.log(amount); // 500, 350, 50
}
// iterate kardan ruye [key, value]:
// iteration ruye recipeMap = iteration ruye recipeMap.entries()
for (let entry of recipeMap) {
console.log(entry); // [cucumber,500] (va elaa aakhar)
}
توجه به این نکته ضروری است که در ایتراسیون Mapها، بر خلاف آبجکتهای معمول جاوااسکریپت، ترتیب وارد کردن داده به Map حفظ میشود. یعنی دادهها با همان ترتیبی که به Map داده شدهاند، ایتریت میشوند.
علاوه بر این روشها، Map دارای متد forEach
هم هست. مشابه با آرایهها میتوان نوشت:
// function raa be ezaye har (key, value) ejraa mikonad:
recipeMap.forEach( (value, key, map) => {
console.log(`${key}: ${value}`); // cucumber: 500 va ...
});
ساختن Map از روی آبجکتها با Object.entries
زمانی که Map درست میکنیم، میتوانیم با افزودن یک آرایه (یا هر ایتریبل دیگر) که حاوی جفتهای (کلید و مقدار) باشد، Map را مقداردهی اولیه کنیم. برای مثال:
// arayeye joft-haye [key, value]
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
console.log( map.get('1') ); // str1
اگر یک آبجکت معمولی داشته باشیم و بخواهیم از آن یک Map درست کنیم، میتوانیم از متد Object.entries(obj)
استفاده کنیم که جفتهای (کلید و مقدار) از روی آبجکت میسازد. دقیقا با همان فرمتی که در مثال قبل ذکر شد. بنابراین به شکل زیر میتوانیم از روی یک آبجکت، یک Map درست کنیم:
let obj = {
name: "John",
age: 30
};
let map = new Map(Object.entries(obj));
console.log( map.get('name') ); // John
در اینجا، Object.entries
آرایهای به شکل [ ["name","John"], ["age",30] ]
تولید میکند. دقیقا چیزی که Map به عنوان وروی به آن احتیاج دارد.
ساختن آبجکت از Map با Object.fromEntries
دیدیم که چطور از آبجکتهای معمولی جاوااسکریپت Map بسازیم. متد Object.fromEntries
برعکس این کار را انجام میدهد. روش کار این متد به این صورت است که با دادن آرایهای از جفتهای [key, value]
به آن، آبجکتی از روی آن ساخته میشود:
let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);
// prices = { banana: 1, orange: 2, meat: 4 }
console.log(prices.orange); // 2
با استفاده از همین ویژگی هست که میتوانیم از Map آبجکت معمولی درست کنیم. مثلا فرض کنید ما دادههایی را در Map ذخیره کردهایم اما میخواهیم آنها را به یک کد آماده یا کتابخانه و غیره به عنوان ورودی بدهیم که این کد یا کتابخانه ورودی را فقط به شکل آبجکت میپذیرد. در این صورت مینویسیم:
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
// dorost kardane object mamooli:
let obj = Object.fromEntries(map.entries());
// obj = { banana: 1, orange: 2, meat: 4 }
console.log(obj.orange); // 2
صدا زدن map.entries
جفتهای کلید / مقدار را دقیقا با همان فرمتی درست میکند که ورودی متد Object.fromEntries
انتظار دارد. البته میتوان به جای استفاده از map.entries
مستقیما خود map
را نیز به کار برد:
let obj = Object.fromEntries(map); // be jaye map.entries()
هر دو این کدها درست است. چون Object.fromEntries
به عنوان آرگومان یک ایتریبل نیاز دارد که لزوما آرایه نیست و ایتراسیون خود map
دقیقا مانند ایتراسیون map.entries
جفتهای کلید / مقدار برمیگرداند. بنابراین با دادن خود map
هم به عنوان ورودی متد Object.fromEntries
، آبجکت معمولی ما تولید میشود.
ساختار دادهی Set
Set کلکسیونی از دادههاست که بدون «کلید» کنار هم میآیند (مانند آرایهها)؛ با این تفاوت که هر مقداری تنها یک بار میتواند وجود داشته باشد (دادههای تکراری نخواهیم داشت). مهمترین متدهای مورد استفاده با Setها به شرح زیر است:
new Set(iterable)
– یک Set درست میکند و اگر آبجکت با قابلیت ایتراسیون به آن داده شود (معمولا آرایه) این مقادیر را داخل Set ساخته شده وارد میکند.set.add(value)
– یک مقدار را وارد Set کرده و کل Set را برمیگرداند.set.delete(value)
– مقدار را از Set پاک میکند. اگر مقدار مورد نظر در هنگام پاک کردن داخل Set حضور داشت مقدار true
و در غیر این صورت مقدار false
برمیگرداند. set.has(value)
– اگر مقدار value داخل Set وجود داشته باشد مقدار true
و در غیر این صورت مقدار false
را برمیگرداند.set.clear()
– همگی مقادیر داخل Set را پاک میکند.set.size
– تعداد مقادیر داخل Set (اندازهی Set) را برمیگرداند.
مهمترین ویژگی Set این است که فراخوانی set.add()
با دادههای تکراری هیچ کاری انجام نمیدهد. بنابراین هر مقداری در داخل Set فقط یکبار قابل وارد کردن است. برای مثال میخواهیم لیستی از بازدیدکنندگان را داشته باشیم تا بفهمیم چه کسانی از سایت بازدید کردهاند. با این حال نمیخواهیم بازدیدهای تکراری ثبت شوند و بازدیدکنندگان تنها باید یکبار ثبت شوند. استفاده از Set در این گونه موارد راه درستی خواهد بود:
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// barzdid-hayi anjam va sabt mishavad:
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// set tanha maqadire monhaser be fard ra zakhire mikonad:
console.log( set.size ); // 3
for (let user of set) {
console.log(user.name); // John (dar edame Pete and Mary)
}
روش دیگر حل این مساله استفاده از آرایهها است. اما قبل از وارد کردن داده باید با استفاده متد find()
تکراری نبودن داده را در آرایه چک کنیم. اما این موضوع پرفورمنس خوبی ندارد. چون باید تک تک اعضای آرایه مورد بررسی قرار بگیرند. استفاده از Set روش بسیار بهینهتری برای ثبت دادههای منحصر به فرد (یونیک) است.
ایتراسیون روی Setها
هم با استفاده از for...of
و هم با استفاده از forEach()
میتوانیم برای Setها حلقه درست کنیم:
let set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) console.log(value);
// be tor moshabeh baa forEach:
set.forEach((value, valueAgain, set) => {
console.log(value);
});
به نکته جالب کد توجه کردید؟ تابع کالبک برای forEach
دارای سه آرگومان است. یک مقدار (value
)، دوباره همان مقدار (valueAgain
) و خود set
. بنابراین یک مقدار دو بار در آرگومانها تکرار میشود. این موضوع به دلیل سازگاری با Mapها است که در آن متد forEach
دارای سه آرگومان است. اندکی عجیب به نظر میآید اما در شرایط خاصی این امکان را میدهد که Map و Set به راحتی با همدیگر تعویض شوند (یا برعکس).
در اینجا متدهایی که Map پشتیبانی میکند نیز وضعیت مشابهی دارند. یعنی توسط Setها هم پشتیبانی میشوند اما ممکن است مقادیر جالبی برگردانند:
set.keys()
– یک آبجکت ایتریبل از مقادیر را برمیگرداند!set.values()
– مانند set.keys()
هست؛ به خاطر سازگاری با Mapهاset.entries()
– آبجکت ایتریبل از جفتهای [value, value]
میسازد و برای سازگاری با Mapها این امکان فراهم شده است.
حال با Map و Set در جاوااسکریپت آشنا شدیم. نظرات خودتان را در رابطه با این دو مفهوم جاوااسکریپتی برای من بفرستید. به امید دیدار
منبع: Map and Set
8 اردیبهشت وردپرس wordpress و وردپرس
چگونه در وردپرس از نیمفاصله استفاده کنیم؟
استفاده از نیمفاصله برای نگارش یک متن فارسی درست و استاندارد ضروری است. همهی ما به تعدادی Shortcut برای گذاشتن نیمفاصله عادت داریم. مثلا در نرم افزار Microsoft Word از ترکیب Ctrl و علامت منها استفاده میکنیم. اما زمانی که کار به نوشتن در وب و استفاده از وردپرس، فتوشاپ و نرمافزارهای دیگر برسد، ممکن است دچار مشکل شویم. پس چگونه در وردپرس از نیمفاصله استفاده کنیم؟
5 اردیبهشت جاوااسکریپت Promise و جاوااسکریپت
Promiseها در جاوااسکریپت - بخش اول
جاوااسکریپت از جمله زبانهایی است که اتفاقات غیر همزمان (asynchronous) در آن معمول و متداول است. مثلا وقتی یک درخواست به سرور میفرستید جاوااسکریپت منتظر نمیماند تا پاسخ آن دریافت شود. بلکه به اجرای خط به خط برنامه ادامه میدهد. اما این همیشه مطلوب نیست. بعضی اوقات لازم است تا از جاوااسکریپت قول بگیریم که بعد از مشخص شدن تکلیف اجرای یک دستور غیر همزمان کاری را انجام دهد. اینجاست که راه Promiseها به قضیه باز میشود. دنیای برنامهنویسی غیر همزمان دنیای مرموز و گاها دشواری است. پس بیایید یکی از بهترین مقالات را در این زمینه مرور کنیم.
13 اردیبهشت جاوااسکریپت جاوااسکریپت
this در جاوااسکریپت را بهتر بشناسیم
کلیدواژهی this در جاوااسکریپت یکی از مفهومهایی است که باعث سردرگمی مبتدیان این زبان میشود. شاید یکی از دلایل این موضوع این باشد که کلیدواژهی this در جاوااسکریپت، در مقایسه با زبانهای برنامهنویسی دیگر اندکی متفاوت است. از طرفی یادگیری این کلیدواژه بسیار ضروری است. چرا که برای خواندن و نوشتن کدهای حرفهای و درک مفاهیم جاوااسکریپت همواره به آن نیاز خواهیم داشت. در این مقاله به بررسی این کلیدواژه و کاربردهای مختلفی که در زبان جاوااسکریپت دارد میپردازیم.
6 اردیبهشت جاوااسکریپت Promise و جاوااسکریپت
Promiseها در جاوااسکریپت – بخش دوم
در این مقاله مبحث پیشین را ادامه میدهیم. سعی میکنیم نحوهی تعامل با Promiseها را یک به یک مرور کرده و با مثال ابعاد مختلف این مفهوم را واکاوری کنیم.