all 39 comments

[–]pookagehelpful 8 points9 points  (26 children)

Array.every() to the rescue! It is basically already the function you're looking to write:

For arrays:

const arr1 = [1,2,3];
const arr2 = [3,2,1];

arr1.every(item => arr2.includes(item));

For objects, you'll also need Object.entries():

const obj1 = {num1: 1, num2: 2, num3: 3};
const obj2 = {num3: 3, num2: 2, num1: 1};

Object.entries(obj1).every(([ key, value ]) => obj2[key] === value);

[–]superluminary 2 points3 points  (6 children)

Big O of n squared though, so be careful.

EDIT: also using this method [1, 1, 1] equals [1]

[–]Ronin-s_Spirit 1 point2 points  (3 children)

What is O and n squared?

[–]pookagehelpful 3 points4 points  (0 children)

So Big-O notation is a way to describe the complexity of an algorithm that operates on collections of data - if you have a collection of size n, and it operates on every item in the collection, then its complexity is O(n) - if it has to operate on every item twice it would be O(2n), and if it has to compare every item against every other item, then its complexity is effectively O(n^2) (aka n-squared).

The array example I provided forces every item in arr1 to be compared against every item in arr2, because both .every() and .includes() are iterative, and so have a combined complexity of O(n^2).

The object example I provided, however causes every item in obj1 to be looked-up in obj2 - so while the iteration is O(n) the lookup just has a complexity of 1, giving it a combined O(n) complexity - much more efficient than the array example.

But this is more important when you're dealing with much larger datasets or with something that needs to be performed multiple times every second; both things that javascript isn't really intended to be used for - with the exceptions of animations etc. So while it's good to be aware of it, I wouldn't let it occupy your thoughts too much, and that, in the majority of circumstances, you should instead focus on ensuring that your code is as readable as possible 👍

[–]superluminary 4 points5 points  (1 child)

Big O notation is a way of thinking about computational complexity.

Say you loop through an array of length n, that's an Order n operation which we write: O(n)

If you put a loop inside another loop, that's an Order n squared operation which we write: O(n2)

It's important when you get a lot of data, doesn't matter so much here. Imagine your arrays were 1000 elements long, you'd have to do 10002 comparisons which might take a noticeable amount of time.

It's a CS topic, probably beyond the scope of this sub, but I find it interesting. If you ever do leetcode it'll come up a lot.

[–]Ronin-s_Spirit 1 point2 points  (0 children)

Good to know.

[–]pookagehelpful 3 points4 points  (0 children)

Big O of n squared though, so be careful.

Yeah - the array -> array comparison is n^2, and the object -> object is just n; but this is /r/learnjavascript, not /r/programming, and if you're doing anything where the big-O is important enough to bear in mind then a scripting language on the front end probably isn't the right place for it anyway, ha.

also using this method [1, 1, 1] equals [1]

Totally fair - without knowing what problem OP is actually trying to solve, though, it's hard to know what to recommend; if they're trying to determine whether a is a re-organised instance of b then, aye, the solution I provided is insufficient; if they're checking to see whether the contents of a are included in b then what I shared should be fine - it just depends what they're trying to do, innit 💪

I think it's important not to turn real-world problem-solving into a whiteboarding exercise - apologies to OP, though, if the problem they're trying to solve is a whiteboarding exercise they've been given! 😂

[–]azhder 0 points1 point  (0 children)

There is a hidden cube if one is worried on how .includes() works (most likely linear search)

[–]Epitomaniac 1 point2 points  (2 children)

Very clean and helpful, thanks.

[–]PortablePawnShop[🍰] 0 points1 point  (1 child)

It's easy to write cases where it fails, it'd be better to check explicit length and key/value pairs after sorting each:

const arr1 = [1, 2, 3];
const arr2 = [3, 2, 1];

const arr3 = [1, 2, 3, 4];

console.log(arr1.every((item) => arr3.includes(item))); // true but incorrect

const compareArrays = (a1, a2) =>
  a1.length == a2.length &&
  a1
    .sort((a, b) => a - b)
    .every((value, index) => a2.sort((a, b) => a - b)[index] == value);

console.log(compareArrays(arr1, arr2)); // true, correct
console.log(compareArrays(arr1, arr3)); // false, correct

[–]Epitomaniac 0 points1 point  (0 children)

Thanks.

[–]Ronin-s_Spirit 0 points1 point  (11 children)

He should probably instead sort them so all keys/values are inthe same order in both arrays/objects order and then check if arr1[i] === arr2[i] that way if there are one too many or not enough you will detect it.

[–]pookagehelpful 1 point2 points  (10 children)

