尝试使用 Javascript 解决对称差异

Trying to solve symmetric difference using Javascript

我正在尝试找出对称的解决方案 使用 javascript 完成以下操作的区别 目标:

因此,例如, 如果输入是 ([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]), 解决方案是 [1, 1, 6, 5, 4].

我正在尝试解决这个在线挑战 编码社区。挑战的确切说明 州,

Create a function that takes two or more arrays and returns an array of the symmetric difference of the provided arrays.

The mathematical term symmetric difference refers to the elements in two sets that are in either the first or second set, but not in both.

虽然我下面的解决方案找到了数字 每个数组都是唯一的,它消除了所有出现的数字 不止一次并且不保持数字的顺序。

我的问题与 上的问题非常接近。然而,解决方案 不保留数字的原始顺序,也不保留单个数组中出现的唯一数字的重复项。

function sym(args){
    var arr = [];
    var result = [];
    var units;
    var index = {};
    for(var i in arguments){
        units = arguments[i];

    for(var j = 0; j < units.length; j++){
         arr.push(units[j]);
        }
    }

    arr.forEach(function(a){
        if(!index[a]){
            index[a] = 0;
        }
            index[a]++;

    });

       for(var l in index){
           if(index[l] === 1){
               result.push(+l);
           }
       }

    return result;
}
symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]); // => Desired answer: [1, 1, 6. 5. 4]

这是一个使用 Set 对象来加快查找速度的版本。这是基本逻辑:

  1. 它将作为参数传递的每个数组放入一个单独的 Set 对象(以促进快速查找)。
  2. 然后,它迭代传入的每个数组并将其与其他 Set 对象(不是由正在迭代的数组生成的对象)进行比较。
  3. 如果在任何其他集合中都找不到该项目,则会将其添加到结果中。

因此,它从第一个数组 [1, 1, 2, 6] 开始。由于 1 未在任何其他数组中找到,因此前两个 1 值中的每一个都被添加到结果中。然后在第二组中找到 2,因此不会将其添加到结果中。然后 6 在其他两个集合中都没有找到,所以它被添加到结果中。对第二个数组 [2, 3, 5] 重复相同的过程,其中 23 在其他集合中找到,但 5 不是,因此将 5 添加到结果中.并且,对于最后一个数组,只有 4 在其他集合中没有找到。所以,最后的结果是[1,1,6,5,4].

Set 对象用于方便和性能。可以使用 .indexOf() 在每个数组中查找它们,或者如果您不想依赖 Set 对象,可以使用普通对象进行自己的类似 Set 的查找。 this answer.

中还有一个 Set 对象的部分 polyfill

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);
}

此代码的一个关键部分是如何将给定项目与其他数组中的集合进行比较。它只是遍历 Set 对象的列表,但它会跳过数组中与被迭代数组具有相同索引的 Set 对象。这会跳过由此数组创建的 Set,因此它只查找存在于其他数组中的项。这允许它保留仅出现在一个数组中的重复项。


这是一个使用 Set 对象(如果存在)的版本,但如果不存在则插入一个微小的替换(因此这将适用于更多旧浏览器):

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);
}

与所有问题一样,最好从编写算法开始:

Concatenate versions of the arrays, where each array is filtered to contain those elements which no array other than the current one contains

然后用 JS 写下来:

function sym() {
  var arrays = [].slice.apply(arguments);

  return [].concat.apply([],               // concatenate
    arrays.map(                            // versions of the arrays
      function(array, i) {                 // where each array
        return array.filter(               // is filtered to contain
          function(elt) {                  // those elements which
            return !arrays.some(           // no array
              function(a, j) {             // 
                return i !== j             // other than the current one
                  && a.indexOf(elt) >= 0   // contains
                ;
              }
            );
          }
        );
      }
    )
  );
}

非注释版本,使用 ES6 写得更简洁:

function sym(...arrays) {
  return [].concat(arrays . 
    map((array, i) => array . 
      filter(elt => !arrays . 
        some((a, j) => i !== j && a.indexOf(elt) >= 0))));
}

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]);

