FreeCodeCamp and JavaScript: Using reduce() and the arguments object to solve a problem

So at last, here’s the FreeCodeCamp problem I was struggling with.  It’s called Symmetric Difference.

NOTE: IF YOU HAVEN’T COMPLETED THIS – Go and try it first.  If you just copy my solution, you won’t learn anything, and when it comes time to interview, you won’t pass the coding test.

“Create a function that takes two or more arrays and returns an array of the symmetric difference (△ or ⊕) of the provided arrays.

Given two sets (for example set A = {1, 2, 3} and set B = {2, 3, 4}), the mathematical term “symmetric difference” of two sets is the set of elements which are in either of the two sets, but not in both (A △ B = C = {1, 4}). For every additional symmetric difference you take (say on a set D = {2, 3}), you should get the set with elements which are in either of the two the sets but not both (C △ D = {1, 4} △ {2, 3} = {1, 2, 3, 4}).”

Now, as I write this, I haven’t solved it, so you’re going to see my thought process (somewhat edited) in action.

So, at first glance, this looks like the problem we solved last time, Seek and Destroy.  There are some differences, though.  First, we want to keep the elements that are in either but not both, rather than just eliminating the ones in the first array and returning it.  Second, we want to compare more than two arrays in some cases.

Still, I think we can do this: Make the first array equal to a variable we’ll call compare.  You know what, I’m going to do this as pseudocode because it will be harder to understand if I don’t.  So

var compare = arguments[0];

Then we’ll use a loop:

for (var i = 1; i <arguments.length; i ++) {
}

The loop is going to cycle through the rest of the elements in arguments so we end up comparing all the arrays.  Inside the loop we’ll compare the next argument to the compare array, and create a new array that will have items that are in either but not both arrays. Well figure the code for this in a minute.  The new array will be added to on each iteration of the loop, so we need to declare it outside the loop.  We’ll continue to execute the loop until we’ve processed the last element in the arguments object.  Then we’ll return the new array which should meet the conditions of the assignment.

So, how to compare them and create the new array?  Maybe two loops, one to compare arraya to array b, and then another to compare array b to array a?

UPDATE:I solved it.  But here’s kind of what I went through in solving it.  Next time I’ll try to write while I’m solving a challenge instead of trying to remember afterwards what I did!

The first thing I did was combine all the arguments into one giant array, because I thought we wanted to eliminate ALL of the elements that ALL of the arrays had in common.  It turns out that this is not the correct answer.  Instead, you compare the first two arrays, get an answer, then compare the answer to the next array, get an answer, then compare THAT answer to the NEXT array, and so on until you’ve gone through all the arrays in arguments.

So it was back to my original plan.  Now, if you only compare the first array to the second, you’ll end up with the elements in the first array that are not in the second array as your answer.  But you won’t have the elements in the second array that aren’t in the first as part of your answer.  No problem, we’ll just compare the second array to the first…oh wait, we changed the first array already, so that won’t work!  So, what we need to do is make a copy of the first array to hold temporarily.

Oh! Sneaky JavaScript!  Creating a new variable and setting it equal to the first array means they both point to the SAME array which means when you change the first array, both variables now point to the modified array!  No good!  Indeed, when you want to make a copy of an array, you need to loop through it and copy each value in the array to the new array.

So, we run a loop and modify the first array, then run a loop and modify the second array.  Then we use the concat() method to mash them together into one array. (I covered the concat() method in a previous entry).  That array becomes the first array and we then compare it to the next array in the arguments.  We continue this loop until we hit the end of the arguments.

All done, right?  Um…no.

Looking at the answers we get back, we see that the final array we returned does indeed have all the elements that are in either but not in both, so what’s the problem?  For example, one answer we get back is:

{1, 1, 4, 5, 5}

But take a look.  How many UNIQUE numbers do you see?  There are 5 numbers, but only three unique numbers – 1, 4, 5.  And {1, 4, 5} is the right answer according to the feedback.  So what we need to do is eliminate the duplicate numbers.

So, what we’ll do is create (yet) another array, called final, that will hold all the non-duplicated numbers.  We’ll then loop through the  array with the duplicates and use the indexOf() function, which I also covered in a previous post.  What we are doing is looking to see if there is another duplicate after the number we’re looking at; if so, we ignore the number and move to the next element.  If there are no other duplicates after the number we are currently looking at, then we’ll add that number to the Final array.  This way there will only be one of each number in the Final array.

Now, we can return the Final array, which has the right answers and no duplicates, and we’re done!  Here’s the final code:

function sym(arr) {
  // Remove all the values
  var compare = arguments[0];
  var final = [];
// Do this until the last argument
  for (var count = 1; count < arguments.length; count++) {
    var args = arguments[count];
// Create a temporary copy of the first array.
    var temp = []; // A new empty array
    for (var i = 0, len = compare.length; i < len; i++) {
      temp[i] = compare[i];
    }
// Compare the first array to the second
    for (var i = 0; i < args.length; i++) {
      for (var x = compare.length; x >= 0; x–) {
        if (args[i] === compare[x]) {
          compare.splice(x, 1);
        }
      }
    }
// Compare the second array to a copy of the first
// since we’ve already modified the first array in the
// loop above.
    for (var y = 0; y < temp.length; y++) {
      for (var j = args.length; j >= 0; j–) {
        if (temp[y] === args[j]) {
          args.splice(j, 1);
        }
      }
    }
// combine the modified first array with the
// modified second array
    compare = compare.concat(args);
  }
// Get rid of the duplicates
  for (var item = 0; item < compare.length; item++) {
    var current = compare[item];
    if (final.indexOf(current) < 0) {
      final.push(current);
    }
  }
  return final;
}
sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]);

