Symmetric Difference

Given two sets(forexample setA={1,2,3}andsetB={2,3,4}),the mathematical term"symmetric difference"of two sets isthe set of elements which are ineither of the two sets,but notinboth(A△B=C={1,4}).Forevery additional symmetric difference you take(say onasetD={2,3}),you should get the set with elements which are ineither of the two the sets but notboth(C△D={1,4}△{2,3}={1,2,3,4}).

Definition: the symmetric difference of two sets A and B is the set of elements either in A or in B but not in both.

I had to think hard about this one. I had already dealt with symmetric difference in another challenge (Diff Two Arrays), and thought to start from there. However, I immediately realized that the two challenges differed in one very important respect:

Diff Two Arrays dealt with (only) 2 real arrays, and Symmetric Difference deals with various numbers of arguments. In other words, given the expected outcomes, you don’t know the number of arguments you are dealing with beforehand. Therefore, you have to treat the array arguments like arguments, and not arrays, and since they are being treated like arguments, they have to be converted into real arrays. I also dealt with that particular problem in the Seek and Destroy algorithm.

So how was I to approach this challenge? Good question. Let’s first examine what the arguments object is all about:

Definition:

arguments object: an array-like object corresponding to the arguments passed to a function.

Syntax:

arguments object Syntax

JavaScript

1

arguments

Description:

The arguments object is a local variable within all functions. You can refer to a function’s arguments within the function by using the arguments object. This object contains an entry for each argument passed to the function, the first entry’s index starting at 0. For example, if a function is passed three arguments, you can refer to them as follows:

arguments object entries

JavaScript

1

2

3

arguments[0]

arguments[1]

arguments[2]

The arguments can also be set:

arguments[1] = “new value”;

The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length. For example, it doesn’t have the .pop() method.However, it can be converted to a real Array:

Converting an arguments object into a real Array

JavaScript

1

varargs=Array.prototype.slice.call(arguments);

So that is the first thing I did towards achievement of a solution:

First step towards solution

JavaScript

1

2

3

4

functionsym(args){

args=Array.prototype.slice.call(arguments);

returnargs;

}

Then things got a bit more complicated. I had to find an alternative to my approach to Symmetric Difference in the Diff Two Arrays algorithm. There the function accepted two parameters as arguments for each array, I used a for loop to iterate over each array, and then the .indexOf() === -1 method to identify which elements were present in one array and not the other. Great for when there are only two arrays/arguments. I knew that I should use the .indexOf() method, but the for loop would not be appropriate in this situation. The number of arguments involved at any given time was a wild card. The helpful links provided a hint as to how to proceed.

Since everything had to be “reduced” to one array, I also knew I should use the .reduce() method as well.

Definition:

.reduce(): applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.

Syntax:

.reduce() Syntax

JavaScript

1

arr.reduce(callback[,initialValue])

Parameters:

callback: Function to execute on each value in the array, taking four arguments:

previousValue: The value previously returned in the last invocation of the callback, or initialValue, if supplied.

currentValue: The current element being processed in the array.

currentIndex:The index of the current element being processed in the array.

array:The array reduce was called upon.

initialValue:Optional. Value to use as the first argument to the first call of the callback.

The first time the callback iscalled,previousValue andcurrentValue can be one of two values.IfinitialValue isprovided inthe call toreduce,thenpreviousValue will be equal toinitialValue andcurrentValue will be equal tothe first value inthe array.Ifno initialValue was provided,thenpreviousValue will be equal tothe first value inthe arrayandcurrentValue will be equal tothe second.

Ifthe arrayisempty andno initialValue was provided,TypeError would be thrown.Ifthe arrayhas only one element(regardless of position)andno initialValue was provided,orifinitialValue isprovided but the arrayisempty,the solo value would be returned without calling callback.

After revisiting previous algorithms to see what I could come up with (most specifically Diff Two Arrays and Seek and Destroy), I decided that I should use the .reduce() method and the .filter() method. With the the .filter() method, I killed two birds with one stone because it involves the index of an element:

Definition:

filter(): creates a new array with all elements that pass the test implemented by the provided function.

Syntax:

Syntax

JavaScript

1

arr.filter(callback[,thisArg])

Parameters:

callback: function to test each element of the array. Invoked with arguments (element, index, array). Return true to keep the element, false otherwise.

The range of elements processed by filter()isset before the first invocation of callback.Elements which are appended tothe arrayafter the call tofilter()begins will notbe visited by callback.Ifexisting elements of the arrayare changed,ordeleted,their value aspassed tocallback will be the value at the time filter()visits them;elements that are deleted are notvisited.

But then how was I going to bring everything together? That’s where the .concat() method came into play:

Definition:

.concat(): returns a new array comprised of the array on which it is called joined with the array(s) and/or value(s) provided as arguments.

Syntax:

.concat() Syntax

JavaScript

1

varnew_array=old_array.concat(value1[,value2[,...[,valueN]]])

Parameters:

valueN:

Arrays and/or values to concatenate into a new array.

Description:

concat creates a new array consisting of the elements in the object on which it is called, followed in order by, for each argument, the elements of that argument (if the argument is an array) or the argument itself (if the argument is not an array).

concat does not alter this or any of the arrays provided as arguments but instead returns a shallow copy that contains copies of the same elements combined from the original arrays. Elements of the original arrays are copied into the new array as follows:

Object references (and not the actual object): concat copies object references into the new array. Both the original and new array refer to the same object. That is, if a referenced object is modified, the changes are visible to both the new and original arrays.

Strings and numbers (not String and Number objects): concat copies the values of strings and numbers into the new array.

Concatenating array(s)/value(s) will leave the originals untouched. Furthermore, any operation on the new array will have no effect on the original arrays, and vice versa(only if the element is not object reference).

The Solution in ES5:

Symmetric Difference Solution

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

functionsym(args){

args=Array.prototype.slice.call(arguments);

// this determines the symmetric difference

varpseudo=args.reduce(function(a,b){

returnb.filter(function(i){

returna.indexOf(i)===-1;

}).concat(a.filter(function(i){

returnb.indexOf(i)===-1;

}));

});

// this filter makes sure that there are no duplicates.

returnpseudo.filter(function(element,position){

returnpseudo.indexOf(element)===position;

});

}

Note 12.29.16: When I went back today to publish this article in a Github Wiki, I first revisited the ES6 solution to this algorithm and realized that all the actual outcomes from the execution of my code did not return in the same order of the expected outcomes. It was easy match the order of my actual outcomes to the expected outcomes by adding .sort() to the end of the explicit return statement. However, it was NOT possible in the ES5 solution. I am leaving the ES5 code as is since I will be submitting the ES6 code to FreeCodeCamp. Why is it possible to add .sort() in ES6 and not ES5? If you examine the ES6 solution, you will see that the return statement at the end of the code was possible to write all in one line. That then made it easy to add .sort() to the end of the statement. In addition, there is only 1 return statement. However, with the code in ES5, there are TWO return statements, so it was not possible to add .sort() to the end of the SECOND return statement. Thankfully we now have ES6!

So then where’s the “Symmetric Difference” in all of this you might ask? Let’s revisit the Symmetric Difference in Diff Two Arrays:

Diff Two Arrays Solution

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

functiondiffArray(arr1,arr2){

varnewArr=[];

// Same, same; but different.

for(vari=0;i<arr1.length;i++){

if(arr2.indexOf(arr1[i])===-1){

newArr.push(arr1[i]);// because everything that is present in arr1 is also present in arr2 so an empty array is returned

}

}

for(i=0;i<arr2.length;i++){

if(arr1.indexOf(arr2[i])===-1){

newArr.push(arr2[i]);// because everything that is present in arr2 is not present in arr1 so the difference is return in newArr

}

}

returnnewArr;

}

