FCC Bonfire Series 143: Symmetric Difference

This next bonfire, Symmetric Difference, seems to be a brick wall for many people; when we are done with it, you’ll see how abstraction turns a complex problem into a series of simple smaller steps.

Symmetric Difference will have us write a function that given any number of arrays, returns an array displaying their symmetric difference. If you are wondering what symmetric difference means, don’t worry, I’ve got you covered:

Symmetric Difference: The set of elements belonging to one but not both of two given sets.

It seems simple enough at first sight, but the problem comes in as we realize that we need to compute the symmetric difference for an arbitrary number of arrays, and not just two. Wrong. We are going to do exactly that, you’ll see how that works as we go on.

Let’s start by constructing the bare-bones of our function. We are going to be getting an unknown number of arrays as input, so let’s use the arguments object and turn it into an array as we did here. We will also create a new function inside, and have it take in just two arrays; this function will get us the symmetric difference for those two arrays in the following steps.

function sym() {
    var args = [];
    for (var i = 0; i < arguments.length; i++) {        // We can also use: var args = Array.prototype.slice.call(arguments);
        args.push(arguments[i]);
    }
    // We now have all the input arrays inside args.

    function symDiff(arrayOne, arrayTwo) {
        // Get sym difference for these two arrays here.
    }
}

Now, onto the actual logic, let’s get the symDiff function to work first; then, we’ll see how to apply it to any number of arrays. From now on, we’ll exclusively work on the symDiff function; we’ll get back to the outer function later.

To get the symmetric difference for two arrays, we need to do two things (heh). First, loop over the first array, and check if the current item is present in the second using the indexOf method (we also need to check if the item already exists in the results array, so we don’t get repeated numbers). Then, we loop over the second array, and add any items not present in the first or results array. The idea is that each number only gets added to the results array once, as long as it isn’t present within both input arrays.

This is what it looks like:

function symDiff(arrayOne, arrayTwo) {
    var result = [];

    arrayOne.forEach(function(item) {   // Loop over the first array. You can use any type of loop you prefer!
        if (arrayTwo.indexOf(item) < 0 && result.indexOf(item) < 0) {   // Check if current item exists on the second or result array.
            result.push(item);                                          // If not, add it to the result.
        }
    });

    arrayTwo.forEach(function(item) {   // We do the exact same thing for the second array, and check agains the first array.
        if (arrayOne.indexOf(item) < 0 && result.indexOf(item) < 0) {
            result.push(item);
        }
    });

    return result;
}

This thing should be working properly now. You can use any kind of loop here (such as for, which should be faster), but forEach keeps it a little more understandable this time around.

You may think that we still have some ways to go, as we only got the symmetric difference for two arrays, but we are actually almost done. Remember our friend reduce? We’ll take advantage of it. Just pass in the symDiff function and apply the reduce method to the args array, this will apply the symDiff function to every array and compare it to the previous value (an ever increasing array of symmetric difference values):

function sym() {
    var args = [];
    for (var i = 0; i < arguments.length; i++) {
        args.push(arguments[i]);
    }

    function symDiff(arrayOne, arrayTwo) {
        var result = [];

        arrayOne.forEach(function(item) {
            if (arrayTwo.indexOf(item) < 0 && result.indexOf(item) < 0) {
                result.push(item);
            }
        });

        arrayTwo.forEach(function(item) {
            if (arrayOne.indexOf(item) < 0 && result.indexOf(item) < 0) {
                result.push(item);
            }
        });

        return result;
    }

    return args.reduce(symDiff);    // We apply the reduce method to the args array, using the SymDiff function.
}

All set to go! This function should take care of the problem at hand, but, as always, you should try and improve it, explore and come up with a different and original solution. Remember that if something isn’t clear, you can shoot me an email, ping me on twitter or post a comment below!