Advertisements

FreeCodeCamp and JavaScript: the indexOf Method()

Today’s post is on the indexOf() method, which will tell you the first position in an array that an element appears, or returns a -1 (negative one) if it isn’t in the array.  This is handy when you want to know whether a number, character, string, or whatever is in an array or not.  It’s also useful when searching for duplicates.

From MDN, the syntax:

arr.indexOf(searchElement[, fromIndex = 0])

We have two parameters in the above method:

searchElement – This is whatever element you want to find.

fromIndex – this is the spot to start at in the array.  It’s optional.  If you give a number that’s equal to or greater than the length of the array (array.length), of course, you’re going to get a -1 back.  As with several of the methods I’ve discussed on the blog, you can put a negative number for this parameter and it will start from that index but go from front to back in the array.  The default, if you don’t put anything, is index 0.

Finally, note that indexOf() uses strict equality, or ===, too.  Keep that in mind!

MDN then gives a number of simple examples:

var array = [2, 9, 9];

array.indexOf(2); // 0

2 has an index of 0, because it’s the first element in the array.  Remember, arrays start with element[0] in JavaScript!

array.indexOf(7); // -1

There is no -7 in the array, so the indexOf() method returns -1.

array.indexOf(9, 2); // 2

We are looking for the index of 9, starting at element[2].  Element[2] just happens to be 9, so we get 2 back.

array.indexOf(2, -1); // -1

We are looking for 2, beginning at element 1 – 1 back from the length of the array, which is 2.  Element 1 is -1, and then we move FORWARD, to the next element.  There ISN’T a next element, so the search terminates and you get -1 back.

array.indexOf(2, -3); // 0

We are looking for 2, beginning at element -1.  That doesn’t exist, of course, so the search will begin at element[0], which happens to be 2.  So we get a 0 back.

Next, MDN asks what happens if we want to know ALL of the occurrences of an element in an array, not just the first?  Here’s the code:

var indices = [];
var array = [‘a’, ‘b’, ‘a’, ‘c’, ‘a’, ‘d’];
var element = ‘a’;
var idx = array.indexOf(element);
while (idx != -1) {
  indices.push(idx);
  idx = array.indexOf(element, idx + 1);

console.log(indices);
// [0, 2, 4]

First, we create a blank array called ‘indices’.  This is going to contain the locations of all the elements that match the one we’re looking for.  Next we create an array and then we decide we’re going to look for the character ‘a’.

We assign the index of a to the variable idx.  This line also searches the array to make sure the element we are looking for is in the array to begin with.

Now we enter a while loop.  The loop will begin by checking to see if idx is -1.  If this is the first time through, and the line before it has already set the value of idx to -1 because the element we want isn’t even in the array, we’ll skip the loop and return -1.

Then the while statement ‘pushes’ the value of idx into indices.  If this is the first time through, the first value to be pushed into indices is the first location of the element we are looking for.  If not, this will be the next location found by the lines below.

the next line is idx = array.indexOf(element, idx + 1);  This is the heart of the loop.  We perform the same search as before, looking for the next occurrence of the element, but starting at the element AFTER the occurrence we already found.  That way we don’t keep getting the same occurrence back.  We work to the end of the array.  Eventually we’ll hit a point where there are no more occurrences of our element to be found, or we’ll hit the end of the array, and we’ll get -1 back.  This will terminate the loop.

The next line prints the indices array with all the indices (thuis the name of the array) where our element is to be found.  In this case, the letter a can be found at elements{0], [2], and [4].

Finally, MDN gives an example where we want to search an array, see if an element is in it, and if not, modify it.

function updateVegetablesCollection (veggies, veggie) {
  if (veggies.indexOf(veggie) === -1) {
             veggies.push(veggie);
             console.log(‘New veggies collection is : ‘ + veggies);
         } else if (veggies.indexOf(veggie) > -1) {
             console.log(veggie + ‘ already exists in the veggies collection.’);
         }}

We have a function called updateVegetablesCollection that receives two parameters, an array called veggies and a veggie to add.  First, we check the whole array to see if the element is in it already.  This is the if statement saying that if we look and get -1 back then proceed, else print that it already contains that element in the console.

Assuming it’s a new element, then, we’re going to add it to the array and output the modified array to the console.

Let’s see this in action.

     var veggies = [‘potato’, ‘tomato’, ‘chillies’, ‘green-pepper’];

So, we’re going to work with an array called veggies that contains the elements ‘potato’, ‘tomato’, ‘chillies’, and ‘green-pepper’.

updateVegetablesCollection(veggies, ‘spinach’);
          // New veggies collection is : potato, tomato, chillies, green-pepper, spinach

We’re going to add spinach to the veggies array.  After we do so, the array now contains it right there at the end.

updateVegetablesCollection(veggies, ‘spinach’);
// spinach already exists in the veggies collection.

We’re going to add spinach to the veggies array…again.  But this time the indexOf() method returns -1, because we already added spinach.

And that’s the indexOf() method!