دليل شامل لمصفوفات الكائنات في JavaScript: إنشاء، تحديث، والتنقل عبرها بفعالية
JSON. إذا كنت مطورًا، فمن المحتمل أنك تتعامل مع هياكل البيانات هذه بشكل يومي. فكر في الأمر: كم مرة احتجت للبحث عن طريقة محددة لمعالجة مصفوفة من الكائنات في JavaScript؟ غالبًا ما تكون الإجابة هي “كثيرًا”. هذا المقال هو دليلك الشامل لإتقان العمل مع مصفوفات الكائنات (Array of Objects) في JavaScript. ستتعلم أساسيات إنشاء هذه المصفوفات، وكيفية تحديثها، والبحث فيها، والتنقل عبرها باستخدام أقوى دوال JavaScript المدمجة. تذكر أن JSON ما هو إلا اختصار لـ JavaScript Object Notation، مما يعني أنك إذا عملت مع JSON، فقد عملت بالفعل مع كائنات JavaScript.
إن إنشاء كائن في JavaScript أمر بسيط للغاية. إليك مثال يمثل سيارة بخصائصها:
{
"color": "purple",
"type": "minivan",
"registration": new Date('2012-02-03'),
"capacity": 7
}
يمثل هذا الكائن سيارة محددة بخصائصها مثل اللون، النوع، تاريخ التسجيل، والسعة. يمكن أن يكون هناك العديد من أنواع وألوان السيارات، وكل كائن يمثل سيارة فريدة.
في معظم الحالات، تحصل على بيانات بهذا التنسيق من خدمات خارجية (APIs). ولكن في بعض الأحيان، قد تحتاج إلى إنشاء الكائنات ومصفوفاتها يدويًا. على سبيل المثال، عند بناء متجر إلكتروني، قد تحتاج إلى قائمة بفئات المنتجات.
إذا كان كل عنصر في قائمة الفئات يبدو هكذا في HTML:
فإن تكرار هذا الكود 12 مرة سيجعل صيانته أمرًا صعبًا للغاية. هنا يأتي دور مصفوفات الكائنات لتبسيط العملية وجعل الكود أكثر قابلية للإدارة.
إنشاء مصفوفة من الكائنات (Array of Objects)
لنعد إلى مثال السيارات. تخيل أن لدينا مجموعة من السيارات كما في الصورة:
يمكننا تمثيل هذه المجموعة كمصفوفة من الكائنات في JavaScript على النحو التالي:
let cars = [
{ "color": "purple", "type": "minivan", "registration": new Date('2017-01-03'), "capacity": 7 },
{ "color": "red", "type": "station wagon", "registration": new Date('2018-03-03'), "capacity": 5 },
// ... المزيد من الكائنات
];
مصفوفات الكائنات نادرًا ما تبقى ثابتة؛ غالبًا ما نحتاج إلى تعديلها بإضافة عناصر جديدة، أو تحديث عناصر موجودة، أو حذفها. دعنا نستكشف كيف يمكننا إضافة كائنات إلى مصفوفة موجودة بالفعل.
إضافة كائنات جديدة إلى المصفوفة
إضافة كائن جديد في البداية باستخدام Array.unshift()
لإضافة كائن جديد إلى بداية المصفوفة، يمكنك استخدام الدالة Array.unshift(). هذه الدالة تُضيف عنصرًا أو أكثر إلى مقدمة المصفوفة وتُعيد طول المصفوفة الجديد.
let newCar = {
"color": "red",
"type": "cabrio",
"registration": new Date('2016-05-02'),
"capacity": 2
};
cars.unshift(newCar);
// الآن، newCar هو أول عنصر في مصفوفة cars
إضافة كائن جديد في النهاية باستخدام Array.push()
إذا كنت ترغب في إضافة كائن إلى نهاية المصفوفة، فإن الدالة Array.push() هي الخيار الأمثل. هذه الدالة تُضيف عنصرًا أو أكثر إلى نهاية المصفوفة وتُعيد طول المصفوفة الجديد.
let anotherNewCar = {
"color": "blue",
"type": "sedan",
"registration": new Date('2020-10-15'),
"capacity": 5
};
cars.push(anotherNewCar);
// الآن، anotherNewCar هو آخر عنصر في مصفوفة cars
إضافة كائن جديد في المنتصف باستخدام Array.splice()
لإضافة كائن في موقع محدد داخل المصفوفة (المنتصف)، يمكنك استخدام الدالة Array.splice(). هذه الدالة متعددة الاستخدامات؛ لا يمكنها فقط إضافة عناصر، بل يمكنها أيضًا إزالة عناصر أو استبدالها. كن حذرًا عند استخدام معاملاتها:
Array.splice(
{مؤشر البدء}, // index where to start
{عدد العناصر المراد إزالتها}, // how many items to remove
{العناصر المراد إضافتها} // items to add
);
على سبيل المثال، إذا أردنا إضافة سيارة كابريو حمراء في الموضع الخامس (المؤشر 4)، فسنستخدم الكود التالي:
let cabrioCar = {
"color": "red",
"type": "cabrio",
"registration": new Date('2016-05-02'),
"capacity": 2
};
cars.splice(4, 0, cabrioCar);
// هنا، 4 هو مؤشر البدء، 0 يعني عدم إزالة أي عناصر، و cabrioCar هو الكائن المراد إضافته.
التنقل والبحث في مصفوفة الكائنات
قد يتبادر إلى ذهنك سؤال: لماذا أرغب في التنقل عبر مصفوفة من الكائنات؟ في الواقع، نادرًا ما يكون “التنقل” هو الهدف الأساسي بحد ذاته. توفر JavaScript العديد من الدوال المدمجة عالية المستوى (Higher-Order Functions) التي يمكنها حل مشكلتك بكفاءة أكبر دون الحاجة لتطبيق منطق دورة عامة (general loop). دعنا نلقي نظرة على بعضها.
البحث عن كائن واحد في مصفوفة بقيمه باستخدام Array.find()
لنفترض أننا نريد العثور على أول سيارة حمراء في قائمتنا. يمكننا استخدام الدالة Array.find(). هذه الدالة تُعيد قيمة أول عنصر في المصفوفة يفي بالشرط الذي توفره الدالة الاختبارية.
let redCar = cars.find(car => car.color === "red");
console.log(redCar);
// الناتج سيكون أول كائن يطابق الشرط:
// {
// color: 'red',
// type: 'station wagon',
// registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
// capacity: 5
// }
من الممكن أيضًا البحث عن قيم متعددة في نفس الوقت:
let specificCar = cars.find(car => car.color === "red" && car.type === "cabrio");
// ستُعيد هذه الدالة أول سيارة حمراء من نوع "cabrio" تجدها.
الحصول على كائنات متعددة تطابق شرطًا باستخدام Array.filter()
بينما تُعيد الدالة Array.find() كائنًا واحدًا فقط (أول تطابق)، ماذا لو أردنا الحصول على جميع السيارات الحمراء؟ في هذه الحالة، نحتاج إلى استخدام الدالة Array.filter(). هذه الدالة تُنشئ مصفوفة جديدة تحتوي على جميع العناصر التي اجتازت الاختبار الذي توفره الدالة الاختبارية.
let redCars = cars.filter(car => car.color === "red");
console.log(redCars);
// الناتج سيكون مصفوفة تحتوي على جميع السيارات الحمراء:
// [
// {
// color: 'red',
// type: 'station wagon',
// registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
// capacity: 5
// },
// {
// color: 'red',
// type: 'cabrio',
// registration: 'Sat Mar 03 2012 01:00:00 GMT+0100 (GMT+01:00)',
// capacity: 2
// }
// ]
تحويل كائنات المصفوفة باستخدام Array.map()
تُعد عملية تحويل مصفوفة من الكائنات إلى مصفوفة أخرى من الكائنات أو القيم المختلفة من المهام الشائعة جدًا. هذه هي المهمة المثالية للدالة Array.map(). هذه الدالة تُنشئ مصفوفة جديدة عن طريق تطبيق دالة معينة على كل عنصر في المصفوفة الأصلية.
لنفترض أننا نريد تصنيف سياراتنا إلى ثلاث مجموعات بناءً على سعتها (الحجم).
let sizes = cars.map(car => {
if (car.capacity <= 3) {
return "small";
}
if (car.capacity <= 5) {
return "medium";
}
return "large";
});
console.log(sizes);
// الناتج سيكون مصفوفة من الأحجام:
// ['large', 'medium', 'medium', ..., 'small']
من الممكن أيضًا إنشاء كائنات جديدة تمامًا إذا كنا بحاجة إلى الاحتفاظ بالمزيد من القيم أو الخصائص:
let carsProperties = cars.map(car => {
let properties = { "capacity": car.capacity, "size": "large" };
if (car.capacity <= 5) {
properties['size'] = "medium";
}
if (car.capacity <= 3) {
properties['size'] = "small";
}
return properties;
});
console.log(carsProperties);
// الناتج سيكون مصفوفة من الكائنات الجديدة:
// [
// { capacity: 7, size: 'large' },
// { capacity: 5, size: 'medium' },
// { capacity: 5, size: 'medium' },
// { capacity: 2, size: 'small' },
// // ...
// ]
إضافة خاصية إلى كل كائن في المصفوفة باستخدام Array.forEach()
ماذا لو أردنا إضافة خاصية "الحجم" (size) مباشرة إلى كل كائن سيارة موجود في المصفوفة الأصلية بدلاً من إنشاء مصفوفة جديدة؟ هذه هي حالة استخدام ممتازة للدالة Array.forEach(). هذه الدالة تُنفذ دالة معينة مرة واحدة لكل عنصر في المصفوفة.
cars.forEach(car => {
car['size'] = "large"; // قيمة افتراضية
if (car.capacity <= 5) {
car['size'] = "medium";
}
if (car.capacity <= 3) {
car['size'] = "small";
}
});
// الآن، كل كائن سيارة في مصفوفة cars الأصلية يحتوي على خاصية 'size' جديدة.
فرز مصفوفة الكائنات حسب خاصية معينة باستخدام Array.sort()
بعد الانتهاء من معالجة الكائنات وتحويلها، غالبًا ما نحتاج إلى فرزها بطريقة معينة. عادةً ما يعتمد الفرز على قيمة خاصية معينة مشتركة بين جميع الكائنات. يمكننا استخدام الدالة Array.sort()، ولكن يجب علينا توفير دالة مقارنة (compare function) تُحدد آلية الفرز.
لنفترض أننا نريد فرز السيارات بناءً على سعتها (capacity) بترتيب تنازلي (من الأكبر للأصغر).
let sortedCars = cars.sort((c1, c2) => {
// للترتيب التنازلي: إذا كانت سعة c1 أقل من c2، فضع c1 بعد c2 (أعد 1)
if (c1.capacity < c2.capacity) {
return 1;
}
// إذا كانت سعة c1 أكبر من c2، فضع c1 قبل c2 (أعد -1)
if (c1.capacity > c2.capacity) {
return -1;
}
// إذا كانت السعات متساوية، لا تغير الترتيب (أعد 0)
return 0;
});
console.log(sortedCars);
// الناتج سيكون مصفوفة السيارات مرتبة تنازليًا حسب السعة:
// [
// {
// color: 'purple',
// type: 'minivan',
// registration: 'Wed Feb 01 2017 00:00:00 GMT+0100 (GMT+01:00)',
// capacity: 7
// },
// {
// color: 'red',
// type: 'station wagon',
// registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
// capacity: 5
// },
// // ...
// ]
تُقارن الدالة Array.sort() كائنين وتضع الكائن الأول في المكان الثاني إذا كانت نتيجة دالة الفرز موجبة. يمكنك النظر إلى دالة الفرز وكأنها تجيب على سؤال: "هل يجب وضع الكائن الأول في المكان الثاني؟"
تأكد دائمًا من إضافة الحالة التي تكون فيها قيمة المقارنة لكلا الكائنين متساوية (إرجاع 0) لتجنب التبديلات غير الضرورية وضمان استقرار الفرز.
التحقق مما إذا كانت الكائنات في المصفوفة تفي بشرط معين باستخدام Array.some() و Array.every()
تُعد الدالتان Array.some() و Array.every() مفيدتين للغاية عندما نحتاج فقط إلى التحقق مما إذا كانت الكائنات في المصفوفة تفي بشرط معين. على سبيل المثال، هل لدينا سيارة كابريو حمراء في قائمة السيارات؟ هل جميع السيارات قادرة على نقل 4 أشخاص على الأقل؟ أو في سياق تطوير الويب: هل يوجد منتج معين في سلة التسوق؟
-
Array.some(): تُعيدtrueإذا كان عنصر واحد على الأقل في المصفوفة يفي بالشرط. -
Array.every(): تُعيدtrueإذا كانت جميع العناصر في المصفوفة تفي بالشرط.
// هل يوجد أي سيارة حمراء من نوع "cabrio"؟
cars.some(car => car.color === "red" && car.type === "cabrio"); // الناتج: true
// هل جميع السيارات قادرة على حمل 4 أشخاص على الأقل؟
cars.every(car => car.capacity >= 4); // الناتج: false
قد تتذكر الدالة Array.includes() التي تُشبه Array.some() في فحص وجود عنصر، ولكنها تعمل فقط مع الأنواع البدائية (primitive types) مثل الأرقام أو السلاسل النصية، ولا يمكنها البحث عن كائنات معقدة بناءً على خصائصها.
الخلاصة التقنية
لقد تناولنا في هذا المقال مجموعة واسعة من الدوال الأساسية في JavaScript التي تُمكّنك من إنشاء مصفوفات الكائنات، ومعالجتها، وتحويلها، والبحث فيها بكفاءة عالية. هذه الدوال، مثل Array.unshift()، Array.push()، Array.splice()، Array.find()، Array.filter()، Array.map()، Array.forEach()، Array.sort()، Array.some()، و Array.every()، تُغطي معظم حالات الاستخدام الشائعة التي ستواجهها كمطور. إن إتقان هذه الدوال لا يقتصر على كتابة كود أنظف وأكثر قابلية للصيانة فحسب، بل يُحسن أيضًا من أداء تطبيقاتك من خلال الاستفادة من التحسينات الداخلية لمحركات JavaScript. ننصح دائمًا بالرجوع إلى التوثيق الرسمي (مثل MDN Web Docs) لمزيد من التفاصيل والحالات المتقدمة، والاستمرار في الممارسة لتثبيت هذه المفاهيم.