762 words
4 minutes
How to Compare Objects in JavaScript

How to Compare Objects in JavaScript#

دا سؤال مهم جدا تعال نجاوب عليه in depth
لما تيجي تقارن أي primitive values في ال js ف الموضوع بسيط بتستخدم أي equality operators كمثال :

'a' === 'g'; // => false  
10 === 10; // => true  

مع ال objects الموضوع أعقد من كدا شوية لإنهم structured data تعال بقي نعرف ازاي نقارن بين الobjects صح تعال الأول نعرف احنا بنقارن بناءا علي إيه ، هنقسمها ل 4 حاجات

  • 1 - Referential equality
  • 2 - Manual comparison
  • 3 - Shallow equality
  • 4 - Deep equality

نمسك واحدة واحدة ونبدأ نتفاهم

أول حاجة ال Referential equality#

تقدر تقول عليها المساواة المرجعية بص هفهمك الjs بتديك 3 طرق عشان ت compare values

  • The strict equality operator ===
  • The loose equality operator ==
  • Object.is() function

حلو ؟ لو جيت تقارن بين objects ب استخدام واحدة من دول عمرها م هتديك true إلا في حالة واحدة وهي إن ال compared values refer to the same object instance يعنني يكون ليهم نفس ال reference تعال نبص كدا بالأمثلة عشان الدنيا توضح

const hero1 = {  
  name: 'Batman'  
};  
const hero2 = {  
  name: 'Batman'  
};  
hero1 === hero2; // => false  
hero1 == hero2; // => false  
Object.is(hero1, hero2); // => false  

طب تعال نعمل تعديل بسيط ف الكود كدا ؟

const hero1 = {  
  name: 'Batman'  
};  
const hero2 = hero1;  
hero1 === hero2; // => true  
hero1 == hero2; // => true  
Object.is(hero1, hero2); // => true  

مع العلم ان الjs مفيهاش pass By reference من باب العلم بالشئ مش مصدقني ؟ اقرأ دي كدا من هنا

متتوهش مني دي أول حاجة قارننا ع أساسها الحاجة التانية وهي Manual comparison#

ودي بكل بساطة بتعملها ب ايدك بتقرأ ال properties وبتقارنهم ببعض ب إيدك كمثال

function isHeroEqual(object1, object2) {  
  return object1.name === object2.name;  
}

const hero1 = {  
  name: 'Batman'  
};  
const hero2 = {  
  name: 'Batman'  
};  
const hero3 = {  
  name: 'Joker'  
};

console.log(isHeroEqual(hero1, hero2)); // => true  
console.log(isHeroEqual(hero1, hero3)); // => false  

متتوهش بقي مني تالت حاجة وركز عشان الجايين دول مهمين Shallow equality وبنستخدمها في حالات لأنها أسرع من deep equality#

بص ركز معايا ف الكود دا كدا دي custom function بتعمل Shallow equality

const shallowCompare = (source, target) => {  
  if (typeof(source) !== typeof(target)) {  
    return false;  
  }  

  if (typeof(source) === "array") {  
    if (source.length !== target.length) {  
      return false;  
    }  
    return source.every((el, index) => el === target[index]);  
  }   

  else if (typeof(source) === "object") {  
    return Object.keys(source).every((key) => source[key] === target[key]);  
  }  

  else if (typeof(source) === "date") {  
    return source.getTime() === target.getTime()  
  }  

  return source === target;  
}

بص هو هنا بيcheck لو ال Source والtarget نفس ال type وبعد كدا بيشوف الtype دا لو Array أو object أو date وبناءا عالtype بتقارن بين العناصر دي يعني ك Array مثلا بتشوف كل el === الEl اللي له نفس الاندكس قصاده بيساويه ولا لا وهكذا ف بافي types بس الshallow comparison دا له عيوب تعال نبص كدا كتطبيق له

shallowCompare({a: 1}, {a: 1}) // => true  

جميل وزي الفل لكن لما تستخدمه مع nested objects أو arrays تبدأ تظهر المشكلة الكبيرة

shallowCompare({a: {b: 1}}, {a: {b: 1}}) // false  

وهنا هتتحل بال deep comparison اللي هو رابع حاجة معانا بس قبل م أروحله أكيد مش كل مرة هتعمل ٍshallow comparison هتكتب ال custom function دي ف بدايل كتير بتعمل نفس الشغل تقريبا زي ايه ؟ JSON.stringify

const obj1 = { a: 1 };  
const obj2 = { a: 1 };  
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true  

بس الترتيب مهم يعني دي

JSON.stringify({a: 1, b:2}) === JSON.stringify({b: 2 , a:1})  

هتدي false لأنه بكل بساطة عشان هنا انت بتحول الobjects ل ٍStrings وبتقارنهم ك Strings

وف حل أسهل وأضمن انك تستخدم مكتبة loadash

The Lodash library provides a _.isEqual method for shallow comparisons of objects or arrays

const _ = require('lodash');  
const obj1 = { a: 1, b: { c: 2 } };  
const obj2 = { a: 1, b: { c: 2 } };  
console.log(_.isEqual(obj1, obj2)); // true  

ندخل بقي لأخر حاجة وهي ال deep comparison#

شايف ال custom function اللي ف shallow تعال نعدل عليها شوية هنضيف عليها recursion وحاول تتجنب تقارن nested objects هتبقي مكلفة كpreformance


const deepCompare = (source, target) => {  
  if (typeof(source) !== typeof(target)) {  
    return false;  
  }  

  if (typeof(source) === "array") {  
    if (source.length !== target.length) {  
      return false;  
    }  
    return source.every((entry, index) => deepCompare(entry, target[index]));  
  }  

  else if (typeof(source) === "object") {  
    if (Object.keys(source).length !== Object.keys(target).length) {  
      return false;  
    }  

    return Object.keys(source).every((key) =>  
      deepCompare(source[key], target[key])  
    );  
  }   

  else if (typeof(source) === "date") {  
    return source.getTime() === target.getTime()  
  }  

  return source === target  
}

وكتطبيق عليها

deepCompare({a: {b: 1}}, {a: {b: 1}}) // true  

وطبعا مش كل مرة هنستخدم custom function ف انت هتستخدم third libraries زي loadash أو مكتبة زي deep equal

Join our whatsapp group here
My Channel here