Тесты для сравнения объектов

Подскажите, что не учитывается для успешного прохождения тестов для сравнения (пара тестов проваливается):
function objEqual(a, b) {
var keysA = Object.keys(a);
var keysB = Object.keys(b);

for (var i = 0; i < keysA.length; i++) {
var key = keysA[i];
if (key in keysB) {
return true;
}
}
if (keysA.length !== keysB.length) {
return false;
} else {
return true;
}
}

function deepEqualTests() {

var H1={ a:5, b: { b1:6, b2:7 } };
var H2={ b: { b1:6, b2:7 }, a:5 };
var H3={ a:5, b: { b1:6 } };
var H4={ a:5, b: { b1:66, b2:7 } };
var H5={ a:5, b: { b1:6, b2:7, b3:8 } };
var H6={ a:null, b:undefined, c:Number.NaN };
var H7={ c:Number.NaN, b:undefined, a:null };

console.log( (H1 === H2)?‘пройден’:‘НЕ ПРОЙДЕН!’ ); // => true
console.log( (H1 !== H3)?‘пройден’:‘НЕ ПРОЙДЕН!’ ); // => false
console.log( (H1 !== H4)?‘пройден’:‘НЕ ПРОЙДЕН!’ ); // => false
console.log( (H1 !== H5)?‘пройден’:‘НЕ ПРОЙДЕН!’ ); // => false
console.log( (H6 === H7)?‘пройден’:‘НЕ ПРОЙДЕН!’ ); // => true
}
Не учитывается порядок расположения ключей в объекте? как лучше эти случаи исправить?

Уже ответил тут Сравнение объектов JS - #5 от пользователя dmitry.
Давай продолжим в этой теме чтобы держать эти два обсуждения в рамках разных вопросов.

В массиве важен порядок расположения элементов. В объекте порядок расположения неважен. Он может зависеть от того когда и как был добавлен элемент.

Эта задача учитывает много условий.
Поправьте код, что бы он запускался. И тогда проще будет идти по тестам.

  1. На данный момент в коде нет рекурсии, из-за этого первый тест не пройдет. Если элемент является объектом сравнение его с любым другим объектом кроме него самого выдаст false.

  2. NaN нельзя сравнивать напрямую. NaN === NaN всегда будет false. Для этого есть функции isNaN

  3. Если обратиться к несуществующему элементу объекта, его значение будет undefined.
    const a = {};
    console.log(a[‘property’]); // undefined

И есть несколько ошибок тут.

for (var i = 0; i < keysA.length; i++) {
var key = keysA[i]; //берем значение по первому ключу (ок).
if (key in keysB) {
// проверяем является ли это значение ключом у какого-то элемента (-). Тут нужно поставить несколько условий. (i in keysB) && (key === keysB[i] || isNaN(key) && isNaN(keysB[i]))

return true; //получается что если условие выполнилось и мы дошли сюда, на этом выполнение функции заканчивается успешно. И мы не переходим к проверке следующего элемента.
}
}

2 лайка

function objEqual(a, b) {
var keysA = Object.keys(a);
var keysB = Object.keys(b);

for (var i = 0; i < keysA.length; i++) {
var key = keysA[i];
if (i in keysB) && (key === keysB[i] || isNaN(key) && isNaN(keysB[i])) {
return true;
}
}
if (keysA.length !== keysB.length) {
return false;
} else {
return true;
}
}
В таком виде код пока не работает, видимо, как и упоминали нужно использовать рекурсию для сравнения значений под одинаковыми ключами. Можете помочь с ней, не могу разобраться.

Нехорошо давать готовый ответ, так как ты лишаешься удовольствия решить эту задачу самостоятельно. И получается не научишься сама решать эти задачи без помощи. Но я считаю, что иногда на первых порах пока только начинаешь разбираться в JS, то можно подсказывать прямо, чтобы человек не загруз с решением задачи и не остановился в ее решении.
Вижу, что мое решение не идеальное, надо добавлять тесты и проверки и дополнять код. Это каркас, так сказать.

function isObject(obj) {
  // определение типа переменной
  // Object.prototype.toString.call([]) вернет '[object Array]'
  // Object.prototype.toString.call({}) вернет '[object Object]'
  return Object.prototype.toString.call(obj) === '[object Object]';
}

function isNumber(num) {
  // определение типа переменной
  // Object.prototype.toString.call(1) вернет '[object Number]'
  // Object.prototype.toString.call(NaN) вернет '[object Number]'
  return Object.prototype.toString.call(num) === '[object Number]';
}