它会 return 输出为:[3,5,4]

只需使用 _.xor 或复制 lodash 代码。

纯javascript溶液。

function diff(arr1, arr2) {
var arr3= [];
  for(var i = 0; i < arr1.length; i++ ){
    var unique = true;
     for(var j=0; j < arr2.length; j++){
          if(arr1[i] == arr2[j]){
               unique = false;
               break;
          }
     }
  if(unique){
    arr3.push(arr1[i]);}
  }
 return arr3;
}

function symDiff(arr1, arr2){
  return diff(arr1,arr2).concat(diff(arr2,arr1));
}

symDiff([1, "calf", 3, "piglet"], [7, "filly"])
//[1, "calf", 3, "piglet", 7, "filly"]

我的简短解决方案。最后,我通过 filter() 删除了重复项。

function sym() {
  var args = Array.prototype.slice.call(arguments);
  var almost = args.reduce(function(a,b){
    return b.filter(function(i) {return a.indexOf(i) < 0;})
    .concat(a.filter(function(i){return b.indexOf(i)<0;}));
  });
  return almost.filter(function(el, pos){return almost.indexOf(el) == pos;});
}

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

//Result: [4,5,1]

我在研究 FCC 上的相同编码挑战时遇到了这个问题。我能够使用 forwhile 循环解决它,但在使用推荐的 Array.reduce() 解决问题时遇到了一些问题。在学习了大量 .reduce 和其他数组方法之后,我想我也应该分享我的解决方案。

这是我解决的第一种方法,没有使用.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;
}

在学习和尝试各种方法组合之后,我想出了这个我认为非常简洁和可读的方法。

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; });
  });

}

最后 .filter 行我认为对数组进行重复数据删除非常酷。我找到了它 here,但由于方法链接,将其修改为使用第三个回调参数而不是命名数组。

这个挑战很有趣!

这对我有用:

function sym() {
  var args = [].slice.call(arguments);
  
  var getSym = function(arr1, arr2) {
    return arr1.filter(function(each, idx) {
      return arr2.indexOf(each) === -1 && arr1.indexOf(each, idx + 1) === -1;
    }).concat(arr2.filter(function(each, idx) {
      return arr1.indexOf(each) === -1 && arr2.indexOf(each, idx + 1) === -1;
    }));
  };
  
  var result = getSym(args[0], args[1]);
  var len = args.length - 1, i = 2;
  while (--len) {
    result = [].concat(getSym(result, args[i]));
    i++;
  }
  
  return result;
}

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

备选方案:在地图而不是数组中使用查找

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])

function sym(args) {
  var initialArray = Array.prototype.slice.call(arguments);
  var combinedTotalArray = initialArray.reduce(symDiff);

  
  // Iterate each element in array,  find values not present in other array and push values in combinedDualArray if value is not there already
  // Repeat for the other array (change roles)
  function symDiff(arrayOne, arrayTwo){
    var combinedDualArray = [];
    arrayOne.forEach(function(el, i){
      if(!arrayTwo.includes(el) && !combinedDualArray.includes(el)){
        combinedDualArray.push(el);
      }
    });
      
    arrayTwo.forEach(function(el, i){
      if(!arrayOne.includes(el) && !combinedDualArray.includes(el)){
        combinedDualArray.push(el);
      }
    });
    combinedDualArray.sort();
    return combinedDualArray;
  }
  
  return combinedTotalArray;
}

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

创建一个包含所有唯一值(跨数组)计数的映射。然后连接所有数组,并使用 Map 过滤非唯一值。

const symsym = (...args) => {
  // create a Map from the unique value of each array
  const m = args.reduce((r, a) => {
    // get unique values of array, and add to Map
    new Set(a).forEach((n) => r.set(n, (r.get(n) || 0) + 1));
    
    return r;
  }, new Map());
  
  // combine all arrays
  return [].concat(...args)
    // remove all items that appear more than once in the map
    .filter((n) => m.get(n) === 1); 
};