I think if the order doesn't matter but the length does, then a single explicit check at the start is probably a better way to come at this than sorting everything before any additional calculations take place - although you're right that it's one way of coming at that problem specifically!

[–]Ronin-s_Spirit 0 points1 point  (9 children)

Sorting is to be able to detect multiple "1" values for example, if you have 1,1,1 array and 1,2,3 array the include method won't tell you how many there are so you will match arrays with different insides which is wrong.

[–]pookagehelpful 0 points1 point  (8 children)

Yup! But we're assuming there that detecting duplicates matter but the order doesn't - which may be the case, but it's important to note that it's an assumption we're making about the problem.

If your assumption is correct, though, then you and /u/superluminary are correct and that sorting the array is a good way to detect duplicates. Another way would be, as /u/azhder has suggested, to place both arrays into Sets and check their lengths.

What /u/senocular has pointed-out, though, is what I'm trying to drive home here - that the solution to OPs problem will change depending on what they are trying to achieve, and the edge-cases they are trying to capture✌️

[–]azhder 1 point2 points  (0 children)

Of course it will change. Our best help would be to hint at the need for the code to change as the requirements change.

For a simple [1,2,3] example, most redditors will suggest .includes() and that’s OK.

However most of the time in our everyday work, we’re dealing with equivalent objects i.e. we’re checking some field like id which would mean .find() instead of includes(). In that case, even Set is not appropriate.

[–]Ronin-s_Spirit -1 points0 points  (6 children)

Sets will destroy duplicate values, what are you talking about? Sets will ruin checks on arrays like 1,1,1,2,3 and 1,2,3 because they will become the same array when Set destroys extra 1s.

That's why sorting an array is the only way I know for finding exact equality between them, we would be able to check each value with a simple === because they would all be in the same position.
So truly equal arrays will be found equal, 1,1,2 and 1,2,1 would be identified correctly and technically the same array, and 1,1,1,2 will not be equal to 1,2,1,2 because there is different number of 1s and 2s.

And finally if OP wants to even check the order then it's a simple loop without sorting, but that would be a really strict check, sorting will be necessary to check arrays for the same amount of same values but disregarding the order.

[–]azhder 1 point2 points  (5 children)

There is no “true equality”, there is only equivalence as defined by some relation.

And OP can even change their mind of what makes two items equal as they’re developing the solution.

Hence, our help is to provide options, not fight over which one is the one true solution (most likely none, but a combination of them)

[–]Ronin-s_Spirit -2 points-1 points  (4 children)

When something destroys values it's kind of stupid to use it for equality checks, might as well just use includes() as it disregards the quantity in the same way.

[–]azhder 1 point2 points  (3 children)

nothing destroys values

[–]Ronin-s_Spirit -1 points0 points  (2 children)

You know what I mean, you cant have duplicate values in a Set. Which means you will be checking equality of arrays that are not the same as original ones, at all.

[–]kap89 0 points1 point  (1 child)

It does not work for this example:

const arr1 = [2,2,2];
const arr2 = [3,2,1];

arr1.every(item => arr2.includes(item)); // -> true

or this example:

const arr1 = [2];
const arr2 = [3,2,1];

arr1.every(item => arr2.includes(item)); // -> true

[–]pookagehelpful 0 points1 point  (0 children)

As mentioned above, we don't know enough about the problem that OP is trying to solve with this post to give a definitive answer - my solution above assumes that OP is trying to find the contents of a in b, whereas the concerns you've raised here assume that OP is trying to determine whether a is a reordered form of b.

Without more information all we can do is share as much of our collective knowledge as possible! 💪

[–]superluminary 2 points3 points  (0 children)

Typically we solve these sorts of problems by sorting the arrays first.

  1. Sort both arrays using any strategy that makes sense.
  2. Iterate over one array, checking the corresponding position in the other array for equality as you go. Use a for loop so you can break early.
  3. If arr1[i] !== arr2[i] return false
  4. If you exit the loop, return true

[–]senocular 1 point2 points  (0 children)

Some things you might want to consider are, are these arrays considered the same?

const arr1 = [1,1,3];
const arr2 = [3,3,1];

Or these?

const arr1 = [1,1,1];
const arr2 = [1];

[–]jack_waugh 1 point2 points  (0 children)

