Trying to solve symmetric difference using Javascript

后端 未结 16 1141
伪装坚强ぢ
伪装坚强ぢ 2020-11-29 10:12

I am trying to figure out a solution for symmetric difference using javascript that accomplishes the following objectives:

  • accepts an unspecified number of ar
相关标签:
16条回答
  • 2020-11-29 10:36

    Here's a version that uses the Set object to make for faster lookup. Here's the basic logic:

    1. It puts each array passed as an argument into a separate Set object (to faciliate fast lookup).
    2. Then, it iterates each passed in array and compares it to the other Set objects (the ones not made from the array being iterated).
    3. If the item is not found in any of the other Sets, then it is added to the result.

    So, it starts with the first array [1, 1, 2, 6]. Since 1 is not found in either of the other arrays, each of the first two 1 values are added to the result. Then 2 is found in the second set so it is not added to the result. Then 6 is not found in either of the other two sets so it is added to the result. The same process repeats for the second array [2, 3, 5] where 2 and 3 are found in other Sets, but 5 is not so 5 is added to the result. And, for the last array, only 4 is not found in the other Sets. So, the final result is [1,1,6,5,4].

    The Set objects are used for convenience and performance. One could use .indexOf() to look them up in each array or one could make your own Set-like lookup with a plain object if you didn't want to rely on the Set object. There's also a partial polyfill for the Set object that would work here in this answer.

    function symDiff() {
        var sets = [], result = [];
        // make copy of arguments into an array
        var args = Array.prototype.slice.call(arguments, 0);
        // put each array into a set for easy lookup
        args.forEach(function(arr) {
            sets.push(new Set(arr));
        });
        // now see which elements in each array are unique 
        // e.g. not contained in the other sets
        args.forEach(function(array, arrayIndex) {
            // iterate each item in the array
            array.forEach(function(item) {
                var found = false;
                // iterate each set (use a plain for loop so it's easier to break)
                for (var setIndex = 0; setIndex < sets.length; setIndex++) {
                    // skip the set from our own array
                    if (setIndex !== arrayIndex) {
                        if (sets[setIndex].has(item)) {
                            // if the set has this item
                            found = true;
                            break;
                        }
                    }
                }
                if (!found) {
                    result.push(item);
                }
            });
        });
        return result;
    }
    
    var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]);
    log(r);
    
    function log(x) {
        var d = document.createElement("div");
        d.textContent = JSON.stringify(x);
        document.body.appendChild(d);
    }

    One key part of this code is how it compares a given item to the Sets from the other arrays. It just iterates through the list of Set objects, but it skips the Set object that has the same index in the array as the array being iterated. That skips the Set made from this array so it's only looking for items that exist in other arrays. That allows it to retain duplicates that occur in only one array.


    Here's a version that uses the Set object if it's present, but inserts a teeny replacement if not (so this will work in more older browsers):

    function symDiff() {
        var sets = [], result = [], LocalSet;
        if (typeof Set === "function") {
            try {
                // test to see if constructor supports iterable arg
                var temp = new Set([1,2,3]);
                if (temp.size === 3) {
                    LocalSet = Set;
                }
            } catch(e) {}
        }
        if (!LocalSet) {
            // use teeny polyfill for Set
            LocalSet = function(arr) {
                this.has = function(item) {
                    return arr.indexOf(item) !== -1;
                }
            }
        }
        // make copy of arguments into an array
        var args = Array.prototype.slice.call(arguments, 0);
        // put each array into a set for easy lookup
        args.forEach(function(arr) {
            sets.push(new LocalSet(arr));
        });
        // now see which elements in each array are unique 
        // e.g. not contained in the other sets
        args.forEach(function(array, arrayIndex) {
            // iterate each item in the array
            array.forEach(function(item) {
                var found = false;
                // iterate each set (use a plain for loop so it's easier to break)
                for (var setIndex = 0; setIndex < sets.length; setIndex++) {
                    // skip the set from our own array
                    if (setIndex !== arrayIndex) {
                        if (sets[setIndex].has(item)) {
                            // if the set has this item
                            found = true;
                            break;
                        }
                    }
                }
                if (!found) {
                    result.push(item);
                }
            });
        });
        return result;
    }
    
    
    var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]);
    log(r);
    
    function log(x) {
        var d = document.createElement("div");
        d.textContent = JSON.stringify(x);
        document.body.appendChild(d);
    }

    0 讨论(0)
  • 2020-11-29 10:36

    This is the JS code using higher order functions

        function sym(args) {
          var output;
          output = [].slice.apply(arguments).reduce(function(previous, current) {
            current.filter(function(value, index, self) { //for unique
              return self.indexOf(value) === index;
            }).map(function(element) { //pushing array
              var loc = previous.indexOf(element);
              a = [loc !== -1 ? previous.splice(loc, 1) : previous.push(element)];
            });
            return previous;
          }, []);
          document.write(output);
          return output;
        }
    
        sym([1, 2, 3], [5, 2, 1, 4]);

    And it would return the output as: [3,5,4]

    0 讨论(0)
  • 2020-11-29 10:37
    function sym(arr1, arr2, ...rest) {
    
      //creating a array which has unique numbers from both the arrays
      const union = [...new Set([...arr1,...arr2])];
    
      // finding the Symmetric Difference between those two arrays
      const diff = union.filter((num)=> !(arr1.includes(num) && arr2.includes(num)))
    
      //if there are more than 2 arrays
      if(rest.length){
        // recurrsively call till rest become 0 
        // i.e.  diff of 1,2 will be the first parameter so every recurrsive call will reduce     //  the arrays till diff between all of them are calculated.
    
        return sym(diff, rest[0], ...rest.slice(1))
      }
      return diff
    }
    
    0 讨论(0)
  • 2020-11-29 10:38

    Alternative: Use the lookup inside a map instead of an array

    function sym(...vs){
        var has = {};
        //flatten values
        vs.reduce((a,b)=>a.concat(b)).
            //if element does not exist add it (value==1)
            //or mark it as multiply found value > 1
            forEach(value=>{has[value] = (has[value]||0)+1});
        return Object.keys(has).filter(x=>has[x]==1).map(x=>parseInt(x,10));
    }
    console.log(sym([1, 2, 3], [5, 2, 1, 4],[5,7], [5]));//[3,4,7])
    
    0 讨论(0)
  • 2020-11-29 10:41

    I came across this question in my research of the same coding challenge on FCC. I was able to solve it using for and while loops, but had some trouble solving using the recommended Array.reduce(). After learning a ton about .reduce and other array methods, I thought I'd share my solutions as well.

    This is the first way I solved it, without using .reduce.

    function sym() {
      var arrays = [].slice.call(arguments);
    
      function diff(arr1, arr2) {
        var arr = [];
    
        arr1.forEach(function(v) {
          if ( !~arr2.indexOf(v) && !~arr.indexOf(v) ) {
            arr.push( v );
          }
        });
    
        arr2.forEach(function(v) {
          if ( !~arr1.indexOf(v) && !~arr.indexOf(v) ) {
            arr.push( v );
          }
        });
        return arr;
      }
    
      var result = diff(arrays.shift(), arrays.shift());
    
      while (arrays.length > 0) {
        result = diff(result, arrays.shift());
      }
    
      return result;
    }
    

    After learning and trying various method combinations, I came up with this that I think is pretty succinct and readable.

    function sym() {
      var arrays = [].slice.call(arguments);
    
      function diff(arr1, arr2) {
        return arr1.filter(function (v) {
          return !~arr2.indexOf(v);
        });
      }
    
      return arrays.reduce(function (accArr, curArr) { 
        return [].concat( diff(accArr, curArr), diff(curArr, accArr) )
        .filter(function (v, i, self) { return self.indexOf(v) === i; });
      });
    
    }
    

    That last .filter line I thought was pretty cool to dedup an array. I found it here, but modified it to use the 3rd callback parameter instead of the named array due to the method chaining.

    This challenge was a lot of fun!

    0 讨论(0)
  • 2020-11-29 10:41

    Another simple, yet readable solution:

     
    /*
    This filters arr1 and arr2 from elements which are in both arrays
    and returns concatenated results from filtering.
    */
    function symDiffArray(arr1, arr2) {
      return arr1.filter(elem => !arr2.includes(elem))
                 .concat(arr2.filter(elem => !arr1.includes(elem)));
    }
    
    /*
    Add and use this if you want to filter more than two arrays at a time.
    */
    function symDiffArrays(...arrays) {
      return arrays.reduce(symDiffArray, []);
    }
    
    console.log(symDiffArray([1, 3], ['Saluton', 3])); // [1, 'Saluton']
    console.log(symDiffArrays([1, 3], [2, 3], [2, 8, 5])); // [1, 8, 5]

    Used functions: Array.prototype.filter() | Array.prototype.reduce() | Array.prototype.includes()

    0 讨论(0)
提交回复
热议问题