console.log(symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4])); // => Desired answer: [1, 1, 6, 5, 4]

另一个简单但可读的解决方案:

 
/*
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]

使用的函数:Array.prototype.filter() | Array.prototype.reduce() | Array.prototype.includes()

嘿,如果有人感兴趣,这是我的解决方案:

function sym (...args) {
  let fileteredArgs = [];
  let symDiff = [];
  args.map(arrayEl =>
    fileteredArgs.push(arrayEl.filter((el, key) =>
      arrayEl.indexOf(el) === key
      )
    )
  );

  fileteredArgs.map(elArr => {
    elArr.map(el => {
      let index = symDiff.indexOf(el);
      if (index === -1) {
        symDiff.push(el);
      } else {
        symDiff.splice(index, 1);
      }
    });
  });

  return (symDiff);
}

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

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
}

// Set difference, a.k.a. relative compliment
const diff = (a, b) => a.filter(v => !b.includes(v))

const symDiff = (first, ...rest) => 
  rest.reduce(
    (acc, x) => [
      ...diff(acc, x), 
      ...diff(x, acc),
    ], 
    first,
  )    

/* - - - */
console.log(symDiff([1, 3], ['Saluton', 3]))    // [1, 'Saluton']
console.log(symDiff([1, 3], [2, 3], [2, 8, 5])) // [1, 8, 5]

此函数删除重复项,因为对称差异的原始概念对集合进行操作。在此示例中,函数以这种方式对集合进行操作:((A △ B) △ C) △ D ...

function sym(...args) {
    return args.reduce((old, cur) => {
        let oldSet = [...new Set(old)]
        let curSet = [...new Set(cur)]
        return [
            ...oldSet.filter(i => !curSet.includes(i)),
            ...curSet.filter(i => !oldSet.includes(i))
        ]
    })
}

// Running> sym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4])
console.log(sym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]))
// Return>  [1, 6, 5, 2, 4]

这是解决方案

let a=[1, 1, 2, 6]
let b=[2, 3, 5];
let c= [2, 3, 4]

let result=[...a,...b].filter(item=>!(a.includes(item) && b.includes(item) ))
result=[...result,...c].filter(item=>!(b.includes(item) && c.includes(item) ))

console.log(result)  //[1, 1, 6, 5, 4]

使用

的简洁解决方案
const symPair = (a, b) => 
  [...a.filter(item => !b.includes(item)),
  ...b.filter(item => !a.includes(item))]

const sym = (...args) => [...new Set(args.reduce(symPair))]

函数symPair适用于两个输入数组,函数sym适用于两个或更多数组,使用symPair作为reducer。

const symPair = (a, b) => 
  [...a.filter(item => !b.includes(item)),
  ...b.filter(item => !a.includes(item))]

const sym = (...args) => [...new Set(args.reduce(symPair))]

console.log(sym([1, 2, 3], [2, 3, 4], [6]))

const removeDuplicates = (data) => Array.from(new Set(data));
const getSymmetric = (data) => (val) => data.indexOf(val) === data.lastIndexOf(val)

function sym(...args) {
  let joined = [];
  args.forEach((arr) => {
    joined = joined.concat(removeDuplicates(arr));
    joined = joined.filter(getSymmetric(joined))
  });
 return joined;
}


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

以下代码在所有情况下都运行良好。试试下面的代码

function sym() {
  var result = [];
  for (var i = 0; i < arguments.length; i++) {
    if (i == 0) {
      var setA = arguments[i].filter((val) => !arguments[i + 1].includes(val));
      var setB = arguments[i + 1].filter((val) => !arguments[i].includes(val));
      result = [...setA, ...setB];
      i = i + 1;
    } else {
      var setA = arguments[i].filter((val) => !result.includes(val));
      var setB = result.filter((val) => !arguments[i].includes(val));
      result = [...setA, ...setB];
    }
  }
  return result.filter((c, index) => {
    return result.indexOf(c) === index;
  }).sort();
}