提问人: 提问时间:2/11/2021 最后编辑:Peter Seliger 更新时间:2/12/2021 访问量:190
对于符合 JSON 的对象文字数组,如何比较这些项的相等性?
For an array of JSON conform object literals, how does one compare the equality of such items?
问:
如何使用 React.js 和打字稿检查数组是否包含不同的值?
例:
[{
name: 'John',
value: 1,
}, {
name: 'John',
value: 1,
}, {
name: 'Carla',
value: 15,
}]
如果数组中的所有对象都相同,我想返回 false,如果至少有一个不同的对象,我想返回 true。
答:
我会通过引用复制的数组来检查字符串化对象数组是否包含字符串化项目,其中我删除了最新的项目。我将用于比较所有项目是否匹配在一起,然后返回相反的值。Array.every()
但是,如果对象数组非常长,这可能是非常繁重的操作
const arrSame = [{name: 1}, {name: 1}, {name: 1}];
const arrDiff = [{name:1}, {name: 2}, {name: 2}];
const arrDiff2 = [{name:1}, {name: 1}, {name: 2}];
const hasDifferentValues = (arr) => !arr.every((item, i, ref) => JSON.stringify([...ref].shift()).includes(JSON.stringify(item)));
console.log(hasDifferentValues(arrSame));
console.log(hasDifferentValues(arrDiff));
console.log(hasDifferentValues(arrDiff2));
评论
{ name: 'John', value: 1 }
{ value: 1, name: 'John' }
这并不完全是特定于反应的,但要检查差异,您可以使用每个类似的方法遍历数组。
const fooArray = [{
name: 'John',
value: 1,
nest: {
isValid: [1, 2]
}
},
{
value: 1,
name: 'John',
nest: {
isValid: [1, 1]
}
}, {
name: 'John',
value: 1,
nest: {
isValid: [1, 1]
}
}
]
// check each member against the last, see if there is a diff
const isSame = (element, index, arr) => {
if (index > 0) {
// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
// return JSON.stringify(element) === JSON.stringify(arr[index - 1])
// alternatively, you can check to see if some of the values are different
// by stringifying and checking if either are permuations of each other
// this is probably not the ideal way, but I added it for the sake of a different solution
const currentObStr = JSON.stringify(element).split("").sort().join()
const prevObStr = JSON.stringify(arr[index - 1]).split("").sort().join()
return currentObStr === prevObStr
}
return true
}
const everyElementIsSame = fooArray.every(isSame)
console.log(everyElementIsSame)
评论
{ value: 1, name: 'John' }
JSON.stringify
{ name: 'John', value: 1 }
{ value: 1, name: 'John' }
false
true
不能使用直接相等比较,因为对象永远不会返回相等。
即 ,和 != 。{} != {}
{name: 'John', value: 1}
{name: 'John', value: 1}
因此,首先,您必须决定要为这些对象定义什么为“相等”。
为了这个原因,我们假设你只使用字段作为平等的测试。因此,如果数组中的两个对象具有相同的字段,那么您将称它们相等。然后,您将定义函数:name
name
type NameValue = {name: string, value: string}
const areEqual = (obj1: NameValue, obj2: NameValue): boolean => obj1.name === obj2.name
当然,您可以更改此函数以反映您定义为“相等”的任何内容。也有 npm 包可以帮助您进行深度相等性检查,或者您可以 JSON.stringify 两者并检查相等性
然后你可以使用 Array.some()。如果数组中的任何元素通过测试,Array.some() 将返回 true。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
测试是否有任何元素不等于第一个元素就足够了。
const areNotAllEqual = yourArray.some((currentElement) => {
return !areEqual(currentElement, yourArray[0])
})
评论
在评论和批评了基于的方法之后,我想就此事做出一些贡献。由于与此同时,所有现代 JS 引擎似乎都知道对象的键顺序(该对象的创建方式),并且似乎也保证了键迭代的顺序,因此可以编写一个递归函数,对于任何深度嵌套但符合 JSON 的 JS 对象,该函数会为此类对象重新建立规范化的键顺序,但不会触及数组。JSON.stringify
然后,将此类键规范化对象传递给这些对象,使其具有字符串化签名的可比性......JSON.stringify
function defaultCompare(a, b) {
return ((a < b) && -1) || ((a > b) && 1) || 0;
}
function comparePropertyNames(a, b) {
return a.localeCompare
? a.localeCompare(b)
: defaultCompare(a, b);
}
function getJsonDataWithNormalizedKeyOrder(data) {
let value;
if (Array.isArray(data)) {
value = data.map(getJsonDataWithNormalizedKeyOrder);
} else if (data && (typeof data === 'object')) {
value = Object
.getOwnPropertyNames(data)
.sort(comparePropertyNames)
.reduce((obj, key) => {
obj[key] = getJsonDataWithNormalizedKeyOrder(data[key])
return obj;
}, {});
} else {
value = data;
}
return value;
}
const objA = {
name: 'foo',
value: 1,
obj: {
z: 'z',
y: 'y',
a: {
name: 'bar',
value: 2,
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
name: 'baz',
value: 3,
obj: {
k: 'k',
i: 'i',
c: 'c',
},
arr: ['5', 6, 'B', 'A'],
}],
},
},
arr: ['Z', 'Y', 1, '2'],
};
const objB = {
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'c',
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 1,
};
const objC = {
arr: ['Z', 'Y', 1, '2'],
obj: {
z: 'z',
y: 'y',
a: {
obj: {
x: 'x',
w: 'w',
b: 'b',
},
arr: ['3', 4, 'W', 'X', {
obj: {
k: 'k',
i: 'i',
c: 'c',
},
name: 'baz',
value: 3,
arr: ['5', 6, 'B', 'A'],
}],
name: 'bar',
value: 2,
},
},
name: 'foo',
value: 2,
};
console.log(
'getJsonDataWithNormalizedKeyOrder(objA) ...',
getJsonDataWithNormalizedKeyOrder(objA)
);
console.log(
'getJsonDataWithNormalizedKeyOrder(objB) ...',
getJsonDataWithNormalizedKeyOrder(objB)
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA)) ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA))
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB)) ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB))
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC)) ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC))
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA)).length ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objA)).length
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB)).length ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objB)).length
);
console.log(
'JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC)).length ...',
JSON.stringify(getJsonDataWithNormalizedKeyOrder(objC)).length
);
console.log(`
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objB)
) ?`,
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objB)
)
);
console.log(`
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objC)
) ?`,
JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objA)
) === JSON.stringify(
getJsonDataWithNormalizedKeyOrder(objC)
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
将上述内容应用于以更通用的方式解决 OP 原始问题的方法,然后可能看起来类似于下一行......
function defaultCompare(a, b) {
return ((a < b) && -1) || ((a > b) && 1) || 0;
}
function comparePropertyNames(a, b) {
return a.localeCompare
? a.localeCompare(b)
: defaultCompare(a, b);
}
function getJsonDataWithNormalizedKeyOrder(data) {
let value;
if (Array.isArray(data)) {
value = data.map(getJsonDataWithNormalizedKeyOrder);
} else if (data && (typeof data === 'object')) {
value = Object
.getOwnPropertyNames(data)
.sort(comparePropertyNames)
.reduce((obj, key) => {
obj[key] = getJsonDataWithNormalizedKeyOrder(data[key])
return obj;
}, {});
} else {
value = data;
}
return value;
}
const sampleList = [{
name: 'John',
value: 1,
}, {
value: 1,
name: 'John',
}, {
name: 'Carla',
value: 15,
}];
function hasDifferentValues(arr) {
// stringified first item reference.
const referenceItem = JSON.stringify(getJsonDataWithNormalizedKeyOrder(arr[0]));
// run `some` from a sub-array which excludes the original array's first item.
return arr.slice(1).some(item =>
referenceItem !== JSON.stringify(getJsonDataWithNormalizedKeyOrder(item))
);
}
console.log(
'hasDifferentValues(sampleList) ?',
hasDifferentValues(sampleList)
);
console.log(
'hasDifferentValues(sampleList.slice(0,2)) ?',
hasDifferentValues(sampleList.slice(0,2))
);
console.log(
'hasDifferentValues(sampleList.slice(0,1)) ?',
hasDifferentValues(sampleList.slice(0,1))
);
console.log(
'hasDifferentValues(sampleList.slice(1)) ?',
hasDifferentValues(sampleList.slice(1))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
评论