在多个数组中查找唯一值

时间:2016-02-01 18:00:17

标签: javascript arrays for-loop

我正在尝试用这个目标来解决freeCodeCamp exercise

  

编写一个带有两个或更多数组的函数并返回一个新数组   按原始提供的数组的顺序排列唯一值。

     

换句话说,应包括所有数组中存在的所有值   按照原始顺序,但在最终数组中没有重复。

     

唯一的数字应按其原始顺序排序,但是   最终数组不应按数字顺序排序。

所以我所做的就是将所有参数连接成一个名为everything的数组。然后,我在数组中搜索重复项,然后搜索这些重复项的参数,并.splice()将它们输出。

到目前为止,一切都按预期工作,但最后一个参数的最后一个数字没有被删除,我无法弄清楚原因。

任何人都可以指出我做错了什么吗?请记住,我正在努力学习,所以显而易见的事情对我来说可能并不明显,需要指出。提前谢谢。

function unite(arr1, arr2, arr3) {
  var everything = [];
  //concat all arrays except the first one
  for(var x = 0; x < arguments.length; x++) {
    for(var y = 0; y < arguments[x].length; y++) {
      everything.push(arguments[x][y]);
    }
  }
  
  //function that returns duplicates
  function returnUnique(arr) {
    return arr.reduce(function(dupes, val, i) {
      if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
        dupes.push(val);
      }
      return dupes;
    }, []);
  }
  
  //return duplicates
  var dupes = returnUnique(everything);
  
  //remove duplicates from all arguments except the first one
  for(var n = 1; n < arguments.length; n++) {
    for(var m = 0; m < dupes.length; m++) {
      if(arguments[n].hasOwnProperty(dupes[m])) {
        arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
      }
    }
  }
  
  //return concatenation of the reduced arguments
  return arr1.concat(arr2).concat(arr3);
  
}  

//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);

2 个答案:

答案 0 :(得分:4)

看起来你过分复杂了一点;)

function unite() {
    return [].concat.apply([], arguments).filter(function(elem, index, self) {
        return self.indexOf(elem) === index;
    });
}


res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));

说明

我们将问题分为两个步骤:

  • 将参数组合成一个大数组
  • 从此大数组中删除非唯一元素

这部分处理第一步:

[].concat.apply([], arguments)

内置方法someArray.concat(array1, array2 etc)将给定数组附加到目标。例如,

[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]

如果我们的函数有固定的参数,我们可以直接调用concat

function unite(array1, array2, array3) {
    var combined = [].concat(array1, array2, array3);
    // or
    var combined = array1.concat(array2, array3);

但由于我们不知道我们将收到多少args,因此我们必须使用apply

 someFunction.apply(thisObject, [arg1, arg2, etc])

相同
 thisObject.someFunction(arg1, arg2, etc)

所以上面一行

 var combined = [].concat(array1, array2, array3);

可以写成

 var combined = concat.apply([], [array1, array2, array3]);

或只是

 var combined = concat.apply([], arguments);

其中arguments是一个特殊的类数组对象,包含所有函数参数(实际参数)。

实际上,最后两行不起作用,因为concat不是普通函数,它是Array个对象的方法,因此是Array.prototype结构的成员。我们必须告诉JS引擎在哪里找到concat。我们可以直接使用Array.prototype

 var combined = Array.prototype.concat.apply([], arguments);

或创建一个新的,不相关的数组对象并从那里拉concat

 var combined = [].concat.apply([], arguments);

这种原型方法效率稍高(因为我们没有创建虚拟对象),但也更冗长。

无论如何,第一步现在已经完成。为了消除重复,我们使用以下方法:

 combined.filter(function(elem, index) {
     return combined.indexOf(elem) === index;
 })

有关解释和替代方法,请参阅此post

最后,我们将临时变量(combined)和链“合并”和“重复数据删除”调用一起删除:

return [].concat.apply([], arguments).filter(function(elem, index, self) {
    return self.indexOf(elem) === index;
});

使用filter的第三个参数(“this array”),因为我们不再有变量了。

简单,不是吗? ;)如果您有疑问,请告诉我们。

最后,如果你有兴趣,可以参加一个小练习:

  

combinededupe写为单独的函数。创建一个函数compose,它接受​​两个函数ab并返回一个以相反顺序运行这些函数的新函数,这样compose(a,b)(argument)将与{b(a(argument))相同1}}。将unite的上述定义替换为unite = compose(combine, dedupe),并确保其完全相同。

答案 1 :(得分:0)

您也可以尝试以下方法:

var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]

var UniqueValues = []

for (var i = 0; i < Data.length; i++) {
 UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}

console.log(UniqueValues)

相关问题