0

Whats the best way to check if the arrays within an array are the same in my case?

var arrSession = [
  {
    type: '1',
    usecase: [ '1' ]
  },
  {
    type: '1',
    usecase: [ '1' ]
  }
];

var checkUsecase = arrSession.every(isSame);

function isSame(obj, index, arr) {
    if (index === 0) {
        return true;
    } else {
        return (
            (obj.type === arr[index - 1].type) &&
            (obj.usecase === arr[index - 1].usecase)
        );
    }
}

console.log('checkUsecase:');
console.log(checkUsecase);

The 'usecase' object within the array use to be a string '' and the isSame function used to work. But now they are arrays too. How to change the isSame function?

Here is a fiddle: https://jsfiddle.net/3j0odpec/

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Philipp M
  • 3,306
  • 5
  • 36
  • 90
  • [This question](https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript) is worth a read. Comparing arrays in Javascript is unfortunately not the easiest thing to do. – Liftoff Jun 20 '23 at 08:30
  • What is the datatype that is used for the `usecase` array elements? Always string? Or maybe more objects, arrays, sets, maps, dates,... etc? – trincot Jun 20 '23 at 11:50
  • @PhilippM ... Regarding all the provided answers, are there any questions left? – Peter Seliger Jun 26 '23 at 11:09
  • @PeterSeliger No, this is more than perfect! :-) ... both answers are great, hard to decide which to accept. Thanks a lot for the help, much appreciated! – Philipp M Jun 27 '23 at 08:38

2 Answers2

4

First we make some recursive compare function to walk nested arrays/objects. Then we run it over the array comparing that the current item is equal to the previous one.

And it's definitely faster than comparing JSON.stringified array items of the OP's data:

enter image description here

const arrSession = [
    {
        type: '1',
        usecase: ['1']
    },
    {
        type: '1',
        usecase: ['1']
    }
];

const isSameItems = arrSession.every((item, idx, arr) => idx === 0 || isSame(item, arr[idx - 1]));

console.log('the array has all items the same: ', isSameItems);

function isSame(a, b) {

    const iterator = Array.isArray(a) && Array.isArray(b) ? a.keys() : 
    (typeof a === 'object') && (typeof b === 'object') ? Object.keys(a) : null;

    if (iterator) {
        for (const i of iterator) {
            if (!isSame(a[i], b[i])) {
                return false;
            }
        }
        return true;
    }
    
    return a === b;

}

<script benchmark data-count="1">

    const arrSession = JSON.parse(JSON.stringify(Array.from({ length: 300000 }).reduce(arr => arr.push(...[
        {
            type: '1',
            usecase: ['1'],
            child: {
                type: '1',
                usecase: ['1'],
                child: {
                    type: '1',
                    usecase: ['1'],
                    child: {
                        type: '1',
                        usecase: ['1'],
                    }
                }
            },
            child: {
                type: '1',
                usecase: ['1'],
            }
        }
    ]) && arr, [])));

    // @benchmark JSON.stringify

    arrSession.every((item, idx, arr) => idx === 0 || JSON.stringify(item) === JSON.stringify(arr[idx - 1]));

    // @benchmark recursive isSame

    function isSame(a, b) {

        const iterator = Array.isArray(a) && Array.isArray(b) ? a.keys() : 
        (typeof a === 'object') && (typeof b === 'object') ? Object.keys(a) : null;

        if (iterator) {
            for (const i of iterator) {
                if (!isSame(a[i], b[i])) {
                    return false;
                }
            }
            return true;
        }

        return a === b;

    }

    // @run 
    
    arrSession.every((item, idx, arr) => idx === 0 || isSame(item, arr[idx - 1]));
    
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
Alexander Nenashev
  • 8,775
  • 2
  • 6
  • 17
  • Use of `__proto__` is [discouraged](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) -- see the multiple notices on the MDN page. – trincot Jun 20 '23 at 11:44
  • Also a great answer, thanks for the help! Much appreciated! Hard to decide which answer to accept! – Philipp M Jun 27 '23 at 08:42
3

This answer too chooses the already proposed some based comparison approach ...

const isFullItemEquality = arrSession
  .every((item, idx, arr) =>
    idx === 0 || isDeepDataStructureEquality(item, arr[idx - 1])
  );

... only that it does not utilize JSON.stringify.

For sameness / equality comparison of JSON conform data structures where performance is important as well, one might consider going with the following recursive implementation of isDeepDataStructureEquality ...

<script>
  // implemented by Peter Seliger
  function isDeepDataStructureEquality(a, b) {
    let isEqual = Object.is(a, b);

    if (!isEqual) {
      if (Array.isArray(a) && Array.isArray(b)) {

        isEqual = (a.length === b.length) && a.every(
          (item, idx) => isDeepDataStructureEquality(item, b[idx])
        );
      } else if (
        a && b
        && (typeof a === 'object')
        && (typeof b === 'object')
      ) {
        const aKeys = Object.keys(a);
        const bKeys = Object.keys(b);

        isEqual = (aKeys.length === bKeys.length) && aKeys.every(
          (key, idx) => isDeepDataStructureEquality(a[key], b[key])
        );
      }
    }
    return isEqual;
  }
</script>
<script>
  // implemented by Alexander Nenashev
  function isSame(a, b) {

    const iterator = Array.isArray(a) && Array.isArray(b) ? a.keys() : 
    (typeof a === 'object') && (typeof b === 'object') ? Object.keys(a) : null;

    if (iterator) {
        for (const i of iterator) {
            if (!isSame(a[i], b[i])) {
                return false;
            }
        }
        return true;
    }

    return a === b;

  }
</script>
<script benchmark data-count="10">

  const arrSession = JSON.parse(JSON.stringify(Array
    .from({ length: 300000 })
    .reduce(arr => arr.push(...[{
      type: '1',
      usecase: ['1'],
      child: {
        type: '1',
        usecase: ['1'],
        child: {
          type: '1',
          usecase: ['1'],
          child: {
            type: '1',
            usecase: ['1'],
          },
        },
      },
      child: {
        type: '1',
        usecase: ['1'],
      }
    }]) && arr, [])));

    // countercheck implementations with an unequal data structure.
    // arrSession.at(-1).child.usecase[0] = 0;

    // @benchmark JSON.stringify
    arrSession.every((item, idx, arr) =>
      idx === 0 || JSON.stringify(item) === JSON.stringify(arr[idx - 1])
    );
    // @benchmark recursive isSame
    arrSession.every((item, idx, arr) =>
      idx === 0 || isSame(item, arr[idx - 1])
    );
    // @benchmark recursive isDeepDataStructureEquality
    arrSession.every((item, idx, arr) =>
      idx === 0 || isDeepDataStructureEquality(item, arr[idx - 1])
    );
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
Alexander Nenashev
  • 8,775
  • 2
  • 6
  • 17
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • Use of `__proto__` is [discouraged](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) -- see the multiple notices on the MDN page. – trincot Jun 20 '23 at 11:45
  • @PeterSeliger my bad was to hurry. i definitely saw that using `reduce()` wasn't optimal to handle just 2 values a & b. changed it with the same logic as before just avoiding `reduce()`. so now my function is more comparable to yours in speed terms. i guess you're not against to fix my code in your answer. that's a some drawback of stackoverflow that if you aren't fast enough you get no points even your answer is the best... – Alexander Nenashev Jun 20 '23 at 15:23
  • @PeterSeliger comparing array and object key length was a smart move. i did the same in my code before but forgot to add this check today – Alexander Nenashev Jun 20 '23 at 15:31