网站搜索

typescript - 比较两个对象数组是否相等,而不考虑对象属性的顺序


我正在尝试比较两个对象数组是否相等,忽略属性顺序的任何差异。我只想知道每个属性的值是否匹配。下面是每个数组中一个对象的示例,根据每个属性的值,这些对象应该是相等的。属性的顺序无关紧要,“values”属性值的顺序也无关紧要。

{ "fieldId": "123456789",
"operationType": 1,
"definitionType": 1,
"values": ["123","456"],
"isAllSelected": false,
"dateRangeSelectionType": 0
}

{ "isAllSelected": false,
"operationType": 1,
"definitionType": 1,
"fieldId": "123456789",
"values": ["456","123"],
"dateRangeSelectionType": 0
}

我尝试过使用 JSON.stringify 来比较这些数组,但我知道这行不通,因为它会将顺序考虑进相等性。

我还有以下辅助函数,我正在将它们用于另一个对象数组,但它们对这些数组不起作用:

> const objectsAreEqual = (object1: any, object2: any) =>
>     Object.keys(object1).length === Object.keys(object2).length 
>     && Object.keys(object1).every(index => object1[index] === object2[index]);
> 
> const objectArraysAreEqual = (array1: Object[], array2: Object[]) =>
>     array1.length === array2.length && array1.every((x, index) => objectsAreEqual(x, array2[index]));

答案#1

首先,请注意我整理了一下数据。在你的第二个示例对象中,"isAllSelected": false, 出现了两次,这不是一个有效的 JavaScript 对象。此外,values 数组的元素顺序被交换了,这实际上使它们 不是 同一个对象,所以在这些演示示例中,我将它们都设为完全相同的数组。

既然你把这个标记为 TypeScript 问题,我首先为这个对象的结构创建了一个 TypeScript 类型,我将其命名为“Foo”。

type Foo = {
  fieldId: string;
  operationType: number;
  definitionType: number;
  values: string[];
  isAllSelected: boolean,
  dateRangeSelectionType: number
}

然后我将这两个对象定义为 Foo 类型。

const fooA: Foo = { 
  "fieldId": "123456789",
  "operationType": 1,
  "definitionType": 1,
  "values": ["123","456"],
  "isAllSelected": false,
  "dateRangeSelectionType": 0
}

const fooB: Foo = { 
  "isAllSelected": false,
  "operationType": 1,
  "definitionType": 1,
  "fieldId": "123456789",
  "values": ["123", "456"],
  "dateRangeSelectionType": 0
}

对于原始类型来说,使用 == 和 === 运算符是好的,但在比较这些对象时,它们会返回“false”,Object.is 函数也是如此。

console.log(fooA == fooB)               // false    
console.log(fooA === fooB)              // false
console.log(Object.is(fooA, fooB))      // false

所以,看起来你将不得不挽起袖子,检查对象中的每一个键和值...

你基本上想做的是:

  1. 确保对象具有相同的键。

  2. 确保这些键具有相同的值。

棘手的部分是如果值本身是对象,会发生什么?在某些情况下,你可能只想进行浅层相等比较(例如,如果你希望你的函数在“values”数组有不同元素时仍然认为它们是相等的)。

如果你希望该函数继续深入检查对象中的所有内容是否都相同,那么你需要检查“深度相等”。

这是你可以实现此功能的一种实现方式:

export default function deepEquals(object1: any, object2: any) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);
  if (keys1.length !== keys2.length) {
    return false;
  }
  for (const key of keys1) {
    const val1 = object1[key];
    const val2 = object2[key];
    const areObjects = isObject(val1) && isObject(val2);
    if (
      areObjects && !deepEquals(val1, val2) ||
      !areObjects && val1 !== val2
    ) {
      return false;
    }
  }
  return true;
}

function isObject(object: Object) {
  return object != null && typeof object === 'object';
}

注意当比较这些对象时,这个函数返回 true:

import deepEquals from './deepEquals';

console.log(deepEquals(fooA, fooB))     // true

你也可以使用像 lodash 这样的库,它具有深度对象比较功能:

import isEqual from 'lodash.isequal';

console.log(isEqual(fooA, fooB))        // true

答案#2

你可以这样使用函数

function deepEqual(obj1: object, obj2: object): boolean {
  if (obj1 === obj2) {
    return true;
  }

  if (typeof obj1 !== 'object' || obj1 === null ||
      typeof obj2 !== 'object' || obj2 === null) {
    return false;
  }

  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  for (const key of keys1) {
    if (!obj2.hasOwnProperty(key)) {
      return false;
    }

    if (!deepEqual(obj1[key], obj2[key])) {
      return false;
    }
  }

  return true;
}

或者使用 lodash 函数,请参考这篇文章 https://bytesinsights.dev/blog/comparing-two-deep-objects-in-typescript-a-comprehensive-guide-with-examples