简体   繁体   中英

For an array of JSON conform object literals, how does one compare the equality of such items?

How to check if array contains different values with React.js and typescript?

Example:

[{
  name: 'John',
  value: 1,
}, {
  name: 'John',
  value: 1,
}, {
  name: 'Carla',
  value: 15,
}]

I want to return false if all objects in array are same, and true if there is at least one different object.

You can't use a direct equality comparison since objects will never return equal.

Ie {} != {} , and {name: 'John', value: 1} != {name: 'John', value: 1} .

So firstly you have to decide what you're going to define as 'equal' for these objects.

Let's say for the sake of this that you use just the name field as the test for equality. So if two objects in the array have the same name field, then you'll call them equal. Then you'd define the function:

type NameValue = {name: string, value: string}

const areEqual = (obj1: NameValue, obj2: NameValue): boolean => obj1.name === obj2.name

Of course you can change this function to reflect whatever you define as 'equal'. There are npm packages to help you with deep equality checks too, or you can JSON.stringify both and check that equality

Then you can use Array.some(). Array.some() will return true if any element in the array passes a test. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some

Testing if any element is not equal to the first should be sufficient.

const areNotAllEqual = yourArray.some((currentElement) => {
  return !areEqual(currentElement, yourArray[0])
})

After having commented on and criticized especially the approaches based on JSON.stringify , I want to contribute something on that matter. Since meanwhile all modern JS engines seem to be aware of an object's key order (in how this object was created) and also seem to guarantee such an order for key-iteration one could write a recursive function, which for any deeply nested but JSON-conform JS-objects reestablishes a normalized key-order for such objects but leaves arrays untouched.

Passing such key-normalized objects to JSON.stringify then makes such objects comparable by their's stringified signature...

 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; }

Applying the above to an approach which solves the OP's original problem in a more generic way then might look similar to the next provided lines...

 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; }

I'd check whether stringified object array includes stringified item by referencing to a copied array where I remove the latest item. I'll use Array.every() to compare if all the items match together and then return the opposite value.

However, this can be very heavy operation if an object array is very lengthy

 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));

This is not exactly react specific, but to check for differences you can iterate through the array using every like so.

 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM