freeCodeCamp Challenge Guide: Symmetric difference

Here is my solution (it wasn’t easy):

function sym(args) {
  // Convert object to an array
  args = Array.prototype.slice.call(arguments);
  // Delete duplicate numbers in subArrays
  for (var i = 0; i < args.length; i++) {
    for (var j = 0; j < args[i].length; j++) {
      if (args[i][j] === args[i][j+1]) {
        delete args[i][j];
      }
    }
  }
  // then concat subArrays successively with reduce, filtering duplicates after each iteration
  return args.reduce(function(a, b) {
    return a.concat(b).sort().filter(function(value, index, arr) {
      if (arr[index] != arr[index+1] && arr[index] != arr[index-1]) {
        return value;
      }
    });
  });
}

console.log(sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]));

Another messed up looking one-liner :stuck_out_tongue:

function sym(args) {
  return [...new Set(Array.from(arguments).reduce((A, B) => A.filter(el => B.indexOf(el) < 0).concat(B.filter(el => A.indexOf(el) < 0))))];
}

sym([1, 2, 3], [5, 2, 1, 4]); // [3, 5, 4]
sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]); // [7, 4, 6, 2, 5, 9, 8, 1]

Hi, I am not getting one thing in the Problem Explanation. As explained resultant set of array should contain only unique value and members of either set but not both. But In the example of the problem explanation 5 is the value in all the array and also in resultant array. Since 5 is present in all the array and it should not be a part of the resultant array. And if this must be the resultant array then why not 2 and 3 be in the resultant array? Kindly help me with this
Thanks

//using solution for differnce of arrays
function diffArray(arr1, arr2) {
// Same, same; but different.
return arr1.filter( function( el ) {
return !arr2.includes( el );
}).concat(arr2.filter( function( el ) {
return !arr1.includes( el );
}));
}

//itirates over the whole array and applys diff array and store it in the last argument array
function sym(args) {
for(var i=0;i<arguments.length-1;i++){
arguments[i+1]=diffArray(arguments[i],arguments[i+1]);
}
//removes duplicates in the array
return arguments[arguments.length-1].filter(function(item, pos) {
return arguments[arguments.length-1].indexOf(item) == pos;
});
}

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

// A ∆ B = [A ∖ B] ∪ [B ∖ A]

function sym(...args) {
  const difference = (arr1, arr2) =>  [
      ...arr1.filter(n => !arr2.includes(n)),
      ...arr2.filter(n => !arr1.includes(n))
  ];
  return [...new Set(
    args.reduce((current, union) =>
      difference(current, union))
  )];
}
2 Likes
function sym(...arg) {
  
  let result = [];
  
  result = arg
             .reduce((prv, cur) => findDifference(prv, cur))
  
  return result;
}

function findDifference(last, next) {
  
  let result = [];
    
  last
    .map(item => {
      if(next.indexOf(item) === -1 && result.indexOf(item) === -1) {
        result.push(item)
      }
    });

  next
    .map(item => {
      if(last.indexOf(item) === -1 && result.indexOf(item) === -1) {
        result.push(item)
      }
    });

  return result
}

sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3])

This was very hard:grinning:

function sym(args) {

    var ar = Array.from(arguments);
     function myInit(a, b) {
        var initArr = [];
        for (var i = 0; i < a.length; i++) {
            if (b.indexOf(a[i]) === -1) {
                if(initArr.indexOf(a[i]) === -1){
                initArr.push(a[i]);
                }
            }
        }
        for (var i = 0; i < b.length; i++) {
            if (a.indexOf(b[i]) === -1) {
                if(initArr.indexOf(b[i]) === -1){
                initArr.push(b[i]);
                }
            }
        }
        return initArr;
    }
    var j = ar.reduce(function (acc, cur) {
        return myInit(acc, cur);
    });

     return j;
}

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

Well I just came with this… it’s not too long… and looks pretty understandable, I guess… :upside_down_face:

function sym(args) {
  const argsArr = [...arguments];
  
  function symDiff (a, b) {
    const tempArgsArr = [a, b];
    const tempArr = [];
    
    for (let i = 0; i < tempArgsArr.length; i++) {
      for (const el of tempArgsArr[i]) {
        if (tempArgsArr[i+1]) {
          if (!tempArgsArr[i+1].includes(el)) {
            tempArr.push(el);
          }
        } else if (tempArgsArr[i-1]) {
          if (!tempArgsArr[i-1].includes(el)) {
            tempArr.push(el);
          } 
        }
      }  
    }
    return tempArr;
  }
  
  return [...new Set(argsArr.reduce(symDiff))];
}

Why in the advanced solution const have been used instead of var?

Hi Lucas, const is ES6 syntax… latest javascript especifications.

Try using const or let instead of var.

You must read more and try to find some nice ES6 tutorial to get used with it…there are many developers that are adpting this informal rule:

const // use as default
let // use only if rebinding is needed
var // shouldn’t be used in ES6

If you want to get more information of const check const MDN docs.

If you can invest a few bucks to get used to ES6 quick , I’d recommend this course ES6 for Everyone.

1 Like

I thought my solution was pretty clever until I scrolled down this page and saw all the solutions using various combinations of map, filter, and the arrow function. Nice work everyone! I know what I’ll be studying this week.

UPDATE: According to testing on jsPerf, this solution is faster than the FCC provided solutions and much faster than any solutions making use of the filter function. Here’s a blog post I found about the overhead involved in using filter. This also applies to other native JavaScript functions. If you don’t need all the error-checking those functions provide, a naive implementation will often be more efficient.

Here’s an explanation of my solution:

  1. Convert the arguments to an array (of arrays) and assign to symDiff.

  2. The accumulator acc starts as an empty array; arr starts with the first array in arguments.

  3. For each value in arr:

    • If this is the first instance of this value in arr, proceed. Otherwise, try the next value in arr.
    • If this value is already in acc, remove it. Otherwise, add it.
  4. return acc will trigger reduce() to repeat Step 3 with the updated acc array and arr set to the next array in arguments, until there are no more arguments to compare.

  5. Return symDiff.

function sym(args) {
  var symDiff = Array.prototype.slice.call(arguments).reduce(
    function(acc, arr) {
    for (var i = 0; i < arr.length; i++) {
      if (arr.indexOf(arr[i]) === i) {
        if (~acc.indexOf(arr[i])) {
          acc.splice(acc.indexOf(arr[i]), 1);
        } else {
          acc.push(arr[i]);
        }  
      }
    }
    return acc;
    }, []
  );
  return symDiff;
}
1 Like

Liked this one. Here’s my answer, a little more convoluted than it needs to be but I think a decent solution.
I convert arguments object to array and remove any duplicates from each of the array arguments. Then reduce out the answer by concatenating each array with the array it is being compared to, loop through to create an array of all elements which are duplicates, then filter against that array to remove all of those instances and return the answer for each comparison.

function sym(args) {
  args = Array.prototype.slice.call(arguments).map(function(arg) {
    return arg.filter(function(num, pos) {
      return arg.indexOf(num) === pos;
    });
  });
  return args.reduce(function(a, b) {
    a = a.concat(b).sort();
    console.log(a);
    var dupes = [];
    for (x = 0; x < a.length; x++) {
      if (a[x] === a[x+1]) {
        dupes.push(a[x]);
      }
    }
    return a.filter(function(y) {
      if (!dupes.includes(y)) {
        return y;
      }
    });
  });
}

Not very elegant solution. Not in a JavaScript style. I’m a beginner and should study a lot. But it is working. I divided task into three sub-tasks. First, remove duplicates array, then find difference between two arrays, finally apply this difference for every next array.

function sym(args) {
  var result = [];
  // Using 'arguments' object:
  // I can not path arguments object to filter function directly.
  // Because filter function have its own argument object.
  var arglist = [];
  for ( var i = 0; i<arguments.length; i++) {
    // console.log(arguments[i]);
    arglist.push(arguments[i]);
  }
  result = arglist[0];
  for (i=1; i<arglist.length; i++) {
    result = array_diff(result, arglist[i]);
  } 
  return result;
}

// Function to remove duplicates from array:
function remove_dups(cur_arr) {
  var result = [];
  // console.log(cur_arr);
  for (i=0; i<cur_arr.length; i++) {
    if (result.includes(cur_arr[i]) == false) {
      result.push(cur_arr[i]);
    }
  }
  // Sorting array:
  result.sort();
  return result;
}

// Function to get diff from two array:
function array_diff(arr1, arr2) {
  var result = [];
  var is_found = false;
  
  // We should remove duplicates first:
  arr1 = remove_dups(arr1);
  arr2 = remove_dups(arr2);
  
  for (i=0; i<arr1.length; i++) {
    arr1_item = arr1[i];
    for (j=0; j<arr2.length; j++) {
      arr2_item = arr2[j];
      if (arr1_item == arr2_item) {
        is_found = true;
        break;
      }
    }
    if (is_found == false) {
      result.push(arr1_item);
    } else {
      is_found = false;
    }
  }
  
  for (i=0; i<arr2.length; i++) {
    arr2_item = arr2[i];
    for (j=0; j<arr1.length; j++) {
      arr1_item = arr1[j];
      if (arr1_item == arr2_item) {
        is_found = true;
        break;
      }
    }
    if (is_found == false) {
      result.push(arr2_item);
    } else {
      is_found = false;
    }
  }
  return result;
}

console.log(sym([1, 2, 5], [2, 3, 5], [3, 4, 5]));  // Should return [1, 4, 5]

I came up with a solution I think that counts how many times number exists in all arrays (without counting duplicates in single array). Then its just adding all the numbers that happend uneven number of times.

function sym(args) {
  var result = [];
  var obj = {    // Object created to count how many times numbers are in array
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
    6: 0,
    7: 0,
    8: 0,
    9: 0
  };
  
  var arg = Array.prototype.slice.call(arguments);

  
  for (var i=0;i<arg.length;i++){
    for (var j=1;j<10;j++) {
      if (arg[i].indexOf(j) !== -1) {  //function to check each set if it contains number from 1 to 9 - if yes, 
         obj[j] += 1;                             //  it adds +1 to corresponding number in object
        
      }
    }
  }
   
  var objSliced = Array.prototype.slice.call(Object.values(obj)); 
  
  
  for (var y=0;y<objSliced.length;y++){ //pushes to "result" every number that occurs 1,3,5 etc times.
    if (objSliced[y] === 1||objSliced[y] === 3) {    
     result.push(y+1);
    }}
 
 
return result;

}
sym([3, 3, 3, 2, 5], [2, 1, 5, 7], [3, 4, 6, 6], [1, 2, 3], [5, 3, 9, 8], [1]);

I have to say that this challenge was a real struggle to me and I’m ashamed to say that I couldn’t figure it out on my own. I knew that I had to take the arguments and turn them into an array, so that was a solid first step for my psyche. After that, I attempted to create a function that would compare two arrays to find the symmetrical difference between them. I totally forgot about using the .forEach() method, but I knew I was going to have to compare the first array objects to all of the second array objects then again for second array objects to the first one.

My question is how would I know to have created this function INSIDE the sym() function? Is it because we have an unknown amount of arguments passed into the sym() function that makes us need to create this new function inside of it?

I’m also still a bit iffy on the Array.reduce() method and how running args.reduce(symDiff) is going to run the created function enough times to iterate through all of the arguments passed into the sym() function. I understand that .reduce() uses the first two objects in the array and since symDiff uses two inputs as well, is that why it works how we need it to?

Since I didn’t complete this on my own, is there somewhere I can go to learn more about arrays and get some more practice in rather than just studying this challenge’s answer and hoping to remember to use it in the future? Thanks in advance!

Wow, this challenge was very hard… I’m very happy that I managed to solve it, even if it’s a “more basic than the basic solution” solution :slight_smile:

function sym(args) {
  var newArgs = Array.prototype.slice.call(arguments);
  var result = [];
  result[0] = newArgs[0]; // the first step's result is the first argument
  
  for (var emptyArray = 1; emptyArray < newArgs.length; emptyArray++) {
    result[emptyArray] = []; //for some reason I had to create empty arrays, otherwise it didn' work
  }
  
  for (var i = 1; i < newArgs.length; i++) {
    for (var j = 0; j < result[i-1].length; j++) { // let's check if the i-th argument contains any element of the previous step's result
      if (newArgs[i].indexOf(result[i-1][j]) === -1 ) {
        result[i].push(result[i-1][j]); // if yes, push the element to the i-th result
      }
    }
    for (var k = 0; k < newArgs[i].length; k++) { // let's check if the previous step's result contains any element of the i-th argument
      if (result[i-1].indexOf(newArgs[i][k]) === -1) {
        result[i].push(newArgs[i][k]); // if yes, push the element to the i-th result
      }
    }
  }
  
  return result[newArgs.length - 1].filter(function(item, pos) {
    return result[newArgs.length - 1].indexOf(item) == pos; //remove the duplicate values from the last result
  });
}