diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index 8a38516e1..eea8cbe10 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -1,83 +1,111 @@ - -# Iterables - -*Iterable* objects is a generalization of arrays. That's a concept that allows us to make any object useable in a `for..of` loop. - -Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, strings are also iterable. - -If an object isn't technically an array, but represents a collection (list, set) of something, then `for..of` is a great syntax to loop over it, so let's see how to make it work. - +# المتكررات + +_المتكررات_ +هي تعميم للمصفوفات +(arrays). +وهذا مفهوم يسمح باستخدام أى كائن فى التكرار من نوع +`for..of`. + +إن المصفوفات عباره عن متكررات +(iterables) +بالطبع. ولكن هناك كائنات أخرى موجوده بالفعل والتى هي متكرره أيضا. فعلى سبيل المثال ستجد أن النصوص +(strings) +متكررة أيضا. + +إذا كان الكائن ليس فعليًا عباره عن مصفوفه ولكنها تعرض مجموعة من العناصر +(قائمة أو مجموعه) +إذًا فإن التكرار +`for..of` +هو خيار جيّد للتكرار على هذا الكائن. فهيّا بنا نرى كيف نحقق هذا. ## Symbol.iterator -We can easily grasp the concept of iterables by making one of our own. - -For instance, we have an object that is not an array, but looks suitable for `for..of`. +يمكننا أن نفهم بسهولة مفهوم المتكررات عن طريق إنشاء واحد بنفسنا. -Like a `range` object that represents an interval of numbers: +على سبيل المثال، إذا كان لدينا كائن ولكنه ليس مصفوقة، ولكنه مناسب للتكرار +`for..of` +مثل الكائن +`range` +الذى يعرض مجموعه من الأرقام: ```js let range = { from: 1, - to: 5 + to: 5, }; -// We want the for..of to work: +// نريد أن يعمل التكرار كالآتى: // for(let num of range) ... num=1,2,3,4,5 ``` -To make the `range` iterable (and thus let `for..of` work) we need to add a method to the object named `Symbol.iterator` (a special built-in symbol just for that). - -1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`. -2. Onward, `for..of` works *only with that returned object*. -3. When `for..of` wants the next value, it calls `next()` on that object. -4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` is the next value. - -Here's the full implementation for `range` with remarks: +لنحوّل الكائن +`range` +متكرر (وبالتالى يقوم التكرار `for..of` بعمله) +سنقوم بإضافة دالة إلى الكائن تسمي +`Symbol.iterator` +(رمز موجود بالفعل من أجل هذا). + +1. عندما يبدأ + `for..of`, + فإنه يقوم باستدعاء هذه الدالة مرة واحده (أو تعرض خطأًا إن وُجد). وهذه الدالة يجب أن تقوم بإرجاع _متكرر_ -- أى كائن بالدالة `next`. +2. بعد ذلك, `for..of` تعمل _حيث تعمل مع هذا الكائن الذى تم إرجاعه_. +3. عندما يحتاج التكرار `for..of` القيمة التالية، فستقوم باستدعاء الدالة `next` على هذا الكائن. +4. يجب أن تكون نتيجة استدعاء الدالة `next()` عباره عن الشكل + `{done: Boolean, value: any}`, + وفى حالة أن + `done=true` + فهذا يعني أن التكرار قد انتهي, غير ذلك فيعني أن `value` هو القيمه التالية. + +هنا الكود الكامل للكائن +`range` +ببعض الملاحظات: ```js run let range = { from: 1, - to: 5 + to: 5, }; -// 1. call to for..of initially calls this -range[Symbol.iterator] = function() { - - // ...it returns the iterator object: - // 2. Onward, for..of works only with this iterator, asking it for next values +// 1. عند تشغيل التكرار for..of فهي تقوم باستدعائ هذه الدالة +range[Symbol.iterator] = function () { + // ... وهذه الدالة تقوم بإرجاع الكائن المتكرر: + // 2. بعد ذلك، يعمل التكرار for..of على هذا المتكرر فقط باحثًا عن القيم التالية return { current: this.from, - last: this.to, + last: this.to, - // 3. next() is called on each iteration by the for..of loop + // 3. يتم استدعاء الدالة next() فى كل دورة فى التكرار for..of next() { - // 4. it should return the value as an object {done:.., value :...} + // 4. يجب أن تقوم بإرجاع القيمه على شكل الكائن {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } - } + }, }; }; -// now it works! +// والآن التكرار يعمل! for (let num of range) { alert(num); // 1, then 2, 3, 4, 5 } ``` -Please note the core feature of iterables: separation of concerns. +لاحظ الميزة الأساسيه للمتكررات: الفصل بين المفاهيم +(separation of concerns). -- The `range` itself does not have the `next()` method. -- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and its `next()` generates values for the iteration. +- الكائن + `range` + لا يحتوى بنفسه على الدالة + `next()`. +- بدلًا من ذلك فإن كائنًا آخر ، يدعى "المتكرر" تم إنشائه باستدعاء `range[Symbol.iterator]()`, وتقوم الدالة `next()` بإنشاء القيم للتكرار. -So, the iterator object is separate from the object it iterates over. +ولذلك فإن الكائن المتكرر ليس له علاقة بالكائن الذى يعمل عليه التكرار. -Technically, we may merge them and use `range` itself as the iterator to make the code simpler. +عمليًا، يمكننا أن نجمع بين الكائنين ونستخدم الكائن `range` كمتكرر لتبسيط الكود أكثر. -Like this: +كالآتى: ```js run let range = { @@ -95,7 +123,7 @@ let range = { } else { return { done: true }; } - } + }, }; for (let num of range) { @@ -103,51 +131,54 @@ for (let num of range) { } ``` -Now `range[Symbol.iterator]()` returns the `range` object itself: it has the necessary `next()` method and remembers the current iteration progress in `this.current`. Shorter? Yes. And sometimes that's fine too. +والآن تقوم +`range[Symbol.iterator]()` +تقوم بإرجاع الكائن +`range`: +فهي تحتوى على الدالة `next()` وتقوم باستذكار الدورة الحالية فى `this.current`. أليس هذا أقصر ؟ وفى بعض الأحيان يكون جيدًا أيضًا. -The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. But two parallel for-ofs is a rare thing, even in async scenarios. +إن العيب الوحيد الآن هو أنه من المستحيل أن يكون هناك تكرارين من النوع `for..of` يعملان على نفس الكائن فى نفس الوقت: حيث أن كل التكرارات يتشاركون نفس حالة التكرار -- وهى الكائن نفسه. ولكن وجود تكرارين يعملان فى نفس الوقت على نفس الكائن هي حالة نادرة حتى فى الحالات المتزامنة. -```smart header="Infinite iterators" -Infinite iterators are also possible. For instance, the `range` becomes infinite for `range.to = Infinity`. Or we can make an iterable object that generates an infinite sequence of pseudorandom numbers. Also can be useful. +```smart header="التكرارات الغير منتهية" +التكرارات الغير نتهية ممكنة أيضًا. فعلى سبيل المثال، سيصبح الكائن `range` غير منتهي عندما `range.to = Infinity`. أو يمكننا أن نصنع كائنا متكررًا والذى يقوم بإنشاء تتابعًا غير منتهي من الأرقام العشوائية. ويمكن لهذا أن يكون مفيدأ أيضًا. -There are no limitations on `next`, it can return more and more values, that's normal. +لا يوجد أى حدود للدالة `next` حيث يمكنها أن تقوم بإرجاع المزيد والمزيد من القيم وهذا طبيعي. -Of course, the `for..of` loop over such an iterable would be endless. But we can always stop it using `break`. +إن التكرار من النوع `for..of` على متكرر كهذا سيكون بالطبع غير منتهي. ولكن يمكننا دائما إيقافه باستخدام `break`. ``` +## النصوص كمتكررات -## String is iterable - -Arrays and strings are most widely used built-in iterables. +إن النصوص (strings) والمصفوفات (arrays) هي متكررة بالطبيعة. -For a string, `for..of` loops over its characters: +بالنسبة إلى النص، فإن التكرارات من نوع `for..of` ستقوم بالتكرار على حروف النص: ```js run for (let char of "test") { - // triggers 4 times: once for each character - alert( char ); // t, then e, then s, then t + // يتم استدعؤها 4 مرات، مرة لكل حرف + alert(char); // t, then e, then s, then t } ``` -And it works correctly with surrogate pairs! +وتعمل أيضا بشكل صحيح مع الأشكال! ```js run -let str = '𝒳😂'; +let str = "𝒳😂"; for (let char of str) { - alert( char ); // 𝒳, and then 😂 + alert(char); // 𝒳, and then 😂 } ``` -## Calling an iterator explicitly +## استدعاء المتكرر بوضوح -For deeper understanding let's see how to use an iterator explicitly. +لفهم أعمق، هيا بنا نرى كيف يمكننا استخدام المتكرر صراحةً. -We'll iterate over a string in exactly the same way as `for..of`, but with direct calls. This code creates a string iterator and gets values from it "manually": +سنقوم بالتكرار على نص مثلما تعمل `for..of` ولكن باستدعاءات مباشرة. هذا الكود يقوم بإنشاء نص متكرر ويحصل على قيم بشكل يدوي: ```js run let str = "Hello"; -// does the same as +// تعمل مثل // for (let char of str) alert(char); *!* @@ -157,49 +188,49 @@ let iterator = str[Symbol.iterator](); while (true) { let result = iterator.next(); if (result.done) break; - alert(result.value); // outputs characters one by one + alert(result.value); // تُظهر الحروف واحدًا تلو الآخر } ``` -That is rarely needed, but gives us more control over the process than `for..of`. For instance, we can split the iteration process: iterate a bit, then stop, do something else, and then resume later. +هذا نادرًا ما يحدث، ولكنه يعظينا تحكمًا أكثر بالعملية مقارنة بالتكرار `for..of`. على سبيل المثال، يمكننا فصل عملية التكرار: نكرر قليلًا ثم نقف ثم نفعل شيئًا آخر ثم نستأنف التكرار. -## Iterables and array-likes [#array-like] +## المتكررات وأشباه المصفوفات (array-likes) [#array-like] -There are two official terms that look similar, but are very different. Please make sure you understand them well to avoid the confusion. +يوجد مصطلحان يبدوان شبيهين ولكنهما مختلفين تمامًا. من فضلك تأكد من فهمك لهم حتى لا تتحيّر. -- *Iterables* are objects that implement the `Symbol.iterator` method, as described above. -- *Array-likes* are objects that have indexes and `length`, so they look like arrays. +- _المتكررات_ هي كائنات تستدعى الدالة `Symbol.iterator`، كما أوضحنا سابقًا. +- _أشباه المصفوفات_ هي كائنات أيضًا ولكنها تحتوى على `index` و `length`، وبالتالى فهي تشبه المصفوفة. -When we use JavaScript for practical tasks in browser or other environments, we may meet objects that are iterables or array-likes, or both. +عندما نستخدم الجافاسكريبت لمهام فى المتصفح (browser) أو أى بيئة أخرى، يمكننا أن نجد كائنات متكررة أو شبيهة بالمصفوفه أو كلاهما. -For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`). +على سبيل المثال، ستجد أن النصوص (strings) عبارة عن متكرر (حيث يمكن استخدام `for..of` معها) وكذلك هي شبيهة بالمصفوفه (لأنها تحتوي على `length` و `indexes`). -But an iterable may be not array-like. And vice versa an array-like may be not iterable. +ولكن المتكرر يمكن أن لا يكون شبيهًا بالمصفوفه. والعكس صحيح. -For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`. +على سبيل المثال، فإن الكائن `range` الذى استخدمناه سابقًا هو متكرر ، ولكنه ليس شبيهًا بالمصفوفه لأنها لا تحتوي على `indexes` أو `length`. -And here's the object that is array-like, but not iterable: +ويوجد أيضًا الكائن الذى هو شبيه بالمصفوفة ولكنه ليس متكررًا: ```js run -let arrayLike = { // has indexes and length => array-like +let arrayLike = { // تحتوى على indexes & length إذًا فهي شبيهة بالمصفوفة ولكنها ليست متكررًا 0: "Hello", 1: "World", length: 2 }; *!* -// Error (no Symbol.iterator) +// خطأ (no Symbol.iterator) for (let item of arrayLike) {} */!* ``` -Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array. E.g. we would like to work with `range` using array methods. How to achieve that? +عادة ماتكون المتكررات وأشباه المصفوفات ليست مصفوفة، حيث لا يحتوون على الدوال `push` & `pop` وهكذا. وهذا غير ملائم إذا كنا نريد أن نتعامل مع هذا الكائن كما نتعامل مع المصفوفة. مثلًا: نريد أن نتعامل مع الكائن `range` باستخدام دوال المصفوفه. كيف يمكننا فعل ذلك ؟ ## Array.from -There's a universal method [Array.from](mdn:js/Array/from) that takes an iterable or array-like value and makes a "real" `Array` from it. Then we can call array methods on it. +توجد دالة معروفة [Array.from](mdn:js/Array/from) والتى تستقبل متكررًا أو شبيهًا بالمصفوفه وتقوم بإنشاء مصفوفة حقيقية من هذه القيمة. وبالتالي يمكننا استخدام دوال المصفوفة عليه. -For instance: +على سبيل المثال: ```js run let arrayLike = { @@ -211,41 +242,42 @@ let arrayLike = { *!* let arr = Array.from(arrayLike); // (*) */!* -alert(arr.pop()); // World (method works) +alert(arr.pop()); // World ``` -`Array.from` at the line `(*)` takes the object, examines it for being an iterable or array-like, then makes a new array and copies all items to it. +إن الدالة `Array.from` عند السطر `(*)` تستقبل كائنًا، وترى إن كان متكررًا أو شبيهًا بالمصفوفة، ثم تصنع مصفوفة جديدة وتنسخ جميع العناصر إلى هذه المصفوفة الجديدة. -The same happens for an iterable: +وهذا مايحدث أيضا للمتكرر: ```js -// assuming that range is taken from the example above +// على فرض أن الكائن range مأخوذ من المثال السابق let arr = Array.from(range); -alert(arr); // 1,2,3,4,5 (array toString conversion works) +alert(arr); // 1,2,3,4,5 (يحدث التحويل من مصفوفة إلى نص باستخدام toString) ``` -The full syntax for `Array.from` also allows us to provide an optional "mapping" function: +البناء الكامل للدالة `Array.from` يسمح لنا بأن نضيف دالة اختياريًا تقوم بالتكرار عل عناصر المصفوفة الجديدة: + ```js Array.from(obj[, mapFn, thisArg]) ``` -The optional second argument `mapFn` can be a function that will be applied to each element before adding it to the array, and `thisArg` allows us to set `this` for it. +المتغير الثاني الإفتراضي الذي تستقبله الدالة `Array.from` `mapFn` يمكن أن يكون دالة تعمل على كل عنصر قبل إضافته للمصفوفة الجديدة، والمتغير `thisArg` يتيح لنا بأن نحدد قيمة `this` للعنصر. -For instance: +على سبيل المثال: ```js -// assuming that range is taken from the example above +// على فرض أن الكائن range مأخوذ من المثال السابق -// square each number -let arr = Array.from(range, num => num * num); +// تربيع كل رقم +let arr = Array.from(range, (num) => num * num); alert(arr); // 1,4,9,16,25 ``` -Here we use `Array.from` to turn a string into an array of characters: +فى هذا المثال استخدمنا `Array.from` لتقوم بتحويل النص إلى مصفوفة من الحروف: ```js run -let str = '𝒳😂'; +let str = "𝒳😂"; // splits str into array of characters let chars = Array.from(str); @@ -255,12 +287,12 @@ alert(chars[1]); // 😂 alert(chars.length); // 2 ``` -Unlike `str.split`, it relies on the iterable nature of the string and so, just like `for..of`, correctly works with surrogate pairs. +على عكس `str.split` ، حيث تعتمد على الطبيعة المتكررة للنص، مثل `for..of` تمامًا، وتعمل جيدًا مع الأشكال. -Technically here it does the same as: +وهى تعمل هنا عمليًا كهذا المثال: ```js run -let str = '𝒳😂'; +let str = "𝒳😂"; let chars = []; // Array.from internally does the same loop for (let char of str) { @@ -270,38 +302,38 @@ for (let char of str) { alert(chars); ``` -...But it is shorter. +...ولكن هذا أقصر. -We can even build surrogate-aware `slice` on it: +حتى أنه يمكننا أن نبنى دالة `slice` متوافقة مع الأشكال أيضًا: ```js run function slice(str, start, end) { - return Array.from(str).slice(start, end).join(''); + return Array.from(str).slice(start, end).join(""); } -let str = '𝒳😂𩷶'; +let str = "𝒳😂𩷶"; -alert( slice(str, 1, 3) ); // 😂𩷶 +alert(slice(str, 1, 3)); // 😂𩷶 -// the native method does not support surrogate pairs -alert( str.slice(1, 3) ); // garbage (two pieces from different surrogate pairs) +// الدالة الأساسية لا تدعم الأشكال +alert(str.slice(1, 3)); // قطعة من كل شكل! ``` +## الملخص -## Summary - -Objects that can be used in `for..of` are called *iterable*. +إن الكائنات التى يمكن استخدامها فى التكرار `for..of` تدعى _متكررات_. -- Technically, iterables must implement the method named `Symbol.iterator`. - - The result of `obj[Symbol.iterator]` is called an *iterator*. It handles the further iteration process. - - An iterator must have the method named `next()` that returns an object `{done: Boolean, value: any}`, here `done:true` denotes the end of the iteration process, otherwise the `value` is the next value. -- The `Symbol.iterator` method is called automatically by `for..of`, but we also can do it directly. -- Built-in iterables like strings or arrays, also implement `Symbol.iterator`. -- String iterator knows about surrogate pairs. +- فعليًا، يجب أن تحتوى المتكررات على دالة تسمى `Symbol.iterator`. + - إن نتيجة استدعاء `obj[Symbol.iterator]` يسمى _متكررًا_. وهى تقوم بالتعامل مع عملية التكرار. + - إن المتكرر يجب أن يحتزى على دالة تسمي `next()` والتى تقوم بإرجاع كائن على الشكل `{done: Boolean, value: any}`, وإذا كانت `done:true` فهذا يعني توقف التكرار، غير ذلك فإن الخاصية `value` تحتوى على القيمة التالية. +- إن الدالة `Symbol.iterator` يتم استدعاؤها تلقائيًا عن طريق `for..of`، ولكن يمكننا أيضًا أن نفعله مباشرةً. +- إن المتكررات الموجودة بالفعل مثل النصوص والمصفوفات تقوم أيضًا باستدعاء الدالة `Symbol.iterator`. +- النص المتكرر يدعم الأشكال. +إن الكائنات التى تحتوى على `indexes` & `length` تسمي _أشباه المصفوفات_. هذه الكائنات يمكنها ان تحتوى أيضًا على خصائص ودوالٍ أخرى ولكنها لا تحتوى على دوال المصفوفات مثل `push` & `pop`. -Objects that have indexed properties and `length` are called *array-like*. Such objects may also have other properties and methods, but lack the built-in methods of arrays. +إذا نظرنا إلى المصدر -- سنجد أن أغلب الدوال الموجوده بالفعل تفترض أن أنها تعمل مع متكررات أو أشباه مصفوفات بدلًا من مصفوفات، لأن هذا أكثر اختصارًا. -If we look inside the specification -- we'll see that most built-in methods assume that they work with iterables or array-likes instead of "real" arrays, because that's more abstract. +إن الدالة `Array.from(obj[, mapFn, thisArg])` تصنع مصفوفة حقيقية من متكرر أو شبيهٍ بالمصفوفة، وبالتالى يمكننا استخدام دوال المصفوفات عليهم. حيث أن المتغيرات `mapFn` & `thisArg` تتيح لنا أن ننفذ دالة على كل عنصر قبل إضافته للمصفوفة. `Array.from(obj[, mapFn, thisArg])` makes a real `Array` of an iterable or array-like `obj`, and we can then use array methods on it. The optional arguments `mapFn` and `thisArg` allow us to apply a function to each item. diff --git a/1-js/05-data-types/index.md b/1-js/05-data-types/index.md index 246e2bc91..102e9380a 100644 --- a/1-js/05-data-types/index.md +++ b/1-js/05-data-types/index.md @@ -1,3 +1,3 @@ -# Data types +# أنواع البيانات -More data structures and more in-depth study of the types. +تعرف على أنواع البيانات وأشكالها.