// Здесь все очевидно - сравнение двух массивов
function isArraysEqual(arrFirst, arrSecond) {
  if (arrFirst.length !== arrSecond.length) {
    return false;
  }    
  
  for (let i = 0;  i < arrFirst.length; i++) {
    if (arrFirst[i] !== arrSecond[i]) {
      return false;
    }
  }
    
  return true;
}

function objEqual(a, b) {
  const keysA = Object.keys(a);
  const keysB = Object.keys(b);
  
  // Ставим все условия и проверки вначале
  // если объекты не проходят проверки - то функция возвращает false 
  // если после прохождения всех проверок ни одна проверка не завершилась успешно
  // то в конце функции objEqual() возвращаем true

  if (keysA.length !== keysB.length) {
    return false;
  }
    
  for (var i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    const currentAValue = a[currentKey];
    const currentBValue = b[currentKey];  

    // 'k' in g - это для объектов, а не для массивов, проверяет входит ли ключ 'k' в объект  g    
    // Object.keys(a) и Object.keys(b) вернет массивы, поэтому для проверки
    // входит ли элемент в массив надо использовать метод массива includes()
    if (!keysB.includes(currentKey)) {
      return false;
    }
    if (Array.isArray(currentAValue)) {
     //  Если currentAValue имеет тип массив, а currentBValue не типа массив, то возвращаем false 
     // Если массивы не равны - тоже возвращаем false 
      if (!Array.isArray(currentBValue) || !isArraysEqual(currentAValue, currentBValue)) {
        return false;       
      }
    } else if (isObject(currentAValue)) {
      //  Если currentAValue имеет тип объект, а currentBValue не типа объект, то возвращаем false 
     // Если объекты не равны - тоже возвращаем false
     // Здесь идеи рекурсия: !objEqual(currentAValue, currentBValue)
      if (!isObject(currentBValue) || !objEqual(currentAValue, currentBValue)) {
         return false; 
      }  
    } else if (isNumber(currentAValue) || isNumber(currentBValue)) {
      // если одна из переменных имеет тип number а другая нет - значит
      // эти переменные не равны
      if (!isNumber(currentAValue) || !isNumber(currentBValue)) {
         return false;
      }
      // если currentAValue равен NaN, а currentBValue не равен NaN, или наоборот, то возвращаем false
      if (isNaN(currentAValue) && !isNaN(currentBValue) || !isNaN(currentAValue) && isNaN(currentBValue) ) {
        return false;
      }
      // Если обе переменные currentAValue и currentBValue равны NaN, переходим к следующей итерации цикла  
      if (isNaN(currentAValue) && isNaN(currentBValue)) {
        continue;      
      }    
      // если добрались до сюда, то currentAValue и currentBValue - это числа и их можно сравнивать обычным сравнением
      if (currentAValue !== currentBValue) {
        return false;      
      }  
    } else {
     // Если дошли сюда, значит currentAValue и currentBValue - это простые типы, не объекты и не массивы и не равны NaN
     // Проверяем простые типы обычным сравнением
      if (currentAValue !== currentBValue) {
        return false;      
      }
    }
      
  }

  // Все проверки пройдены и ни одна не вернула false
  // значит объекты равны - возвращаем true
  return true;
 
}

const H1 = { a:5, b: { b1:6, b2:7 } };
const H2 = { b: { b1:6, b2:7 }, a:5 };
const H3 = { a:5, b: { b1:6 } };
const H4 = { a:5, b: { b1:66, b2:7 } };
const H5 = { a:5, b: { b1:6, b2:7, b3:8 } };
const H6 = { a:null, b:undefined, c:Number.NaN };
const H7 = { c:Number.NaN, b:undefined, a:null };

console.log( objEqual(H1, H2) ? 'пройден' : 'НЕ ПРОЙДЕН!' ); // => true
console.log( objEqual(H1, H3) ? 'пройден' : 'НЕ ПРОЙДЕН!' ); // => false
console.log( objEqual(H1, H4) ? 'пройден' : 'НЕ ПРОЙДЕН!' ); // => false
console.log( objEqual(H1, H5) ? 'пройден' : 'НЕ ПРОЙДЕН!' ); // => false
console.log( objEqual(H6, H7) ? 'пройден' : 'НЕ ПРОЙДЕН!' ); // => true
2 лайка

Спасибо огромное за помощь и понимание! Я только начинаю, нет опыта и системы пока.

2 лайка