如何在JavaScript中创建一个唯一项列表?

35

我的CouchDB reduce函数需要将一个项目列表缩减为唯一的项目。

注意:在这种情况下,具有列表是可以接受的,它将是少量字符串类型的项。

我目前的方法是设置一个对象的键,然后返回该对象的键,因为代码无法使用像_.uniq这样的工具。

我想找到一种比这更优雅的方式来表达它。

function(keys, values, rereduce) {
  // values is a Array of Arrays
  values = Array.concat.apply(null, values);
  var uniq = {};
  values.forEach(function(item) { uniq[item] = true; });
  return Object.keys(uniq);
}

根据您对“优雅”的定义,您可以在GitHub上查找下划线的unique源代码。 - Geert-Jan
下划线对于仅限字符串的情况来说更昂贵,而且不够优雅,因为需要在一般情况下工作。 - user78110
你需要一个reduce函数吗?如果你只需要唯一的值,可以在请求视图时使用group=true选项。有关更多信息,请参阅CouchDB Wiki - dignifiedquire
1
可能是[获取数组中所有唯一值(删除重复项)https://dev59.com/PXI-5IYBdhLWcg3wO1rl]的重复问题。 - Gernot
9个回答

65

使用ES6和Set似乎是最好的方法。根据fiddle测试,这种方法比上面的方法更快且只需一行代码。

    
const myList = [1,4,5,1,2,4,5,6,7];
const unique = [...new Set(myList)];
    
console.log(unique);

*在safari中测试过


3
你也可以只使用 new Set([1,2,4,6]),我认为这样写更简洁易懂。 - Alexander Mills
1
new Set(list)只会创建一个Set。 - Joakim Poromaa Helger
1
是的,你说得对,我是指 Array.from(new Set([1,2,4,6])),请看我的回答。 - Alexander Mills

36

2021答案:

const unique = (arr) => [...new Set(arr)];
unique([1, 2, 2, 3, 4, 4, 5, 1]); // [1, 2, 3, 4, 5]

在这里,您只需从给定的数组创建一个集合,然后将其转换回数组。我已经测试过它的性能,比我以前发布的旧答案提出的方法快了近两倍。而且它只是一个一行的代码。

更新的fiddle

旧答案仅供参考:

通常,您使用的方法是一个好主意。但我可以提出一个解决方案,使算法更快。

function unique(arr) {
    var u = {}, a = [];
    for(var i = 0, l = arr.length; i < l; ++i){
        if(!u.hasOwnProperty(arr[i])) {
            a.push(arr[i]);
            u[arr[i]] = 1;
        }
    }
    return a;
}

可以看到这里只有一个循环。

我制作了一个示例,测试了你和我的解决方案。试着去尝试一下它。


1
当处理集合和循环时,算法的速度总是非常重要的。 - Eugene Naydenov
@MarkusMeskanen 这要取决于特定的JavaScript引擎实现。另外,不要忘记属性查找时间。 - Eugene Naydenov
不错的发现。虽然更多地是语言本身的限制。 - Eugene Naydenov
1
今天通过Chrome打开测试链接,测试速度几乎没有任何差异。这个答案是否仍然有效,或者浏览器已经优化了性能差异?我已经更新了一个jsfiddle,同时提出了我的indexOf建议。 - Félix Adriyel Gagnon-Grenier
1
奇怪。我在Linux Chrome中得到了大约0.7-0.8的比率。但有时它会大于1。所以看起来ES引擎现在的工作方式不同了。 - Eugene Naydenov
显示剩余3条评论

13

适合小列表的另一种选择是模仿Unix命令行的方法,使用sort | uniq

    function unique(a) {
        return a.sort().filter(function(value, index, array) {
            return (index === 0) || (value !== array[index-1]);
        });
    }

这个函数会对参数进行排序,然后筛选结果以省略任何与其前一个项目相等的项目。

基于键的方法很好,在大量项目(将n个项目插入hashtable的O(n)比对数组进行O(n log n)排序)方面具有更好的性能特征。但是,在小列表上这不太可能引起注意。此外,使用此版本,如果需要,您可以修改它以使用不同的排序或相等函数;而使用哈希键,则必须满足JavaScript的键相等概念。


很好,我能够在Ember.js中使用它来使用Arrayproxy的“filter”函数过滤recordarray。 - Epirocks

8

这应该适用于任何东西,不仅仅是字符串:

export const getUniqueList =  (a: Array<any>) : Array<any> => {

  const set = new Set<any>();

  for(let v of a){
      set.add(v);
  }

  return Array.from(set);

};

上述内容可以简化为:
export const getUniqueValues = (a: Array<any>) => {
   return Array.from(new Set(a));
};

:)


2
为了获取唯一的对象,您可以使用 JSON.stringifyJSON.parse:

const arr = [{test: "a"}, {test: "a"}];
const unique = Array.from(new Set(arr.map(JSON.stringify))).map(JSON.parse);
console.log(unique);


这是唯一一个能够处理列表嵌套的并找到独特列表的方法。 - Sooth

0

这是一个老问题,我知道。然而,它在一些谷歌搜索结果的顶部,所以我想补充一下,您可以使用以下方法结合@RobHague和@EugeneNaydenov的答案:

function unique(arr) {
  const u = {};
  return arr.filter((v) => {
    return u[v] = !u.hasOwnProperty(v);
  });
};

您也可以通过添加以下内容来忽略 未定义 的值(通常很方便):

function unique(arr) {
  const u = {};
  return arr.filter((v) => {
    return u[v] = (v !== undefined && !u.hasOwnProperty(v));
  });
};

您可以在此处尝试这个解决方案:https://jsfiddle.net/s8d14v5n/


也许可以使用 new Set 来增加乐趣。 - user7898461

0
我发现其他答案过于复杂,而且没有任何可见的收益。
我们可以使用数组的 indexOf方法在推送之前验证其中是否存在项:

const duplicated_values = ['one', 'one', 'one', 'one', 'two', 'three', 'three', 'four'];
const unique_list = [];

duplicated_values.forEach(value => {
  if (unique_list.indexOf(value) === -1) {
    unique_list.push(value);
  }
});

console.log(unique_list);

这也适用于任何类型的变量,甚至包括对象(只要标识符实际上引用相同的实体,等效的对象不被视为相同)。


0

使用 Object.keys 将会返回字符串,如果你给定整数参数 (uniq([1,2,3]) => ['1','2','3']。这里有一个使用 Array.reduce 的例子:

function uniq(list) {
    return list.reduce((acc, d) => acc.includes(d) ? acc : acc.concat(d), []);
}

-1

怎么样?

    function unique(list) {
      for (i = 0; i<list.length; i++) {
        for (j=i+1; j<list.length; j++) {
          if (list[i] == list[j]) {
            list.splice(j, 1);
          }
        }
      }
    }

1
在运行 list.splice() 之后,您需要递减 j。这种 O(N^2) 的解决方案适用于小数组,但一旦数组变得更大,我就不会使用它。 - sffc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接