2-D Loops in Diff Two Arrays

Tell us what’s happening:
It’s my first attempt and it’s a huge mess, but is there anything salvageable from it that I can work of?

Your code so far


function diffArray(arr1, arr2) {
  var newArr = [];
  for(var i = 0; arr1[i].length; i++){
    for(var j = 0; arr2[j].length; j++){
      var findNow = arr1[i].length.find((i, j)=>{
        if(arr[i] !== arr[j] || arr[j] !== arr[i]){
          return this.i || this.j;
        }
        newArr = findNow;
      })
    }
  }  
   
  return newArr;
}

diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/intermediate-algorithm-scripting/diff-two-arrays/

There are ways to do this in one pass, but I would break the problem down to simplify it a bit. It may end up being quite a bit more code, but it should be easier to understand. I would dump your first try there and start clean.

  1. Collect [in a new array] the elements from array 1 that are not in array 2.
  2. Collect [in a new array] the elements from array 2 that are not in array 1.
  3. Take the result of 1 and concat it onto 2.

You shouldn’t need nested loops here, and it will make your life easier if you use filter over a loop (hint includes is also your friend).

Just as info, avoid doing this: return this.i || this.j;. Using this explicitly is important you are working with prototypes (eg Classes), but don’t just use it like you’re doing there: it obfuscates your intentions, it’s quite confusing, and it’s highly likely to do things you’re not expecting; it will cause you problems.

Also this won’t work: arr1[i].length.find(. arr1[i] is a number, so doesn’t have a length property. And length is a number, it doesn’t have a find property. So that bit of the code will just error. find is a method for arrays.

Your loops will not work for the same reason that arr1[i] is a number so arr1[i].length is undefined which is falsy and your loops will never run.

(Warning: if you still want to use loops, you can’t fix this using just arr1.length, which is a number and so it is truthy and will cause an infinite loop)

I feel a bit more comfortable with this code, but not very hot on the !arr1, !arr2

What is salvageable from this code?

function diffArray(arr1, arr2) {
var newArr = [];
var isNotOne = arr1.filter((notOne)=>{
console.log(arr1.includes(!arr2));
});
var isNotTwo = arr2.filter((notTwo)=>{
console.log(arr2.includes(!arr1));
});

return newArr.concat(isNotOne, isNotTwo);
}

diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);

Yes, that’s almost it

  • includes says “does the given array include a value”. So you’re saying “does arr1 include the value arr2 (the whole array)”. Hint: Filter goes through the array one-by-one, so you have the individual value to check each time, you’ve written the variable representing it in both of your filters, you just haven’t used it.
  • You’ve put the NOT in the wrong place. It’s not arr1 includes NOT arr2. It negates, or flips whatever you put Boolean you put it in front of. Array.includes is true or false, so that is what you should be negating.
  • console.log is a convenience function for debugging provided by browsers: it doesn’t do anything except log to the console, you need to actually return values. At the minute the value in both of those filters is undefined (the return value for console.log is nothing, so undefined by default), which coerces to false, so you’re just saying false for every single value.
1 Like

I have two issues.

  1. I do not appear to be caching any data in either of the variables I’m using for filter().
    2.My final variable lastArr is doing nothing.
    You mentioned earlier a variable that I was not utilizing. I’m wondering if you were referring to isNotOne, isNotTwo . When I console log these two they show nothing. It feels as if I am actually doing nothing. Where am I not plugging in?
function diffArray(arr1, arr2) {

//filtering out what is not included in arr2
var isNotOne = arr1.filter((notOne)=>{
return !arr1.includes(arr2);
});

//filtering out what is not included in arr1
var isNotTwo = arr2.filter((arr1)=>{
return !arr2.includes(arr1);

});

//concatenating what is not in arr2 to what is not in arr1
var lastArr = isNotOne.concat(isNotTwo);
console.log(lastArr)

//returning that
return lastArr;

}

diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);

This always true. [1, 2, 3, 5] does not include the array [1, 2, 3, 4, 5], so includes is false, then flip that it’s true. _It doesn’t matter what values are in arr1, it never includes another array, it’s a set of numbers.

var isNotTwo = arr2.filter((arr1)=>{
return !arr2.includes(arr1);
});

This second one is actually correct, but your naming is still exceptionally confusing. The argument for the callback is the current value filter is on as it iterates through the array, it’s a number not an array

I’ve been doing a bit of research and what I have below solves more tests than previous efforts but still doesn’t pass. My concern is that I’m not doing my parameters on filter() effectively so they do not play the role that I wish them to play in the code.

Yet, I’ve a bit of stronger hold on what should happen in the code. I just don’t know if I’m actually writing it correctly. Where am I wrong?


function diffArray(arr1, arr2) {
  var newArr = [];
  // concat arrays to make it easier
  return newArr = arr1.concat(arr2);
  console.log(newArr);
  //use filter method with three different arguments: val for value, ind for the index, arr for the array
  newArr.filter((val, ind, arr)=>{
  //if has two conditions that must both be met - (1)the array does not include a value that matches the current index (2)the array does not include an index that matches the current index - then return the value
    if(!arr.includes(ind++).indexOf(value) && !arr.includes(0, ind).indexOf(val));
  return val;
  });
  
}

diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);

prevents the rest of your function from running.

Ok. You have one array and you want to check if a value is not in it:

!myArr1.includes(value)

You have many values and you want to check if each one is not in the first array:

val is 1: !myArr1.includes(val)
val is 2: !myArr1.includes(val)
val is 3: !myArr1.includes(val)
...

Those values are in another array, and you want to check the values in that other array are not in the first:

myArr2.filter(val => ...........as above)

You have massively overcomplicated things by doing whatever you’re doing in that if statement, I have no clue as to the thinking there.

Edit: also, what @kerafym02 says, and I’m still not sure why you keep randomly adding ++ to the end of things

What are you trying to do with your if statement? includes() returns a boolean, and then you have indexOf() on it, it will return undefined. Which is falsy. Probably you want to use includes() and indexOf() separately. Or just one of them. Also, if you want to use it, indexOf() returns a number, so you will need to compare it to something.



You can also follow the execution of your code with this:
http://pythontutor.com/javascript.html#mode=edit

I ended up going back to the start and using 2-D loops, but I’ve read that this is the least effective way to handle this sort of problem. What is the most effective in your opinion?

I’m thinking that filter() and includes() seem more effective to me (although I have real problems conceptualizing them and translating that conception into proper organization and allocation of data). What is your opinion?

@bananahair you need to get a little better at debugging things — all of this is hard at first, I really do appreciate that, but you need to avoid just dumping code on the page without understanding what it does. Half of the stuff you’re posting here doesn’t make sense when broken down.

You need to start substituting values into the function you’re writing (in your head or on paper) as well as using console log. Pythontutor is good as well. At the minute you seem to be just throwing stuff at the wall and hoping it works. If you find it impossible to trace a value through the function, then likely either you don’t understand something (so you need to go to the documentation and practice in the console), or the logic is too complicated.

When you substitute values into functions and methods evaluate innermost first, and if you’re chaining methods, left to right.


So let’s look at the if statement, which currently makes zero sense when broken down:

You have val, which is a number, ind which is the index, which is also a number, and arr which is the array you are iterating over.

Let’s say:
arr is [10,20,30]
ind is 1 (we’re on the second item)
so val is 20

if(!arr.includes(ind++).indexOf(value) && !arr.includes(0, ind).indexOf(val));
return val;

So first of all, this looks like:

if (condition);
return val;

That’s not the correct syntax for if, so it’s already broken. Fix that:

if(!arr.includes(ind++).indexOf(value) && !arr.includes(0, ind).indexOf(val)) {
  return val;
}

