Dart: مجموعات Dart — عمليات List / Set / Map عملياً
آخر تحديث: 2026-08-26
المجموعات هي جيش البيانات — قوائم مرتبة، مجموعات مفهرسة بالتجزئة بدون تكرار، خرائط مفهرسة بالمفتاح. اجمعها وأصبحت لا تُقهر.
1. ما ستتعلمه
- القائمة (List): الإنشاء / المرور / الفرز / النشر (
...) وعناصر collection-if/for - المجموعة (Set): إزالة التكرار وعمليات المجموعة (التقاطع / الاتحاد / الفرق)
- الخريطة (Map): عمليات المفتاح-القيمة والتكرار
- استنتاج نوع المجموعات والمجموعات الثابتة (const)
- سيناريو بوب: تجميع ملايين الطلبات وتجميع إجمالياتها
2. قصة مطور حقيقي
(1) نقطة الألم: التجميع اليدوي والحسابات الإجمالية مستهلك للوقت وعرضة للخطأ
يحتاج بوب إلى تجميع 1,200,000 طلب حسب الفئة، مع حساب إجمالي الإيرادات وعدد الطلبات لكل فئة. كتب منطق التجميع يدوياً بحلقة for، في 50 سطراً من الكود، لكن واجه مشاكل مثل استبدال المفتاح المكرر والفرز غير المستقر. استغرق معالجة 1,200,000 سجل 8 ثوانٍ.
(2) الحل: عمليات المجموعات
عمليات المجموعات في Dart مع الدوال عالية الرتبة تُختصر التجميع والحسابات إلى 10 أسطر فقط من الكود، مع تحسن في السرعة بنسبة 40%.
// تجميع الطلبات حسب الفئة وحساب الإجمالي
final byCategory = <String, List<Order>>{};
for (final order in orders) {
byCategory.putIfAbsent(order.category, () => []).add(order);
}
// أو استخدام fold للتجميع
final revenueByCategory = orders.fold<Map<String, double>>(
{}, (acc, o) => acc..update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount));
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
(3) الفوائد
- تقليل حجم الكود بنسبة 80%، مع منطق أكثر وضوحاً
- إزالة التكرار في المجموعة تتجنب البيانات المكررة، وتجميع الخريطة يحل محل الفهرسة اليدوية
- النشر و collection-if يجعلان بناء المجموعات أكثر تصريحية
3. القائمة (List)
(1) الإنشاء والعمليات الأساسية
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: إنشاء القائمة
void main() {
// إنشاء حرفي
List<int> counts = [1, 2, 3, 4, 5];
var amounts = <double>[1500.0, 3200.0, 890.0];
// قائمة قابلة للنمو
var orders = <String>['ORD-001', 'ORD-002'];
orders.add('ORD-003');
// قائمة بطول ثابت
var buffer = List<double>.filled(5, 0.0);
buffer[0] = 100.0;
// توليد قائمة
var batch = List.generate(10, (i) => 'Batch-${i + 1}');
print(counts); // [1, 2, 3, 4, 5]
print(orders); // [ORD-001, ORD-002, ORD-003]
print(buffer); // [100.0, 0.0, 0.0, 0.0, 0.0]
print(batch); // [Batch-1, ..., Batch-10]
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: مرور القائمة وفرزها
void main() {
var amounts = [1500.0, 890.0, 3200.0, 50.0];
// فرز في المكان
amounts.sort();
print(amounts); // [50.0, 890.0, 1500.0, 3200.0]
// فرز تنازلي (إنشاء قائمة جديدة)
var descending = [...amounts]..sort((a, b) => b.compareTo(a));
print(descending); // [3200.0, 1500.0, 890.0, 50.0]
// الوصول إلى العناصر
print(amounts.first); // 50.0
print(amounts.last); // 3200.0
print(amounts[2]); // 1500.0
// قائمة فرعية
print(amounts.sublist(1, 3)); // [890.0, 1500.0]
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: النشر و collection-if/for
void main() {
var base = ['ORD-001', 'ORD-002'];
var extra = ['ORD-003', 'ORD-004'];
// عامل النشر
var combined = [...base, ...extra];
print(combined); // [ORD-001, ORD-002, ORD-003, ORD-004]
// النشر مع التعامل مع null
List<String>? maybeList;
var safe = ['header', ...?maybeList, 'footer'];
print(safe); // [header, footer]
// collection-if
bool includePremium = true;
var tiers = [
'Standard',
if (includePremium) 'Premium',
'Enterprise',
];
print(tiers); // [Standard, Premium, Enterprise]
// collection-for
var batch = [
for (int i = 1; i <= 3; i++) 'Batch-$i',
];
print(batch); // [Batch-1, Batch-2, Batch-3]
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
| العملية | الصيغة | الوصف |
|---|---|---|
| النشر | ...list |
ينشر العناصر |
| النشر مع فارغ | ...?list |
ينشر قائمة قابلة للـ فارغ |
| collection-if | if (cond) expr |
تضمين مشروط |
| collection-for | for (var x in list) expr |
توليد بحلقة |
4. المجموعة (Set)
(1) الإنشاء وعمليات المجموعة
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: إزالة التكرار وعمليات المجموعة
void main() {
// الإنشاء من قائمة (يزيل التكرارات)
var allOrders = ['ORD-001', 'ORD-002', 'ORD-001', 'ORD-003'];
var uniqueOrders = <String>{...allOrders};
print(uniqueOrders); // {ORD-001, ORD-002, ORD-003}
// عمليات المجموعة
var setA = {1, 2, 3, 4, 5};
var setB = {4, 5, 6, 7, 8};
print(setA.intersection(setB)); // {4, 5} - التقاطع
print(setA.union(setB)); // {1, 2, 3, 4, 5, 6, 7, 8} - الاتحاد
print(setA.difference(setB)); // {1, 2, 3} - الفرق
// اختبار العضوية
print(setA.contains(3)); // true
print(setA.containsAll({1, 2})); // true
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: تطبيق المجموعة في سيناريوهات إزالة التكرار
void main() {
// إزالة تكرار الفئات من قائمة الطلبات
var orders = [
{'id': 'ORD-001', 'category': 'Electronics'},
{'id': 'ORD-002', 'category': 'Books'},
{'id': 'ORD-003', 'category': 'Electronics'},
{'id': 'ORD-004', 'category': 'Clothing'},
{'id': 'ORD-005', 'category': 'Books'},
];
var categories = orders.map((o) => o['category'] as String).toSet();
print(categories); // {Electronics, Books, Clothing}
print('Unique categories: ${categories.length}'); // 3
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
| العملية | الطريقة | النتيجة |
|---|---|---|
| التقاطع | a.intersection(b) |
العناصر المشتركة |
| الاتحاد | a.union(b) |
جميع العناصر |
| الفرق | a.difference(b) |
عناصر a غير الموجودة في b |
| الاحتواء | a.contains(e) |
bool |
| احتواء الكل | a.containsAll(b) |
bool |
5. الخريطة (Map)
(1) الإنشاء والعمليات
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: إنشاء الخريطة والعمليات الأساسية
void main() {
// إنشاء حرفي
var config = <String, dynamic>{
'appName': 'DataPipeline',
'maxRecords': 1000000,
'taxRate': 0.08,
'verbose': true,
};
// الوصول إلى القيم
print(config['appName']); // DataPipeline
print(config['missing']); // null
print(config['missing'] ?? 'N/A'); // N/A
// إضافة/تحديث الإدخالات
config['outputPath'] = '/tmp/reports'; // إضافة
config['taxRate'] = 0.10; // تحديث
// تحديث آمن
config.update('maxRecords', (v) => v * 2, ifAbsent: () => 500000);
// التكرار
config.forEach((key, value) => print(' $key: $value'));
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: تكرار الخريطة والتجميع
void main() {
var orders = [
{'id': 'ORD-001', 'category': 'Electronics', 'amount': 1500.0},
{'id': 'ORD-002', 'category': 'Books', 'amount': 50.0},
{'id': 'ORD-003', 'category': 'Electronics', 'amount': 3200.0},
{'id': 'ORD-004', 'category': 'Clothing', 'amount': 890.0},
{'id': 'ORD-005', 'category': 'Books', 'amount': 120.0},
];
// التجميع حسب الفئة
final byCategory = <String, List<Map<String, dynamic>>>{};
for (final order in orders) {
final cat = order['category'] as String;
byCategory.putIfAbsent(cat, () => []).add(order);
}
// تجميع الإيرادات حسب الفئة
final revenue = <String, double>{};
for (final entry in byCategory.entries) {
final total = entry.value.fold<double>(
0, (sum, o) => sum + (o['amount'] as double));
revenue[entry.key] = total;
}
// طباعة النتائج
revenue.forEach((cat, total) =>
print('$cat: \$${total.toStringAsFixed(2)} USD'));
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
6. استنتاج نوع المجموعات والمجموعات الثابتة (const)
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: استنتاج النوع والمجموعات الثابتة
void main() {
// استنتاج النوع
var list1 = [1, 2, 3]; // List<int>
var list2 = [1, 2, 3.0]; // List<num> (مزيج من int و double)
var list3 = <String>[]; // نوع عام صريح للقائمة الفارغة
// مجموعات ثابتة - ثابتة بعمق
const categories = ['Electronics', 'Books', 'Clothing'];
const config = <String, int>{'batchSize': 10000, 'timeout': 30};
const flags = {true, false};
// categories.add('Sports'); // خطأ! لا يمكن تعديل const
// const داخل الفئة
const defaultFormats = ['json', 'csv', 'html'];
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
| الميزة | var | final | const |
|---|---|---|---|
| قابلية تعديل المحتوى | نعم | نعم (مرجع ثابت) | لا |
| استنتاج النوع | تلقائي | تلقائي | تلقائي |
| ثابت وقت الترجمة | لا | لا | نعم |
7. سيناريو بوب: تجميع ملايين الطلبات وحساب إجمالياتها
flowchart LR A[قائمة: 1.2 مليون طلب] --> B[where: تصفية] B --> C[groupBy: الفئة] C --> D[Map: الفئة ← الطلبات] D --> E[fold: تجميع] E --> F[الإخراج: الإحصائيات]
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
▶ مثال
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
: التجميع والحساب الإجمالي عملياً
class Order {
final String id;
final String category;
final double amount;
final String status;
Order({required this.id, required this.category, required this.amount, required this.status});
}
Map<String, Map<String, dynamic>> analyzeOrders(List<Order> orders) {
// تصفية الطلبات المكتملة
final completed = orders.where((o) => o.status == 'completed').toList();
// التجميع حسب الفئة
final grouped = <String, List<Order>>{};
for (final order in completed) {
grouped.putIfAbsent(order.category, () => []).add(order);
}
// تجميع الإجماليات لكل فئة
final result = <String, Map<String, dynamic>>{};
for (final entry in grouped.entries) {
final total = entry.value.fold<double>(0, (s, o) => s + o.amount);
final avg = total / entry.value.length;
result[entry.key] = {
'count': entry.value.length,
'total': total,
'average': avg,
};
}
return result;
}
void main() {
final orders = [
Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed'),
Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed'),
Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending'),
Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed'),
Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed'),
];
final analysis = analyzeOrders(orders);
print('=== Category Analysis ===');
for (final entry in analysis.entries) {
final stats = entry.value;
print('${entry.key}:');
print(' Orders: ${stats['count']}');
print(' Total: \$${(stats['total'] as double).toStringAsFixed(2)} USD');
print(' Average: \$${(stats['average'] as double).toStringAsFixed(2)} USD');
}
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
8. المثال الكامل: محلل مجموعات خط أنابيب البيانات
// ============================================
// محلل مجموعات خط أنابيب البيانات
// عرض كامل لعمليات List و Set و Map
// ============================================
class Order {
final String id;
final String category;
final double amount;
final String status;
final String region;
const Order({
required this.id,
required this.category,
required this.amount,
required this.status,
this.region = 'US',
});
}
class OrderAnalyzer {
final List<Order> orders;
OrderAnalyzer(this.orders);
// الفئات الفريدة (Set)
Set<String> get categories => orders.map((o) => o.category).toSet();
// المناطق الفريدة (Set)
Set<String> get regions => orders.map((o) => o.region).toSet();
// الطلبات المكتملة (List filter)
List<Order> get completed =>
orders.where((o) => o.status == 'completed').toList();
// إجمالي الإيرادات (fold)
double get totalRevenue =>
completed.fold(0.0, (sum, o) => sum + o.amount);
// الإيرادات حسب الفئة (Map grouping)
Map<String, double> get revenueByCategory {
return completed.fold<Map<String, double>>({}, (acc, o) {
acc.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
return acc;
});
}
// عدد الطلبات حسب المنطقة والحالة
Map<String, Map<String, int>> get countByRegionStatus {
final result = <String, Map<String, int>>{};
for (final order in orders) {
result.putIfAbsent(order.region, () => {});
result[order.region]!.update(
order.status, (v) => v + 1, ifAbsent: () => 1);
}
return result;
}
// أعلى N فئات حسب الإيرادات
List<MapEntry<String, double>> topCategories(int n) {
final sorted = revenueByCategory.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
return sorted.take(n).toList();
}
void printReport() {
print('=== DataPipeline Analytics Report ===');
print('Total orders: ${orders.length}');
print('Completed: ${completed.length}');
print('Unique categories: ${categories.length}');
print('Regions: ${regions}');
print('Total revenue: \$${totalRevenue.toStringAsFixed(2)} USD');
print('\n--- Revenue by Category ---');
for (final entry in revenueByCategory.entries) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
print('\n--- Top Categories ---');
for (final entry in topCategories(3)) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
print('\n--- Orders by Region & Status ---');
for (final region in countByRegionStatus.entries) {
print(' ${region.key}: ${region.value}');
}
}
}
void main() {
final orders = [
Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed', region: 'US'),
Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed', region: 'EU'),
Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending', region: 'US'),
Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed', region: 'EU'),
Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed', region: 'US'),
Order(id: 'ORD-006', category: 'Electronics', amount: 2100.0, status: 'completed', region: 'AP'),
];
OrderAnalyzer(orders).printReport();
}
> **الإخراج:** شغّل محلياً في DartPad أو عبر `dart run`. جميع الأمثلة في دورة Dart مبنية على Dart 3.x / Flutter 3.x. قد تختلف النتائج قليلاً حسب إصدار SDK.
الإخراج:
=== DataPipeline Analytics Report ===
Total orders: 6
Completed: 5
Unique categories: 3
Regions: {US, EU, AP}
Total revenue: $4660.00 USD
--- Revenue by Category ---
Electronics: $3600.00 USD
Clothing: $890.00 USD
Books: $170.00 USD
--- Top Categories ---
Electronics: $3600.00 USD
Clothing: $890.00 USD
Books: $170.00 USD
--- Orders by Region & Status ---
US: {completed: 2, pending: 1}
EU: {completed: 2}
AP: {completed: 1}
❓ أسئلة شائعة
س: ما الفرق في الأداء بين List و Set؟ ج: القائمة تخزن العناصر حسب ترتيب الإدراج، مع وصول O(1) بالفهرس وبحث O(n). المجموعة مبنية على التجزئة، وتوفر بحث/احتواء O(1) لكن بدون وصول بالفهرس. استخدم المجموعة لسيناريوهات إزالة التكرار، والقائمة عندما يهم الترتيب.
س: هل يمكن أن تكون مفاتيح الخريطة من أي نوع؟ ج: نعم، لكن عند استخدام فئات مخصصة كمفاتيح، يجب عليك تجاوز
==وhashCode؛ وإلا فلن تتمكن الخريطة من العثور على الإدخالات بشكل صحيح. يُنصح باستخدام String أو int كمفاتيح.
س: هل أستخدم
[]أم<Type>[]للقائمة الفارغة؟ ج: استخدم[]عندما يكون سياق استنتاج النوع متاحاً. استخدم<Type>[]عند الإعلان بشكل مستقل لتجنب الاستنتاج كـList<dynamic>.
س: هل يمكن استخدام collection-if و collection-for فقط في القيم الحرفية للقوائم؟ ج: لا، القيم الحرفية للقوائم والمجموعات والخرائط كلها تدعم collection-if و collection-for. على سبيل المثال:
{for (var i in items) i.name: i.value}.
س: ما الفرق بين
putIfAbsentوupdateفي الخريطة؟ ج:putIfAbsentتعين القيمة فقط إذا لم يكن المفتاح موجوداً.updateتحدّث القيمة فقط إذا كان المفتاح موجوداً (استخدم المعاملifAbsentللتعامل مع حالة عدم الوجود).
س: ما الفرق بين المجموعات الثابتة (const) والمجموعات النهائية (final)؟ ج: المجموعة الثابتة هي ثابت وقت الترجمة، بمحتوى غير قابل للتعديل بالكامل (ثبات عميق). المجموعة النهائية لها مرجع غير قابل للتعديل، لكن المحتوى يمكن أن يكون قابلاً للتعديل (مثلاً، يمكن إضافة عناصر إلى List نهائية).
س: كيف يمكنني فرز قائمة كبيرة بكفاءة؟ ج:
List.sort()تفرز في المكان، وهي أكثر كفاءة من[...list].sort()(التي تنشئ نسخة). إذا كنت لا تريد تعديل القائمة الأصلية، أنشئ نسخة أولاً باستخدام[...list]ثم فرز.
📖 ملخص
- القائمة مرتبة وتسمح بالتكرارات، وتدعم النشر (
...) و collection-if/for للبناء التصريحي. - المجموعة غير مرتبة وتمنع التكرارات، مع بحث O(1)، وتدعم عمليات التقاطع/الاتحاد/الفرق.
- الخريطة تخزن أزواج المفتاح-القيمة؛
putIfAbsentوupdateأدوات قوية للتجميع والحسابات الإجمالية. - المجموعات الثابتة (const) ثابتة بعمق؛ المجموعات النهائية لها مراجع ثابتة لكن محتوى قابل للتعديل.
- الدوال عالية الرتبة مع عمليات المجموعات يمكنها تقليل معالجة البيانات من 50 سطراً إلى 10 فقط.
📝 تمارين
- أساسي (صعوبة ⭐): أنشئ قائمة تحتوي على 10 مبالغ طلبات. استخدم
whereلتصفية المبالغ الأكبر من 500، وmapلتنسيقها بالدولار الأمريكي، وreduceلحساب المجموع الكلي. - متوسط (صعوبة ⭐⭐): بالنظر إلى مجموعة من بيانات الطلبات، استخدم
putIfAbsentوfoldفي الخريطة لتجميع الطلبات حسب الفئة، مع حساب عدد الطلبات وإجمالي الإيرادات لكل فئة، وإخراج تقرير منسق. - تحدي (صعوبة ⭐⭐⭐): نفّذ فئة
CollectionPipelineتدعم عمليات متسلسلة مثلwhereوmapوsortByوgroupByوaggregate، وتُخرج في النهاية نتائج التحليل. تلميح: كل خطوة تُرجع مجموعة جديدة أو خريطة جديدة.