(3) Multi-Dimensional List

🧱 القوائم المتعددة الأبعاد (Multi-Dimensional List) في Dart

🔹 وتُعرف أيضًا بـ القوائم المتداخلة أو المصفوفات الثنائية.

✅ ما هي Multi-Dimensional List؟

هي قائمة تحتوي على قوائم بداخلها.

مثال:

List<List<int>> matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
JavaScript

↪ هذا يمثل “مصفوفة” 3 صفوف × 3 أعمدة.

🧠 لماذا نستخدمها؟
  • تمثيل الجداول.
  • تمثيل بيانات الصفوف والأعمدة (مثل قواعد البيانات).
  • إنشاء ألعاب شبكية (مثل Tic-Tac-Toe).
  • بيانات الطلاب والدرجات، إلخ…
🔹 كيفية إنشاء قائمة ثنائية الأبعاد:
إما مباشرة:
List<List<String>> names = [
  ['Ali', 'Sara'],
  ['Omar', 'Lina'],
];
JavaScript
أو ديناميكيًا:
List<List<int>> grid = List.generate(3, (i) => List.generate(3, (j) => 0));
JavaScript

⬅ ينشئ مصفوفة 3×3 مملوءة بالأصفار.

🟦 الوصول إلى عنصر داخل القائمة المتداخلة
print(matrix[0][1]);  // النتيجة: 2
JavaScript

matrix[0] تعني الصف الأول
[1] تعني العنصر الثاني من هذا الصف

🔁 التكرار على القوائم المتعددة الأبعاد
for (int i = 0; i < matrix.length; i++) {
  for (int j = 0; j < matrix[i].length; j++) {
    print('Element at [$i][$j] = ${matrix[i][j]}');
  }
}
JavaScript
🔄 باستخدام for-in:
for (var row in matrix) {
  for (var value in row) {
    print(value);
  }
}
JavaScript
✍️ مثال تطبيقي
جدول درجات الطلاب
List<List<int>> grades = [
  [90, 85, 88],   // درجات الطالب 1
  [70, 75, 72],   // الطالب 2
  [100, 98, 99],  // الطالب 3
];

// طباعة متوسط درجات كل طالب
for (int i = 0; i < grades.length; i++) {
  double avg = grades[i].reduce((a, b) => a + b) / grades[i].length;
  print('Student ${i + 1} average: $avg');
}
JavaScript
✳️ استبدال عنصر داخل القائمة المتعددة
matrix[2][1] = 100;  // استبدال العنصر في الصف الثالث والعمود الثاني
JavaScript
🟡 هل يمكن أن تكون ثلاثية أو أكثر؟

نعم، يمكن أن تكون قائمة من قائمة من قائمة:

List<List<List<int>>> cube = [
  [
    [1, 2],
    [3, 4]
  ],
  [
    [5, 6],
    [7, 8]
  ]
];
JavaScript
✅ ملخص سريع
المفهومالمثال
إنشاء قائمة 2DList<List<int>> matrix = [...]
الوصول لعنصرmatrix[row][col]
تعديل عنصرmatrix[1][2] = 99
التكرار داخل 2D Listfor داخل for
إنشاء ديناميكيList.generate(...)
🔁 Spread Operator في Dart

وهو جزء مهم جدًا عند التعامل مع القوائم (Lists)، وخصوصًا عند دمج القوائم أو نسخ عناصرها.

✅ ما هو الـ Spread Operator؟

هو الرمز: ...

يُستخدم لنسخ جميع عناصر قائمة داخل قائمة أخرى.

🟢 مثال بسيط:
List<int> list1 = [1, 2, 3];
List<int> list2 = [4, 5, 6];

List<int> combined = [...list1, ...list2];

print(combined);  // [1, 2, 3, 4, 5, 6]
JavaScript
📌 بدون spread:
List<int> combined = [list1, list2];  // ❌ هذا ينشئ قائمة داخل قائمة
print(combined);  // [[1, 2, 3], [4, 5, 6]]

******************************************

