javascript - 比较JavaScript中的循环(自引用)对象

标签 javascript

我正在比较两个包含stringnumberarrayobject值的对象。至此没有问题。当我尝试比较自引用对象时,出现以下错误RangeError: Maximum call stack size exceeded。如果自引用对象被引用到另一个对象的相同级别,则应将它们视为相等。我的问题是如何实现。这是我的代码:

const equalsComplex = function(value, other) {
  // Get the value type
  const type = Object.prototype.toString.call(value);

  // If the two objects are not the same type, return false
  if (type !== Object.prototype.toString.call(other)) return false;

  // If items are not an object or array, return false
  if (['[object Array]', '[object Object]'].indexOf(type) < 0) return false;

  // Compare the length of the length of the two items
  const valueLen =
    type === '[object Array]' ? value.length : Object.keys(value).length;
  const otherLen =
    type === '[object Array]' ? other.length : Object.keys(other).length;
  if (valueLen !== otherLen) return false;

  // Compare two items
  const compare = function(item1, item2) {
    // Get the object type
    const itemType = Object.prototype.toString.call(item1);

    // If an object or array, compare recursively
    if (['[object Array]', '[object Object]'].indexOf(itemType) >= 0) {
      if (!equalsComplex(item1, item2)) return false;
    }

    // Otherwise, do a simple comparison
    else {
      // If the two items are not the same type, return false
      if (itemType !== Object.prototype.toString.call(item2)) return false;

      // Else if it's a function, convert to a string and compare
      // Otherwise, just compare
      if (itemType === '[object Function]') {
        if (item1.toString() !== item2.toString()) return false;
      } else {
        if (item1 !== item2) return false;
      }
    }
  };

  // Compare properties
  if (type === '[object Array]') {
    for (let i = 0; i < valueLen; i++) {
      if (compare(value[i], other[i]) === false) return false;
    }
  } else {
    for (let key in value) {
      if (value.hasOwnProperty(key)) {
        if (compare(value[key], other[key]) === false) return false;
      }
    }
  }

  // If nothing failed, return true
  return true;
};
const r = { a: 1 };
r.b = r;
const d = { a: 1 };
d.b = d;

console.log(
  equalsComplex(
    {
      a: 2,
      b: '2',
      c: false,
      g: [
        { a: { j: undefined } },
        { a: 2, b: '2', c: false, g: [{ a: { j: undefined } }] },
        r
      ]
    },
    {
      a: 2,
      b: '2',
      c: false,
      g: [
        { a: { j: undefined } },
        { a: 2, b: '2', c: false, g: [{ a: { j: undefined } }] },
        r
      ]
    }
  )
);

最佳答案

我喜欢@stevendesu的回复。他很好地解决了圆形结构的问题。我使用您的代码编写了一个解决方案,该解决方案也可能会有所帮助。

const equalsComplex = function(value, other, valueRefs, otherRefs) {
  valueRefs = valueRefs || [];
  otherRefs = otherRefs || [];

  // Get the value type
  const type = Object.prototype.toString.call(value);

  // If the two objects are not the same type, return false
  if (type !== Object.prototype.toString.call(other)) return false;

  // If items are not an object or array, return false
  if (['[object Array]', '[object Object]'].indexOf(type) < 0) return false;

  // We know that the items are objects or arrays, so let's check if we've seen this reference before.
  // If so, it's a circular reference so we know that the branches match. If both circular references
  // are in the same index of the list then they are equal.
  valueRefIndex = valueRefs.indexOf(value);
  otherRefIndex = otherRefs.indexOf(other);
  if (valueRefIndex == otherRefIndex && valueRefIndex >= 0) return true;
  // Add the references into the list
  valueRefs.push(value);
  otherRefs.push(other);

  // Compare the length of the length of the two items
  const valueLen =
    type === '[object Array]' ? value.length : Object.keys(value).length;
  const otherLen =
    type === '[object Array]' ? other.length : Object.keys(other).length;
  if (valueLen !== otherLen) return false;

  // Compare two items
  const compare = function(item1, item2) {
    // Get the object type
    const itemType = Object.prototype.toString.call(item1);

    // If an object or array, compare recursively
    if (['[object Array]', '[object Object]'].indexOf(itemType) >= 0) {
      if (!equalsComplex(item1, item2, valueRefs.slice(), otherRefs.slice())) return false;
    }

    // Otherwise, do a simple comparison
    else {
      // If the two items are not the same type, return false
      if (itemType !== Object.prototype.toString.call(item2)) return false;

      // Else if it's a function, convert to a string and compare
      // Otherwise, just compare
      if (itemType === '[object Function]') {
        if (item1.toString() !== item2.toString()) return false;
      } else {
        if (item1 !== item2) return false;
      }
    }
  };

  // Compare properties
  if (type === '[object Array]') {
    for (let i = 0; i < valueLen; i++) {
      if (compare(value[i], other[i]) === false) return false;
    }
  } else {
    for (let key in value) {
      if (value.hasOwnProperty(key)) {
        if (compare(value[key], other[key]) === false) return false;
      }
    }
  }

  // If nothing failed, return true
  return true;
};
const r = { a: 1 };
r.b = {c: r};
const d = { a: 1 };
d.b = {c: d};

console.log(
  equalsComplex(
    {
      a: 2,
      b: '2',
      c: false,
      g: [
        { a: { j: undefined } },
        { a: 2, b: '2', c: false, g: [{ a: { j: undefined } }] },
        r
      ]
    },
    {
      a: 2,
      b: '2',
      c: false,
      g: [
        { a: { j: undefined } },
        { a: 2, b: '2', c: false, g: [{ a: { j: undefined } }] },
        d
      ]
    }
  )
);

基本上,您要跟踪到目前为止在每个分支中看到的对对象和数组的引用(slice()方法对引用数组进行浅拷贝)。然后,每次看到对象或数组时,都要检查引用历史,以查看它是否为循环引用。如果是这样,请确保两个循环引用都指向历史的同一部分(这很重要,因为两个循环引用都可能指向对象结构中的不同位置)。

我建议对此使用库,因为我还没有对代码进行深入测试,但是有一个简单的解决方案可供您使用。

关于javascript - 比较JavaScript中的循环(自引用)对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56048142/

相关文章:

javascript - jQuery 分页器在最后一页上不起作用

javascript - 默认值更改时向输入添加类

javascript - 无法使用三元运算符赋值

javascript - 什么是 node.js 模块?

javascript - 使 div 比它包含的 div 小 30px

javascript - 光标前面的字符

javascript - ExtJS 和 Zend Framework MVC

javascript - Jquery Sticky 导航在满足特定条件时启动

javascript - Firebase Atmonic 添加和删除条目

javascript - 如何将缺失值添加到两个关联数组? (JavaScript)