value isn’t a thing that exists, so I assume you meant val

if(!arr.includes(ind++).indexOf(val) && !arr.includes(0, ind).indexOf(val)) {
  return val;
}

includes takes a single argument, so I’m going to guess here but 0 in includes(0, ind) doesn’t look right so I’m dropping that

if(!arr.includes(ind++).indexOf(val) && !arr.includes(ind).indexOf(val)) {
  return val;
}

The postfix ++ operator returns the value before incrementing, so ind++ is exactly the same as ind where you are applying it. I have no idea why you keep using it

if(!arr.includes(ind).indexOf(val) && !arr.includes(ind).indexOf(val)) {
  return val;
}

So there are the basic errors out of the way, I’ll start substituting values until it breaks again.

if(![10,20,30].includes(1).indexOf(20) && ![10,20,30].includes(1).indexOf(20)) {
  return 20;
}

Now includes. It returns true or false, and 1 is not in that array (why are you checking if the value of the index is one of the numbers in the array?), so:

if(!false.indexOf(20) && !false.indexOf(20)) {
  return 20;
}

Booleans do not have an indexOf method, so this now breaks. Also indexOf returns a positive number if the index is found, or -1 if now. So even if you apply this to an array, 0 is false and every other number is true, so I’m not sure what you think you’re checking for here.

Then I haven’t even touched on the fact that this is a function used for filter. That function has to return true or false, but you’re returning a number.

Hopefully you can see that what you’ve done makes very little sense.


You literally had the answer, then you added loads of stuff to it:

1 Like

Thanks for your help. I definitely see how my code makes no sense and, to be honest, I sensed it from the get-go. I just wanted to find a way to make the code that you said was working to work and I went way overboard. I’m still going after it and it’s still puzzling me, but I appreciate everyone giving me the ole wink-and-nod on where in my code I can access the problem.

1 Like

I don’t mean to be too harsh — I know exactly how it feels when you’re at the stage you’re at where you can kinda get glimpses of how it should all work but you can’t quite express that in code form. You have the right idea each time, so it’s just practise really, it will definitely become easier. Using loops can probably give you a more optimised solution, but it’s IMO easier using filter/includes as you can seperate the three key things (what are the values in arr1 that are unique, what are the values in arr2 that are unique, and then once you have those, how can you join then in one array) – here’s a possible solution (and there are many other ways to do this!), you should be able to see how close you were:

function diffArray(arr1, arr2) {
  const uniqueToArr1 = arr1.filter(val => !arr2.includes(val));
  const uniqueToArr2 = arr2.filter(val => !arr1.includes(val));

  return uniqueToArr1.concat(uniqToArr2);
  // this is exactly the same as:
  //     return [...uniqueToArr1, ...uniqueToArr2]
  // not sure which is more readable
}

@DanCouper I appreciate all the extra work you’re giving me in this regard. And I actually appreciated the directness because it made me take a hard line at my learning process.

I have a question as regards the filter() method. Actually, more of what is probably is confusion with other approaches that I’m conflating with filter(), map() and others of their ilk.

I had always thought the basic blueprint of filter would be var.filter((call ) => {func}) ;
The double parentheses at the important seemed necessary to me because I was in ES6 and was using arrow functions so that the stuff that would go in between the first two ( ( was omitted by the purview of the arrow function. I noticed that this is not the case in the MDN doc and that you also didn’t do it.

Am I just conflating different methods together or is that an actual case where this is necessary?

That is correct, you usually do it that way. This is valid for any function in ES6 not just the callbacks in these methods.
arr.filter((e) => {return func(e)})

But if your callback function is just a return statement you can do it this way and the return statement is implied:
arr.filter((e) => func(e))

And even if you have only one parameter you can also omit the () around the parameter, this way:
arr.filter(e => func(e))

So f you want to filter for just positive numbers you can do:
arr.filter(e => x>0)