尝试使用Javascript解决对称差异

时间:2015-06-14 21:54:10

标签: javascript arrays symmetric-difference

我试图找出对称的解决方案 差异使用javascript完成以下 目标:

  • 接受未指定数量的数组作为参数
  • 保留数组中数字的原始顺序
  • 不会删除单个数组中的重复数字
  • 删除跨数组发生的重复

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

我正试图解决这个问题,因为网上提出了挑战 编码社区。挑战的确切说明 状态,

  

创建一个带有两个或更多数组并返回数组的函数   提供的数组的对称差异。

     

数学术语对称差异是指中的元素   两组在第一组或第二组中,但不在两者中。

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

我的问题与finding symmetric difference/unique elements in multiple arrays in javascript提出的问题非常接近。但是,解决方案 不保留数字的原始顺序,也不保留单个数组中出现的唯一数字的重复。

virtual

17 个答案:

答案 0 :(得分:18)

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

  

数组的连接版本,其中每个数组都被过滤以包含那些除当前数组之外没有数组的元素

然后在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))));
}

答案 1 :(得分:10)

这是一个使用Set对象进行更快查找的版本。这是基本逻辑:

  1. 它将每个数组作为参数传递给一个单独的Set对象(以便于快速查找)。
  2. 然后,它迭代每个传入的数组并将其与其他Set对象(不是从正在迭代的数组中生成的对象)进行比较。
  3. 如果在任何其他集中找不到该项,则会将其添加到结果中。
  4. 因此,它从第一个数组[1, 1, 2, 6]开始。由于在其他任何一个数组中都找不到1,因此前两个1值中的每一个都会添加到结果中。然后在第二组中找到2,因此不会将其添加到结果中。然后在其他两个集合中找不到6,因此将其添加到结果中。对于第二个数组[2, 3, 5]重复相同的过程,其中23在其他集合中找到,但5不是5被添加到结果中。并且,对于最后一个数组,在其他集合中找不到4。因此,最终结果为[1,1,6,5,4]

    Set对象用于方便和性能。可以使用.indexOf()在每个数组中查找它们,或者如果您不想依赖Set对象,则可以使用普通对象进行自己的类似Set的查找。还有一个Set对象的部分polyfill可以在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);
    }

    此代码的一个关键部分是它如何将给定项与其他数组中的集进行比较。它只是遍历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);
    }

答案 2 :(得分:7)

我在对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,但由于方法链接而将其修改为使用第3个回调参数而不是命名数组。

这次挑战非常有趣!

答案 3 :(得分:2)

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

答案 4 :(得分:1)

  

这是使用高阶函数的JS代码

&#13;
&#13;
    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]);
&#13;
&#13;
&#13;

它会将输出返回为:[3,5,4]

答案 5 :(得分:1)

纯粹的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"]

答案 6 :(得分:1)

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

@echo off
for /f %%i in ('wmic logicaldisk where "drivetype=2" get caption^|find ":"') do (
  pushd %%i
    dir /s /a-d "setup.txt" /s "setup2.txt" >nul && (
      dir /b /s /a-d setup.txt /s setup2.txt >>"\\servidor\pastadelogs\FOUND_%computername%.txt"
    ) || (
      echo "[%%i] Este PC não possui os arquivos" >>"\\servidor\pastadelogs\NOTFOUND_%computername%.txt"
    )
  popd
)

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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
}

答案 9 :(得分:1)

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

 
/*
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()

答案 10 :(得分:0)

这是解决方案

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]

答案 11 :(得分:0)

此功能可删除重复项,因为对称差的原始概念适用于集合。在此示例中,函数以以下方式在集合上操作:((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]

答案 12 :(得分:0)

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

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

答案 13 :(得分:0)

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

&#13;
&#13;
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]
&#13;
&#13;
&#13;

答案 14 :(得分:0)

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

答案 15 :(得分:0)

替代方法:在地图内使用查找而不是数组

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

答案 16 :(得分:0)

这对我有用:

&#13;
&#13;
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]));
&#13;
&#13;
&#13;