diffArray([

1,2,3,5

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

In my solution for Diff Two Arrays, I checked for differences between the two arrays using a for loop, and the .indexOf() method. In the first for loop, the loop iterated over the first array, and then checked for the difference between the second array and the first using .indexOf(). In the second for loop, using the same counter i, the loop iterated over the second array, and then checked for the difference between the first array and the second array. In both cases, whatever was not present in one but present in the other, was pushed into newArr. When the loops were completed, newArr was returned and Symmetric Difference was achieved.

In thisSymmetric Difference algorithm, the methods may differ, but the concept is the same. I chose to use the .reduce() method for two reasons:

1. I needed to make sure that only one array was returned, and .reduce() reduces all the array arguments into one array.

2. .reduce() can receive only two arguments as parameters (in this case, previousValue and currentValue). array = args, and is the array reduce() is called upon: args.reduce(). The limit of two arguments is necessary in order to achieve Symmetric Difference.

But simply using .reduce() is not enough in of itself:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

functionsym(args){

args=Array.prototype.slice.call(arguments);

// this determines the symmetric difference

varpseudo=args.reduce(function(a,b){

returnb.filter(function(i){

returna.indexOf(i)===-1;

}).concat(a.filter(function(i){

returnb.indexOf(i)===-1;

}));

});

}

return b.filter(function(i) { return a.indexOf(i) === -1; }) and vice versa is “equivalent” to the use of the for loops and indexOf() in the Diff Two Arrays. .concat() method joins the arguments, and .indexOf() checks for the symmetric difference of two sets A and B is the set of elements either in A or in B but not in both. In other words, it checks for the symmetric difference between each two arrays being concatenated.

So where does the pseudo variable come in? I stored the args.reduce() method in the pseudo variable so that it could be easily “re-used”, as it is in the function sym() return statement:

JavaScript

1

2

3

returnpseudo.filter(function(element,position){

returnpseudo.indexOf(element)===position;

});

This permits the filtering of the new argument array that is formed by the implementation of the args.reduce() method. This filtering makes sure that there are no duplicates lingering after the original .reduce() method has been executed. Otherwise, if I had only returned the pseudo variable, there would have been duplicate elements present in the new argument array.

It took a bit of research before I could find a good explanation for this method of duplicate removal. I was having a bit of difficulty articulating it. I finally came across an excellent example in a gem of a post entitled “Removing Duplicates in an Array Using Javascript”. Applying Mike Heavers’ explanation to this Symmetric Difference scenario, the new array created from .reduce() will run through the original args arrays. The element variable represents the value of the element in the new array which contains the duplicates, the position variable is its 0 index position in the array, and the pseudo.indexOf(element) value is the index of the first occurrence of that element in the original array. So then how do we get rid of those pesky duplicates?

When we loop through the elements in the original arguments arrays and push them into the new .reduce() array the second time we hit the element in question using pseudo.indexOf(element), our position value is 2 for example, and our .indexOf(element) is still 1 (from the original execution of .reduce() etc). It only finds the first instance of the array element because this is “re-used” code. The duplicate does not get pushed into the final array and only unique values are present.

Share this:

Like this:

Related

Maria Campbell

Founder, Inter-Global Media Network, Inc., where I focus on Front End Development. as well as a digital photographer, videographer, blogger, broadcaster, and graphic designer. Genesis Framework user. I got my start 8+ years ago teaching myself HTML/CSS and Wordpress Development because I couldn't find anyone who could create what I wanted. I have been working with open source software ever since. I am an open source software evangelist, and a "Women in Technology" evangelist. A creative professional myself, I find Web Development to be both practical and creative. I love to cook, travel, read mysteries, love everything French, the beach at sunrise, my three cats, and hope to revisit the city of San Francisco one day.

Reader Interactions

Comment Policy: Your words are your own, so be nice and helpful if you can. Please, only use your real name and limit the amount of links submitted in your comment. We accept clean XHTML in comments, but don't overdo it please. Comments are moderated, so spammy or malicious links will be removed.