List<int> combined = [];
combined.addAll(list1);
combined.addAll(list2);
print(combined); // [1, 2, 3, 4, 5, 6]
JavaScript
🟡 الشرطية مع spread (Null-Aware Spread)

إذا كانت القائمة قد تكون null، نستخدم ...?

List<int>? optionalList = null;

List<int> result = [0, ...?optionalList, 100];

print(result);  // [0, 100] بدون خطأ

//////////////////////////////////////////////

List<int> combined = [];
combined.addAll(list1);
combined.addAll(list2);
print(combined); // [1, 2, 3, 4, 5, 6]
JavaScript

لو استعملت ...optionalList بدون ?، سيحدث خطأ إذا كانت null.

🧠 استخدام عملي: إنشاء قائمة جديدة من قائمة مع إضافة عناصر
List<String> base = ['PHP', 'Dart'];

List<String> withNew = [...base, 'JavaScript'];

print(withNew);  // ['PHP', 'Dart', 'JavaScript']
JavaScript
🛠️ استخدام مع الشروط (Collection if)
bool addJS = true;

List<String> langs = [
  'PHP',
  'Dart',
  if (addJS) ...['JavaScript', 'TypeScript']
];

print(langs);  // ['PHP', 'Dart', 'JavaScript', 'TypeScript']
JavaScript
🔄 مقارنة النسخ اليدوي مقابل spread
✅ الطريقة اليدوية:
List<int> a = [1, 2, 3];
List<int> b = [];

for (var x in a) {
  b.add(x);
}
JavaScript
✅ مع spread:
List<int> b = [...a];
JavaScript

أسرع وأوضح!

✅ ملخص
الاستخدامالمثال
دمج قائمتين[...]
إضافة عناصر من قائمة إلى أخرىnewList = [...list1, 'item']
التعامل مع null...?list
مع شرطif (condition) ...[items]
🔍 دالة where() في Dart
✅ ما هي where()؟

هي دالة تُستخدم مع القوائم (List) و المجموعات (Set) و الخرائط (Map) لتصفية العناصر حسب شرط معين.

ترجع where() كائنًا من النوع Iterable يحتوي على العناصر التي تنطبق عليها الشروط.

✅ الصيغة العامة:
list.where((element) => شرط)
JavaScript
  • ترجع قائمة جديدة تحتوي فقط على العناصر التي تحقق الشرط.
  • لا تؤثر على القائمة الأصلية.
  • يمكن تحويل الناتج إلى List باستخدام .toList() إذا أردت استخدامه كقائمة عادية.
🧠 فوائد where():
الفائدةالشرح
🔎 التصفيةاستخراج عناصر مطابقة لشرط معين
✅ تسهيل القراءةأكثر وضوحًا من for مع if
🔁 قابلة للسلسلةيمكن دمجها مع map() أو reduce()
📋 لا تعدل الأصلتُعيد نسخة مصفاة دون تغيير القائمة الأصلية
🟢 أين تُستخدم؟
  • تصفية أرقام أو أسماء.
  • البحث عن عناصر تحقق شروط.
  • التعامل مع بيانات الطلاب، المنتجات، الخ…
  • تقليص النتائج أو تحليل البيانات.
✳️ مثال 1: تصفية الأرقام الزوجية
List<int> numbers = [1, 2, 3, 4, 5, 6];

var evens = numbers.where((n) => n % 2 == 0).toList();

print(evens);  // [2, 4, 6]
JavaScript
✳️ مثال 2: تصفية أسماء تبدأ بحرف معيّن
List<String> names = ['Ali', 'Sara', 'Omar', 'Salma'];

var sNames = names.where((name) => name.startsWith('S')).toList();

print(sNames);  // [Sara, Salma]
JavaScript
✳️ مثال 3: تصفية درجات النجاح فقط
List<int> grades = [45, 75, 60, 30, 80];

var passed = grades.where((grade) => grade >= 50).toList();

print(passed);  // [75, 60, 80]
JavaScript
✳️ مثال 4: تصفية منتجات غالية الثمن
List<Map<String, dynamic>> products = [
  {'name': 'Phone', 'price': 500},
  {'name': 'Watch', 'price': 150},
  {'name': 'Laptop', 'price': 1000},
];