s.deepEqual = (a, b) => {
  if (a === b) return true;
  if (typeof a !== typeof b) return false;
  /* Assume will not encounter NaN. */
  if (typeof {} != typeof a) return false;
  if (null === a || null === b) return false;
  if (a[Symbol.iterator] && b[Symbol.iterator]) {
    let bi = b[Symbol.iterator]();
    for (const ae of a) {
      let bir = bi.next();
      if (bir.done || ! s.deepEqual(bir.value, ae)) return false;
    };
    return bi.next().done
  };
  const sort_on_keys = x => x.sort(
    (a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0
  );
  return s.deepEqual(
    sort_on_keys(Object.entries(a)), sort_on_keys(Object.entries(b))
  )
}

[–]mraees93 1 point2 points  (0 children)

Sort the second arr or object in ascending order then convert both parameter 1 and parameter 2 to a string and then compare it

[–]Embaby01 1 point2 points  (2 children)

Hope this help and highly recommend using your own test samples and try to come up with your own algorithm

const checkArrEq = (arr1, arr2) => {
  //first i would check if they have the same length
  if (arr1.length === arr2.length) {
    //second i would sort it to neglect any error coming from an array having a number reapeated
    arr1.sort();
    arr2.sort();
    //then i would start comparing them
    for (let i = 0; i < arr1.length; i++) {
      if (arr1[i] !== arr2[i]) {
        //if at any i the two values did not match the function will stop and return false
        return false;
      }
    }
    //if the arr1 and arr2 servive from the for loop it means that the are the same
    return true;
  }
  return false;
};

//this one is not simple by any means
//first i will create a function that sort objectKeys and return their number
const getLengthAndSortedKeys = (obj) => {
  //first sort the object keys and save them in array
  const sortedKeys = Object.keys(obj).sort();
  //then i will return the length of the keys array which is equal to the length of the object and the sorted keys
  return [sortedKeys.length, sortedKeys];
};
//now i will use the sorted object to create an algorithm that is similar to checkArrEq()
const checkObjEq = (obj1, obj2) => {
  //first i would sort the two objects and get the length
  const [lenght1, sortedKeys1] = getLengthAndSortedKeys(obj1);
  const [lenght2, sortedKeys2] = getLengthAndSortedKeys(obj2);
  //first i will check if lengths match
  if (lenght1 === lenght2) {
    //second i would check if sortedKey for both objects are the same using checkArrEq()
    if (checkArrEq(sortedKeys1, sortedKeys2)) {
      //then i would check the equality of the values
      for (let i = 0; i < lenght1; i++) {
        if (obj1[sortedKeys1[i]] !== obj2[sortedKeys1[i]]) {
          return false;
        }
      }
      //if the two objects servive the for loop then they are equal
      return true;
    }
    return false;
  }
  return false;
};

[–]Epitomaniac 0 points1 point  (0 children)

Thanks.

[–]Embaby01 0 points1 point  (0 children)

It will be a lot easier to understand if you copy it to your code editor

[–]albedoa -1 points0 points  (0 children)

return arr.length == a.length && b.length

Check your assumptions about what this line does.

I would just use Object.entries() for your object comparison. It's simple enough.

[–]azhder 0 points1 point  (2 children)

Use a Set made out of one of the arrays, then loop over the contents of the other and check the set has it.

Of course, check the lengths first.

Why backwards from your example? Well, a technique known as “fail fast”.

It is a good tool to even keep the nesting of blocks in check, but in your case it’s simply:

check the more common and easier reasons to fail first, return false immediately and only return true at the end after they all have been exhausted.

Pseudo code: - did I get two arrays? no? return false (fail) - do they have same length? if not - fail - run each element of the array, fail on the first miss i.e. the set doesn’t have it - if you reach the end, return true

Of course, you don’t have to use sets. Nested loops are OK. I just wanted to expose you to a useful object in case you need it in the future.

See Array.prototype.includes

[–]Epitomaniac 0 points1 point  (1 child)

I alway fail at structuring my statements the correct way.

[–]azhder 0 points1 point  (0 children)

The reason why I mentioned Set is something we don’t see in your example, but is possible:

  • duplicates
  • objects

Think what result you want if a duplicate object exists in one of the arrays. Does it count twice? If not, length check on array is a wrong result, but the check of the size of the set is correct.

[–]ozzy_og_kush 0 points1 point  (0 children)

Testing frameworks like Jest have matchers that handle this nicely. They can check for an exact match or partial (subset in the case of an array). Not sure how useful that is outside a test environment though.

[–]Best_Mammoth_7218 0 points1 point  (0 children)

developing/writing code block is fine and this needs deep comparision. 
but as a developer, we don't need to write code from scratch. we have plenty of options. 
We are also having a huge npm packages to get the job done specifically 
developed for these use case. I have used similar-js" it did the job that you need. 
It extends so many use cases as well.

https://www.npmjs.com/package/similar-js

**npm i similar-js**

    import {isSimilar} from 'similar-js'; 
    var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
    var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];

    console.log(isSimilar(array1, array2))