المان حذف شد، اما آرایه هنوز هم 3 عضو دارد که میتوانیم آن را با arr.length == 3 ببینیم.
این چیز طبیعی است چون delete obj.key یک مقدار را با استفاده از key حذف میکند. به طور کلی کارش همین است. برای شیءها مناسب است. اما برای آرایهها ما معمولا میخواهیم که بقیه المانها پخش شوند و فضای آزاد شده را اشغال کنند. توقع داریم که الان آرایهای کوتاهتر داشته باشیم.
بنابراین متدهای خاص باید استفاده شوند.
متد arr.splice یک شمشیر ارتشی سوئیسی برای آرایهها است. میتواند هر کاری کند: اضافه کند، حذف کند و المانها را جایگزین کند.
این متد arr را از ایندکس start تغییر میدهد: به تعداد deleteCount المان حذف میکند و سپس elem1, ..., elemN را در مکان خودشان اضافه میکند. آرایهای از المانهای حذف شده را برمیگرداند.
این متد را با مثال به راحتی متوجه میشوید.
بیایید با حذف کردن شروع کنیم:
let arr = ["I", "study", "JavaScript"];
arr.splice(1, 1); // از ایندکس 1 به تعداد 1 المان حذف کن
alert( arr ); // ["I", "JavaScript"]
راحت است، نه؟ از ایندکس 1 به تعداد 1 المان حذف کرد.
در مثال بعد ما 3 المان را حذف و آنها را با دو المان جایگزین میکنیم:
let arr = ["I", "study", "JavaScript", "right", "now"];
// سه المان ابتدایی را حذف کن و آنها را با المانهای دیگر جایگزین کن
arr.splice(0, 3, "Let's", "dance");
alert( arr ) // now ["Let's", "dance", "right", "now"]
اینجا میبینیم که splice آرایهای از المانهای حذف شده را برمیگرداند:
let arr = ["I", "study", "JavaScript", "right", "now"];
// دو المان اول را حذف کن
let removed = arr.splice(0, 2);
alert( removed ); // "I", "study" <-- array of removed elements
متد splice همچنین قادر به اضافه کردن المان بدون هیچ حذفیاتی است. برای این کار باید deleteCount را 0 بگذاریم:
let arr = ["I", "study", "JavaScript"];
// از ایندکس 2
// به تعداد 0 حذف کن
// را اضافه کن "language" و "complex" سپس
arr.splice(2, 0, "complex", "language");
alert( arr ); // "I", "study", "complex", "language", "JavaScript"
ایندکسهای منفی مجاز هستند
اینجا و در دیگر متدهای آرایه، ایندکسهای منفی قابل استفاده هستند. آنها موقعیت را از انتهای آرایه مشخص میکنند، مثل اینجا:
let arr = [1, 2, 5];
// از ایندکس 1- (یک قدم قبل از انتها)
// به تعداد 0 المان حذف کن،
// سپس 3 و 4 را اضافه کن
arr.splice(-1, 0, 3, 4);
alert( arr ); // 1,2,3,4,5
متد arr.slice از متد arr.splice که از لحاظ ظاهری شبیه به آن است بسیار سادهتر است.
سینتکس اینگونه است:
arr.slice([start], [end])
این متد یک آرایه جدید که تمام المانها را از ایندکس start تا end (شامل خود end نمیشود) کپی میکند، برمیگرداند. start و end هر دو میتوانند منفی باشند، که در این صورت موقعیت از انتهای آرایه حساب میشود.
این متد شبیه متد رشته str.slice است، اما به جای زیر رشته، زیر آرایه ایجاد میکند.
برای مثال:
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s (کپی کردن از 1 تا 3)
alert( arr.slice(-2) ); // s,t (کپی کردن از 2- تا انتها)
همچنین میتوانیم آن را بدون آرگومان هم صدا بزنیم: arr.slice() که یک کپی از arr میسازد. معمولا از این روش برای ایجاد یک کپی با هدف اینکه تغییرات آینده روی آرایه اصلی تاثیری نگذارد استفاده میکنند.
متد arr.concat یک آرایه جدید میسازد که حاوی مقدارهای آرایههای دیگر و المانهای اضافی است.
سینتکس آن اینگونه است:
arr.concat(arg1, arg2...)
این متد به هر تعدادی آرگومان میپذیرد – چه آرایه باشند چه مقدار.
نتیجه آن یک آرایه جدید حاوی المانهای arr، سپس arg1، arg2 و غیره.
اگر آرگومان argN یک آرایه باشد، سپس تمام المانهای آن کپی میشود. در غیر این صورت، خود آرگومان کپی میشود.
برای مثال:
let arr = [1, 2];
// و [3,4] arr :ساخت یک آرایه از
alert( arr.concat([3, 4]) ); // 1,2,3,4
// و [3,4] و [5,6] arr :ساخت یک آرایه از
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6
// و [3,4]، سپس اضافه کردن مقدارهای 5 و 6 arr :ساخت یک آرایه از
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
به طور معمول، این متد فقط المانها را از آرایهها کپی میکند. بقیه شیءها، حتی اگر شبیه آرایه باشند، به طور کلی اضافه میشوند:
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1
};
alert( arr.concat(arrayLike) ); // 1,2,[object Object]
…اما اگر یک شیء شبیه به آرایه یک ویژگی Symbol.isConcatSpreadable داشته باشد، سپس concat با آن به عنوان یک آرایه رفتار میکند: در عوض المانهای آن اضافه میشوند:
متدهای arr.indexOf و arr.includes سینتکس مشابه دارند و اساسا همان کار همتایان خود در رشتهها را انجام میدهند، اما به جای کاراکترها با المانها کار دارند:
arr.indexOf(item, from) – با شروع از ایندکس from به دنبال item میگردد و ایندکسی که المان در آن پیدا شد را برمیگرداند، در غیر این صورت 1-.
arr.includes(item, from) – با شروع از ایندکس from به دنبال item میگردد، اگر پیدا کند true را برمیگرداند.
معمولا این متدها تنها با یک آرگومان استفاده میشوند: المانی (item) که جستجو برای آن انجام میشود. به طور پیشفرض، جستجو از ابتدا انجام میشود.
توجه داشته باشید که متدها از مقایسه === استفاده میکنند. پس اگر ما به دنبال false باشیم، متد دقیقا false را پیدا میکند و نه صفر را.
اگر ما بخواهیم بررسی کنیم که item درون آرایه وجود دارد یا نه و به دنبال ایندکس دقیق نیستیم، پس arr.includes ترجیح داده میشود.
متد arr.lastIndexOf مانند indexOf است اما از راست به چپ جستجو میکند.
let fruits = ['Apple', 'Orange', 'Apple']
alert( arr.indexOf('Apple') ); // 0 (Apple اولین)
alert( arr.lastIndexOf('Apple') ); // 2 (Apple آخرین)
````smart header="متد `includes` مقدار `NaN` را به درستی مدیریت میکند"
یک تفاوت بسیار کوچک `includes` این است که این متد به درستی `NaN` را کنترل میکند، درست برعکس `indexOf`:
```js run
const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (اشتباه است، باید 0 باشد)
alert( arr.includes(NaN) );// true (درست است)
به این دلیل که includes بسیار بعدتر به جاوااسکریپت اضافه شد و از درون از الگوریتمهای مقایسه بروزتری استفاده میکند.
let result = arr.find(function(item, index, array) {
// برگردانده شود، المان برگردانده میشود و حلقهی تکرار متوقف میشود true اگر مقدار
// برگردانده میشود undefined مقدار falsy برای سناریوهای
});
تابع برای المانهای آرایه، یکی پس از دیگری، صدا زده میشود:
item المان است.
index ایندکس آن است.
array خود آرایه است.
اگر true برگرداند، جستجو متوقف میشود، item برگردانده میشود. اگر چیزی پیدا نشود، undefined برگردانده میشود.
برای مثال، ما یک آرایهای از کاربران داریم، که هر کدام دارای id و name هستند. بیایید کاربری که id == 1 داشته باشد را پیدا کنیم:
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
let user = users.find(item => item.id == 1);
alert(user.name); // John
در واقعیت، آرایههایی از شیءها چیز متداولی است، پس متد find بسیار مفید است.
توجه داشته باشید که در مثال بالا ما تابع item => item.id == 1 را همراه با یک آرگومان برای find در نظر گرفتیم. این چیز معمولی است، بقیه آرگومانهای این تابع به ندرت استفاده میشوند.
متد arr.findIndex سینتکس یکسانی دارد اما به جای خود المان ایندکسی که المان در آن پیدا شد را برمیگرداند. اگر چیزی پیدا نشد مقدار 1- برگردانده میشود.
متد arr.findLastIndex مانند findIndex است اما مانند lastIndexOf از راست به چپ جستجو میکند.
اینجا یک مثال داریم:
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"},
{id: 4, name: "John"}
];
// را پیدا کن John ایندکس اولین
alert(users.findIndex(user => user.name == 'John')); // 0
// را پیدا کن John ایندکس آخرین
alert(users.findLastIndex(user => user.name == 'John')); // 3
متد find برای یک (اولین) المان که باعث شود تابع true برگرداند، جستجو میکند.
اگر ممکن باشد تعداد بیشتری موجود باشند، میتوانیم از arr.filter(fn) استفاده کنیم.
سینتکس آن مشابه find است اما filter یک آرایه از المانهای منطبق را برمیگرداند:
let results = arr.filter(function(item, index, array) {
// باشد المان به نتیجهها اضافه میشود و حلقه تکرار ادامه پیدا میکند true اگر
// اگر چیزی پیدا نشود یک آرایه خالی برمیگرداند
});
برای مثال:
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
// آرایه شامل دو کاربر اول را برمیگرداند
let someUsers = users.filter(item => item.id < 3);
alert(someUsers.length); // 2
صدازدن arr.sort() آرایه را در محل با تغییر دادن ترتیب المانها، مرتب میکند.
همچنین این متد آرایه مرتب شده را برمیگرداند، اما همانطور که خود arr تغییر داده میشود، مقدار برگردانده شده معمولا نادیده گرفته میشود.
برای مثال:
let arr = [ 1, 2, 15 ];
// را دوباره ترتیب بندی میکند arr این متد
arr.sort();
alert( arr ); // 1, 15, 2
چیز عجیبی را در نتیجه متوجه شدید؟
ترتیب المانها 1, 15, 2 شد. این اشتباه است. اما چرا؟
المانها به صورت پیشفرض به عنوان رشته مرتب میشوند.
به طور کلی، تمام المانها برای انجام مقایسه به رشته تبدیل میشوند. برای رشتهها، ترتیببندی لفتنامهای اعمال میشود و در این صورت "2" > "15" است.
برای استفاده از ترتیببندی خودمان، ما نیاز داریم که یک تابع را به عنوان آرگومان arr.sort() قرار دهیم.
تابع باید دو مقدار دلخواه را مقایسه کند و چیزی را برگرداند:
function compare(a, b) {
if (a > b) return 1; // اگر مقدار اول بزرگتر از دومی باشد
if (a == b) return 0; // اگر مقدارها برابر باشند
if (a < b) return -1; // اگر مقدار اول کمتر از دومی باشد
}
برای مثال، برای مرتب کردن به عنوان اعداد:
function compareNumeric(a, b) {
if (a > b) return 1;
if (a == b) return 0;
if (a < b) return -1;
}
let arr = [ 1, 2, 15 ];
arr.sort(compareNumeric);
alert(arr); // 1, 2, 15
حالا همانطور که انتظار میرفت کار میکند.
بیایید کمی عقب بمانیم و ببینیم چه چیزی در حال اتفاق افتادن است. arr میتواند آرایهای از هر چیزی باشد نه؟ ممکن است شامل اعداد یا رشتهها یا شیءها یا هرچیز دیگری باشد. ما دستهای از چیزها داریم. برای مرتب کردن آن، ما به یک تابع مرتبکننده که میداند چگونه المانهای دسته را مقایسه کند، نیاز داریم. ترتیب رشتهای پیشفرض است.
متد arr.sort(fn) یک الگوریتم مرتبسازی کلی را پیادهسازی میکند. ما نیازی نداریم که بدانیم درون آن چه اتفاقی میافتد (اکثر اوقات از یک مرتبسازی سریع یا Timsort بهینهشده استفاده میشود). این متد آرایه را طی میکند، المانهای آن را با استفاده از تابع فراهم شده مقایسه میکند و آنها را مرتب میکند، تمام آن چیزی که ما نیاز داریم این است که یک fn فراهم کنیم که مقایسه را انجام دهد.
راستی، اگر ما بخواهیم بدانیم که کدام المانها مقایسه میشوند – چیزی ما را از alert کردن آنها متوقف نمیکند:
[1, -2, 15, 2, 0, 8].sort(function(a, b) {
alert( a + " <> " + b );
return a - b;
});
الگوریتم ممکن است یک المان را با چند المان دیگر در حین فرایند مقایسه کند، اما تلاش میکند که تا جایی که میتواند مقایسههای کمی انجام دهد.
یک تابع مقایسه میتواند هر عددی برگرداند
در واقع یک تابع مقایسه فقط نیاز دارد که یک عدد مثبت را برای اینکه بگوید «بزرگتر» است برگرداند و یک عدد منفی را برای گفتن «کمتر» است.
این ویژگی سبب میشود که تابعهای کوتاهتری نوشته شود:
let arr = [ 1, 2, 15 ];
arr.sort(function(a, b) { return a - b; });
alert(arr); // 1, 2, 15
توابع پیکانی بهتریناند
تابعهای پیکانی را به یاد دارید؟ ما میتوانیم از آنها برای مرتبسازی تمیزتر استفاده کنیم:
arr.sort( (a, b) => a - b );
این کد دقیقا مانند نسخه طولانیتر بالایی کار میکند.
برای رشتهها از localeCompare استفاده کنید
الگوریتم مقایسه رشتهها را به یاد دارید؟ این الگوریتم به صورت پیشفرض حروف را با کدهای آنها مقایسه میکند.
برای بساری از حروف الفبا، بهتر است از متد str.localeCompare برای مرتبکردن صحیح حروف استفاده شود، مانند Ö.
برای مثال، بیایید چند کشور را به زبان آلمانی مرتب کنیم:
let countries = ['Österreich', 'Andorra', 'Vietnam'];
alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (اشتباه است)
alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (درست است!)
یک موقعیت در زندگی واقعی را میگوییم. ما در حال نوشتن یک برنامه پیامرسان هستیم و شخص لیستی از دریافت کنندگان که با کاما جدا شدهاند را وارد میکند: John, Pete, Mary. اما یک آرایهای از اسمها بسیار راحتتر از یک رشته خواهد بود. چگونه آن را دریافت کنیم؟
متد str.split(delim) دقیقا همین کار را انجام میدهد. این متد رشته را با استفاده از جداکنندهی داده شده delim به یک آرایه تقسیم میکند.
در مثال بالا، ما توسط یک کاما که بعد آن space میآید رشته را جدا میکنیم:
let names = 'Bilbo, Gandalf, Nazgul';
let arr = names.split(', ');
for (let name of arr) {
alert( `A message to ${name}.` ); // A message to Bilbo (و بقیه اسمها)
}
متد split یک آرگومان اختیاری دوم هم دارد – یک محدودیت برای طول آرایه. اگر این آرگومان اضافه شود، سپس المانهای دیگر نادیده گرفته میشوند. گرچه در عمل به ندرت استفاده میشود:
صدا زدن split(s) با یک s خالی رشته را به آرایهای از حروف جدا میکند:
let str = "test";
alert( str.split('') ); // t,e,s,t
صدا زدن arr.join(glue) عمل برعکس split را انجام میهد. این متد یک رشته از arr میسازد که توسط glue المانها متصل شدهاند.
برای مثال:
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // آرایه را با استفاده از ; به یک رشته تبدیل کنید
alert( str ); // Bilbo;Gandalf;Nazgul
زمانی که ما نیاز داشته باشیم که در یک آرایه حلقه بزنیم، میتوانیم از forEach، for یا for..of استفاده کنیم.
زمانی که ما نیاز داشته باشیم در المانها حلقه بزنیم و داده را برای هر المان برگردانیم، میتوانیم از map استفاده کنیم.
متدهای arr.reduce و arr.reduceRight همچنین به این دسته تعلق دارند، اما کمی پیچیدهتر هستند. آنها برای محاسبه یک مقدار بر اساس آرایه، استفاده میشوند.
سینتکس اینگونه است:
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
تابع روی تمام المانهای آرایه اعمال میشود و نتیجه خود را به فراخوانی بعدی «منتقل میکند».
آرگومانها:
accumulator – نتیجه قبلی فراخوانی تابع است، دفعه اول با initial برابر است (اگر initial وجود داشته باشد).
item – المان کنونی آرایه است.
index – موقعیت آن است.
array – آرایه است.
همانطور که تابع اعمال میشود، نتیجه فراخوانی قبلی به عنوان آرگومان اول به فراخوانی بعدی منتقل میشود.
بنابراین، اولین آرگومان اساسا همان حافظهای است که نتیجه ترکیب شده تمام فراخوانیهای قبلی را ذخیره کرده است. و در پایان تبدیل به نتیجه reduce میشود.
بنظر پیچیده میآید؟
راحتترین راه برای فهمیدن این قضیه، توسط مثال است.
اینجا ما حاصل جمع یک آرایه را در یک خط میگیریم:
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
تابعی که به reduce داده شد تنها از 2 آرگومان استفاده میکند که معمولا کافی است.
بیایید جزئیات چیزی که در حال انجام است را ببینیم.
در اجرای اول، sum برابر با مقدار initial است (آخرین آرگومان reduce)، که برابر با 0 است، و current اولین المان آرایه است، که برابر با 1 است. پس نتیجه تابع 1 است.
در اجرای دوم، sum = 1، که ما المان دوم آرایه (2) را به آن اضافه و برمیگردانیم.
در اجرای سوم، sum = 3 و ما یک المان دیگر به آن اضافه میکنیم و…
گردش محاسبه:
یا به شکل یک جدول که هر ردیف نشاندهنده یک فراخوانی تابع روی المان بعدی آرایه است:
sum
current
نتیجه
فراخوانی اول
0
1
1
فراخوانی دوم
1
2
3
فراخوانی سوم
3
3
6
فراخوانی چهارم
6
4
10
فراخوانی پنجم
10
5
15
اینجا ما میتوانیم به صورت شفاف ببینیم که نتیجه فراخوانی قبلی به اولین آرگومان فراخوانی بعدی تبدیل میشود.
ما همچنین میتوانیم مقدار اولیه را حذف کنیم:
let arr = [1, 2, 3, 4, 5];
// حذف شد (بدون 0) reduce مقدار اولیه از
let result = arr.reduce((sum, current) => sum + current);
alert( result ); // 15
نتیجه یکسان است. به دلیل اینکه اگر مقدار اولیهای وجود نداشته باشد، سپس reduce اولین المان آرایه را به عنوان مقدار اولیه انتخاب میکند و حلقهزدن را از دومین المان شروع میکند.
جدول محاسبات مانند بالا است، منتها ردیف اول را ندارد.
اما استفاده کردن به این صورت به دقت بسیار بالایی نیاز دارد. اگر آرایه خالی باشد، سپس فراخوانی reduce بدون مقدار اولیه ارور میدهد.
یک مثال اینجا داریم:
let arr = [];
// Error: Reduce of empty array with no initial value
// .آن را برای آرایه خالی برمیگرداند reduce ،اگر مقدار اولیه وجود داشت
arr.reduce((sum, current) => sum + current);
بنابراین توصیه میشود همیشه مقدار اولیه را تعیین کنید.
متد arr.reduceRight کار یکسان را انجام میهد، اما از راست به چپ.
##متد Array.isArray
آرایهها شکل جدیدی از انواع داده را شکل نمیدهند. آنها بر اساس شیءها هستند.
بنابراین typeof برای تشخیص یک شیء ساده از آرایه کمکی نمیکند:
…اما آرایهها به دلیل اینکه اغلب اوقات استفاده میشوند، یک متد خاص برای این کار دارند: Array.isArray(value). این متد اگر value یک آرایه باشد true برمیگرداند و در غیر این صورت false.
تقریبا تمام متدهای آرایه که تابعی را صدا میزنند – مانند find، filter، map، همچنین یک استثنا از sort، پارامتر اختیاری اضافی thisArg را قبول میکنند.
این پارامتر به دلیل اینکه به ندرت استفاده میشود، در قسمتهای بالایی گفته نشد. اما برای کامل بودن ما باید آن را پوشش دهیم.
سینتکس کامل این متدها در زیر آمده است:
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// آرگومان اختیاری آخر است thisArg
مقدار thisArg برای func برابر با this خواهد بود.
برای مثال، اینجا ما از متد شیء army به عنوان یک فیلتر استفاده میکنیم، و thisArg محتوا را رد و بدل میکند:
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30}
];
// برمیگرداند را پیدا کن true به ازای آنها army.canJoin هایی کهuser
let soldiers = users.filter(army.canJoin, army);
alert(soldiers.length); // 2
alert(soldiers[0].age); // 20
alert(soldiers[1].age); // 23
اگر در مثال بالا ما از users.filter(army.canJoin) استفاده میکردیم، سپس army.canJoin به عنوان یک تابع جداگانه صدا زده میشد که this=undefined، بنابراین درجا به یک ارور برمیخوردیم.
صدازدن users.filter(army.canJoin, army) میتواند با users.filter(user => army.canJoin(user)) جایگزین شود، که هردو یکسان هستند. نوع دوم بیشتر استفاده میشود، چون برای اکثر مردم مقداری قابل فهمتر است.
unshift(...items) – المانهایی را به آغاز اضافه میکند.
splice(pos, deleteCount, ...items) – در ایندکس pos به تعداد deleteCount المان حذف و items را اضافه میکند.
slice(start, end) – با ساختن یک آرایه جدید، المانها را از ایندکس start تا end (شامل نمیشود) در آن کپی میکند.
concat(...items) – یک آرایه جدید را برمیگرداند: تمام عضوهای آرایه کنونی را کپی میکند و items را به آن اضافه میکند. اگر هر کدام از items آرایه باشد، سپس المانهای آن اضافه میشوند.
برای جستجو در بین المانها:
indexOf/lastIndexOf(item, pos) – با شروع از موقعیت pos به دنبال item میگردد، ایندکس آن را برمیگرداند و در صورتی که پیدا نشود 1- را برمیگرداند.
includes(value) – اگر آرایه دارای value باشد، مقدار true را برمیگرداند در غیر این صورت false.
find/filter(func) – المانها را از طریق تابع فیلتر میکند، اولین/تمام مقدارهایی که سبب میشوند تابع true برگرداند را برمیگرداند.
findIndex مانند find است اما به جای مقدار ایندکس را برمیگرداند.
برای حلقه زدن در یک آرایه:
forEach(func) – برای تمام المانها تابع func را صدا میزند، چیزی را برنمیگرداند.
برای تغییر شکل یک آرایه:
map(func) – از نتایج صدازدن func برای هر المان، یک آرایه جدید میسازد.
sort(func) – آرایه را در محل مرتب میکند، سپس آن را برمیگرداند.
reverse() – آرایه را در محل برعکس میکند، سپس آن را برمیگرداند.
split/join – یک رشته را به آرایه تبدیل میکند و برعکس.
reduce/reduceRight(func, initial) – با صدا زدن func برای هر المان و رد و بدل کردن یک نتیجه واسطه بین هر فراخوانی، یک مقدار مفرد را در آرایه محاسبه میکند.
علاوه بر این:
Array.isArray(arr) بررسی میکند که arr یک آرایه باشد و اگر بود مقدار true را برمیگرداند در غیر این صورت false.
لطفا در نظر داشته باشید که متدهای sort، reverse و splice خود آرایه را تغییر میدهند.
متدهای ذکر شده بیشترین استفاده را دارند، آنها 99% موارد استفاده را پوشش میدهند. اما چند متد دیگر هم هست:
تابع fn رو تمام المانهای آرایه صدا زده میشود درست شبیه map. اگر تمام نتایج true بود، مقدار true را برمیگرداند، در غیر این صورت false.
این متدها تقریبا شبیه عملگرهای || و && رفتار میکنند: اگر fn مقدار truthy را برگرداند، arr.some() درجا true را برمیگرداند و حلقه زدن روی بقیه المانها را متوقف میکند؛ اگر fn یک مقدار falsy برگرداند، arr.every() فورا false را برمیگرداند و حلقه زدن در بقیه المانها را متوقف میکند.
ما میتوانیم از every برای مقایسه آرایهها استفاده کنیم
arr.copyWithin(target, start, end) – المانهای خود را از موقعیت start تا موقعیت end در خودش و در موقعیت target کپی میکند (جایگزین المان موجود میشود).
با اولین نگاه ممکن است به نظر برسد که متدهای بسیار زیادی وجود دارد و به حافظه سپردن آنها مشکل است. اما در واقع بسیار آسانتر است.
برای داشتن شناخت از آنها به برگه تقلب نگاه بیاندازید. سپس تکلیفهای این فصل را برای تمرین انجام دهید تا نسبت به متدهای آرایه تجربه بدست بیاورید.
پس از آن هر موقع که نیاز داشتید با یک آرایه کاری انجام دهید، و نمیدانید چگونه، به این صفحه بیایید، به برگه تقلب نگاهی بیاندازید و متد مناسب را پیدا کنید. مثالها به شما در نوشتن درست آن کمک میکنند. به زودی به طور خودکار شما متدها را به حافظه میسپارید، بدون تلاش خاصی از جانب خودتان.
یک تابع filterRange(arr, a, b) بنویسید که یک آرایه arr دریافت میکند، به دنبال المانهای بزرگتر یا مساوی a و کوچکتر یا مساوی b میگردد و نتیجه را به شکل آرایه برمیگرداند.
تابع نباید آرایه داده شده را تغییر دهد. باید آرایه جدید را برگرداند.
برای مثال:
let arr = [5, 3, 8, 1];
let filtered = filterRange(arr, 1, 4);
alert( filtered ); // 3,1 (مقدارهای مورد نظر)
alert( arr ); // 5,3,8,1 (تغییر داده نشده)
function filterRange(arr, a, b) {
// برای خوانایی بهتر به دور عبارت، پرانتز اضافه کردیم
return arr.filter(item => (a <= item && item <= b));
}
let arr = [5, 3, 8, 1];
let filtered = filterRange(arr, 1, 4);
alert( filtered ); // 3,1 (مقدارهای مورد نظر)
alert( arr ); // 5,3,8,1 (تغییر داده نشده)
function filterRange(arr, a, b) {
// added brackets around the expression for better readability
return arr.filter(item => (a <= item && item <= b));
}
تابع filterRangeInPlace(arr, a, b) را بنویسید که یک آرایه arr دریافت میکند و تمام مقدارها به جز آنهایی که بین a و b هستند را حذف میکند. آزمایش به این صورت است: a ≤ arr[i] ≤ b.
تابع باید فقط آرایه را تغییر دهد. نباید چیزی را برگرداند.
برای مثال:
let arr = [5, 3, 8, 1];
filterRangeInPlace(arr, 1, 4); // تمام اعداد به جز 1 تا 4 را حذف کردیم
alert( arr ); // [3, 1]
function filterRangeInPlace(arr, a, b) {
for (let i = 0; i < arr.length; i++) {
let val = arr[i];
// اگر بیرون از بازه بود آن را حذف کن
if (val < a || val > b) {
arr.splice(i, 1);
i--;
}
}
}
let arr = [5, 3, 8, 1];
filterRangeInPlace(arr, 1, 4); // تمام اعداد به جز 1 تا 4 را حذف کرد
alert( arr ); // [3, 1]
function filterRangeInPlace(arr, a, b) {
for (let i = 0; i < arr.length; i++) {
let val = arr[i];
// remove if outside of the interval
if (val < a || val > b) {
arr.splice(i, 1);
i--;
}
}
}
یک تابع سازنده Calculator بسازید که شیءهای ماشین حساب «قابل توسعه» میسازد.
این تکلیف از دو بخش تشکیل شده است.
اول، متد calculate(str) را بسازید که یک رشته مانند "1 + 2" را در شکل «عدد عملگر عدد» دریافت میکند و نتیجه را برمیگرداند. این متد باید جمع + و منها - را متوجه شود.
مثالی از کاربرد آن:
let calc = new Calculator;
alert( calc.calculate("3 + 7") ); // 10
سپس متد addMethod(name, func) را اضافه کنید که به ماشین حساب یک عملیات جدید را آموزش میدهد. این متد اسم عملگر name و تابع دو آرگومانی func(a,b) که عملیات را پیادهسازی میکند را دریافت میکند.
برای مثال، بیایید عمل ضرب *، تقسیم / و به توان رساندن ** را اضافه کنیم:
let powerCalc = new Calculator;
powerCalc.addMethod("*", (a, b) => a * b);
powerCalc.addMethod("/", (a, b) => a / b);
powerCalc.addMethod("**", (a, b) => a ** b);
let result = powerCalc.calculate("2 ** 3");
alert( result ); // 8
پرانتز یا عبارات پیچیده در این تکلیف وجود ندارند.
اعداد و عملگر دقیقا به یک فاصله خالی محدود میشوند.
اگر دوست داشته باشید میتوانید مدیریت ارور را هم اضافه کنید.
شما یک آرایه از شیءهای user دارید که هر کدام دارای user.name هستند. کدی بنویسید که آن را به آرایهای از اسمها تبدیل میکند.
برای مثال:
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let users = [ john, pete, mary ];
let names = /* ... کد شما */
alert( names ); // John, Pete, Mary
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let users = [ john, pete, mary ];
let names = users.map(item => item.name);
alert( names ); // John, Pete, Mary
تابع sortByName(users) را بنویسید که آرایهای از شیءهای شامل age دریافت میکند و آنها را بر اساس age مرتب میکند.
برای مثال:
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let arr = [ john, pete, mary ];
sortByName(arr);
// now: [john, mary, pete]
alert(arr[0].name); // Mary
alert(arr[2].name); // Pete
function sortByAge(arr) {
arr.sort((a, b) => a.age - b.age);
}
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let arr = [ john, pete, mary ];
sortByName(arr);
// [john, mary, pete] :حالا آرایه مرتب شده به این شکل است
alert(arr[0].name); // John
alert(arr[2].name); // Pete
ترتیب تمام المانها باید احتمال برابر داشته باشند. برای مثال، [1,2,3] میتواند به شکل [1,2,3] یا [1,3,2] یا [3,1,2] و… مرتب شود که احتمال هر مورد برابر است.
راه حل ساده میتواند اینگونه باشد:
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);
این کد تا حدی کار میکند، چون Math.random() - 0.5 یک عدد تصادفی است که ممکن است مثبت یا منفی باشد، پس تابع مرتبکننده المانها را به صورت تصادفی مرتب میکند.
اما به دلیل اینکه تابع مرتبکننده قرار نیست اینگونه استفاده شود، تمام جابجاییها احتمال برابر ندارند.
برای مثال، کد زیر را در نظر بگیرید. این کد shuffle را 1000000 بار اجرا میکند و تعداد وقوع تمام نتایج ممکن را میشمارد:
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
// شمارش تعداد وقوع تمام جایجاییهای ممکن
let count = {
'123': 0,
'132': 0,
'213': 0,
'231': 0,
'321': 0,
'312': 0
};
for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}
// نمایش شمارش تمام جابجاییهای ممکن
for (let key in count) {
alert(`${key}: ${count[key]}`);
}
میتوانیم جانبداری را به طور واضح ببینیم: 123 و 213 نسبت به بقیه بیشتر رخ میدهند.
نتیجه کد ممکن است بین موتورهای جاوااسکریپت متفاوت باشد، اما میتوانیم به سادگی ببینیم که این شیوه قابل اطمینان نیست.
چرا درست کار نمیکند؟ به طور کلی، sort یک «جعبه سیاه» است: ما یک آرایه و تابع مقایسه را درون آن میاندازیم و توقع داریم که آرایه مرتب شود. اما به دلیل منطق تصادفی مقایسه، جعبه سیاه دیوانه میشود و اینکه چگونه دیوانه میشود بستگی به پیادهسازی دارد که بین موتورها متفاوت است.
راههای خوب دیگری هم برای انجام تکلیف وجود دارد. برای مثال، یک الگوریتم عالی به اسم بُرزدن Fisher-Yates وجود دارد. ایده اینگونه است که آرایه را برعکس بررسی کنیم و هر المان را با یک المان تصادفی قبل خود جابجا کنیم:
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // i ایندکس تصادفی بین 0 تا
// را جابجا کن array[j] و array[i] المانهای
// ما از سینتکس «مقداردهی تخریبساختار» برای انجام آن استفاده میکنیم
// شما درباره سینتکس آن در فصلهای آینده بیشتر یاد میگیرید
// به طور یکسان میتوان نوشت:
// let t = array[i]; array[i] = array[j]; array[j] = t
[array[i], array[j]] = [array[j], array[i]];
}
}
بیایید مانند قبل آن را آزمایش کنیم:
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
// شمارش تعداد وقوع تمام جایجاییهای ممکن
let count = {
'123': 0,
'132': 0,
'213': 0,
'231': 0,
'321': 0,
'312': 0
};
for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}
// نمایش شمارش تمام جابجاییهای ممکن
for (let key in count) {
alert(`${key}: ${count[key]}`);
}
برای هر المان بررسی میکنیم که آیا آرایه حاصل دارای آن المان هست یا نه.
اگر بود، المان را نادیده میگیریم، در غیر این صورت آن را به نتایج اضافه میکنیم.
function unique(arr) {
let result = [];
for (let str of arr) {
if (!result.includes(str)) {
result.push(str);
}
}
return result;
}
let strings = ["Hare", "Krishna", "Hare", "Krishna",
"Krishna", "Krishna", "Hare", "Hare", ":-O"
];
alert( unique(strings) ); // Hare, Krishna, :-O
این کد کار میکند، اما احتمالا یک اشکال عملکردی دارد.
متد result.includes(str) درون آرایه result را بررسی میکند و هر المان را با str مقایسه میکند تا المان مورد نظر را پیدا کند.
ینابراین اگر 100 المان درون result وجود داشته باشد و هیچ کدام با str برابر نباشد، سپس تمام result را بررسی میکند و دقیقا 100 مقایسه انجام میدهد. و اگر result بزرگ باشد، مثلا 10000، سپس به تعداد 10000 مقایسه وجود خواهد داشت.
این به خودی خود مشکل محسوب نمیشود، چون موتورهای جاوااسکریپت بسیار سریع هستند، پس بررسی یک آرایه با 10000 المان چند میکروثانیه طول میکشد.
اما ما در حلقه for چنین آزمایشی را برای هر المان درون arr انجام میدهیم.
پس اگر arr.length برابر با 10000 باشد ما چیزی مثل 10000*10000 = 100 میلیون مقایسه خواهیم داشت. این مقدار بسیار زیاد است.
بنابراین این راه حل تنها برای آرایههای کوچک مناسب است.
اگر پیشنهادی برای بهبود ترجمه دارید - لطفا یک ایشوی گیتهاب یا یک پولریکوئست به جای کامنتگذاشتن باز کنید.
اگر چیزی را در مقاله متوجه نمیشوید – به دقت توضیح دهید.
برای قراردادن یک خط از کد، از تگ <code> استفاده کنید، برای چندین خط – کد را درون تگ <pre> قرار دهید، برای بیش از ده خط کد – از یک جعبهٔ شنی استفاده کنید. (plnkr، jsbin، codepen…)
You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.