var expensive = products.where((p) => p['price'] > 400).toList();

print(expensive);
// [{'name': 'Phone', 'price': 500}, {'name': 'Laptop', 'price': 1000}]
JavaScript
⚠️ ملاحظة مهمة:
  • where() تُعيد Iterable، وليس List مباشرة.
  • استخدم .toList() لتحويل النتيجة إلى List عند الحاجة.
✅ ملخص سريع
السؤالالجواب
ماذا تفعل؟تُرجع عناصر مطابقة لشرط
هل تغير القائمة الأصلية؟❌ لا
هل يجب تحويل الناتج إلى List؟يفضل: toList()
هل يمكن استخدامها مع map أو reduce؟✅ نعم
🔍 شرح مفصل لـ:
  • where()
  • firstWhere()
  • whereType<T>()

مع أمثلة لكل واحدة وتوضيح متى نستخدم كل واحدة منها.

🟢 1. where()
✅ ما تفعل:

ترجع جميع العناصر التي تحقق شرطًا معينًا.

✅ النوع الذي تُرجعه:

Iterable<T> → ويمكن تحويله إلى List باستخدام .toList().

📌 مثال 1: تصفية الأعداد الزوجية
List<int> numbers = [1, 2, 3, 4, 5, 6];

var evens = numbers.where((n) => n % 2 == 0).toList();

print(evens); // [2, 4, 6]
JavaScript
📌 مثال 2: تصفية أسماء تبدأ بحرف “A”
List<String> names = ['Ali', 'Omar', 'Ahmed', 'Sara'];

var aNames = names.where((name) => name.startsWith('A')).toList();

print(aNames); // [Ali, Ahmed]
JavaScript
✅ متى نستخدم where()؟

عندما نريد كل العناصر التي تحقق شرطًا معينًا.

🟢 2. firstWhere()
✅ ما تفعل:

ترجع أول عنصر فقط يحقق شرطًا معينًا.
لو لم يوجد، يمكن إرجاع قيمة افتراضية.

✅ النوع الذي تُرجعه:

T → قيمة مفردة من نفس نوع العناصر.

📌 مثال 1: أول رقم أكبر من 10
List<int> numbers = [3, 7, 15, 20];

int first = numbers.firstWhere((n) => n > 10);

print(first); // 15
JavaScript
📌 مثال 2: استخدام قيمة افتراضية إذا لم يوجد شيء
List<String> names = ['Sara', 'Lina'];

String result = names.firstWhere(
  (name) => name.startsWith('Z'),
  orElse: () => 'Not Found',
);

print(result); // Not Found
JavaScript
✅ متى نستخدم firstWhere()؟

عندما نريد عنصرًا واحدًا فقط يطابق الشرط (غالبًا أول عنصر).

🟢 3. whereType<T>()
✅ ما تفعل:

تُرجع العناصر التي تنتمي لنوع معيّن فقط من القائمة.

✅ مفيدة عند التعامل مع قائمة تحتوي أنواع مختلفة.
📌 مثال: استخراج الأعداد فقط من قائمة مختلطة
List<dynamic> mixed = [1, 'hello', true, 2.5, 42];

var onlyInts = mixed.whereType<int>().toList();

print(onlyInts); // [1, 42]
JavaScript
📌 مثال: استخراج السلاسل النصية فقط
List<dynamic> mixed = ['Hi', 100, 'World', false];

var strings = mixed.whereType<String>().toList();

print(strings); // ['Hi', 'World']
JavaScript
✅ متى نستخدم whereType<T>()؟

عندما تكون قائمتك تحتوي على أنواع بيانات متعددة وتريد استخراج نوع محدد فقط (مثل فقط النصوص، أو الأرقام).

🟡 ملخص المقارنة:
الدالةما تُرجعهتُستخدم عندما…
where()Iterable<T>تريد كل العناصر التي تحقق شرطًا
firstWhere()T (عنصر واحد)تريد أول عنصر فقط يحقق شرطًا
whereType<T>()Iterable<T>تريد تصفية حسب النوع فقط