首页 > JavaScript > 从JS数组中删除重复值?

从JS数组中删除重复值?

上一篇 下一篇

获取 JavaScript 数组中的所有唯一值(删除重复项) ?

我有一个非常简单的 JavaScript 数组,可能包含也可能不包含重复项。

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

我需要删除重复项并将唯一值放在新数组中。

我可以指出我尝试过的所有代码,但我认为它毫无用处,因为它们不起作用。我也接受jQuery解决方案。

类似的问题:

  • 获取数组中的所有非唯一值(即:重复/多次出现)

分割线

网友回答:

使用jQuery快速而肮脏:

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});

分割线

网友回答:

博士

使用 Set 构造函数和扩展语法:

uniq = [...new Set(array)];

(请注意,var uniq 将是一个数组…new Set() 将其转换为集合,但 [… ] 再次将其变回数组)


“聪明”但幼稚的方式

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

基本上,我们遍历数组,并针对每个元素检查该元素在数组中的第一个位置是否等于当前位置。显然,对于重复元素,这两个位置是不同的。

使用过滤器回调的第 3 个(“this array”)参数,我们可以避免数组变量的闭包:

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

虽然简洁,但此算法对于大型数组(二次时间)并不是特别有效。

哈希表救援

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

这是通常的做法。这个想法是将每个元素放在哈希表中,然后立即检查其是否存在。这给了我们线性时间,但至少有两个缺点:

  • 由于哈希键在 JavaScript 中只能是字符串或符号,因此此代码不区分数字和“数字字符串”。也就是说,将返回uniq([1,"1"])[1]
  • 出于同样的原因,所有对象都将被视为相等:将只返回 .uniq([{foo:1},{foo:2}])[{foo:1}]

也就是说,如果您的数组仅包含基元并且您不关心类型(例如,它总是数字),则此解决方案是最佳的。

来自两个世界的精华

通用解决方案结合了这两种方法:它使用原语的哈希查找和对象的线性搜索。

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

排序 |优衣空

另一种选择是先对数组进行排序,然后删除与前一个元素相等的每个元素:

function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    });
}

同样,这不适用于对象(因为所有对象都相等)。此外,我们默默地更改原始数组作为副作用 – 不好!但是,如果您的输入已经排序,这是要走的路(只需从上面删除)。sortsort

独特的…

有时,需要根据相等以外的某些条件对列表进行单化,例如,筛选出不同但共享某些属性的对象。这可以通过传递回调来优雅地完成。此“键”回调应用于每个元素,并删除具有相等“键”的元素。由于预期返回一个原语,哈希表在这里可以正常工作:key

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

A particularly useful is which will remove objects that are physically different, but “look” the same:key()JSON.stringify

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

如果不是基元的,则必须诉诸线性搜索:key

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

在 ES6 中,您可以使用:Set

function uniqBy(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

或 :Map

function uniqBy(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

两者都适用于非原始键。

第一个还是最后一个?

通过键删除对象时,您可能希望保留第一个“相等”对象或最后一个对象。

使用上面的变体保留第一个,保留最后一个:SetMap

function uniqByKeepFirst(a, key) {
    let seen = new Set();
    return a.filter(item => {
        let k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}


function uniqByKeepLast(a, key) {
    return [
        ...new Map(
            a.map(x => [key(x), x])
        ).values()
    ]
}

//

data = [
    {a:1, u:1},
    {a:2, u:2},
    {a:3, u:3},
    {a:4, u:1},
    {a:5, u:2},
    {a:6, u:3},
];

console.log(uniqByKeepFirst(data, it => it.u))
console.log(uniqByKeepLast(data, it => it.u))

图书馆

下划线和 Lo-Dash 都提供了方法。他们的算法基本上类似于上面的第一个片段,归结为:uniq

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

这是二次的,但还有一些不错的附加好处,比如包装原生,通过键 uniqify 的能力(用他们的说法),以及对已经排序的数组的优化。indexOfiteratee

如果你正在使用jQuery,并且没有一美元就无法忍受任何东西,它是这样的:

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

这又是第一个片段的变体。

性能

函数调用在 JavaScript 中是昂贵的,因此上述解决方案虽然简洁,但并不是特别有效。为了获得最大的性能,请替换为循环并删除其他函数调用:filter

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

这块丑陋的代码与上面的代码片段 #3 相同,但快了一个数量级(截至 2017 年,它的速度只有两倍——JS 核心人员做得很好!

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

/////

var r = [0,1,2,3,4,5,6,7,8,9],
    a = [],
    LEN = 1000,
    LOOPS = 1000;

while(LEN--)
    a = a.concat(r);

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq(a);
document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS)

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)

ES6

ES6 提供了 Set 对象,这使得事情变得容易得多:

function uniq(a) {
   return Array.from(new Set(a));
}

let uniq = a => [...new Set(a)];

请注意,与 python 不同,ES6 集按插入顺序迭代,因此此代码保留原始数组的顺序。

但是,如果您需要一个具有唯一元素的数组,为什么不从一开始就使用集合呢?

发电机

一个“懒惰”的、基于生成器的版本可以在同样的基础上构建:uniq

  • 从参数中获取下一个值
  • 如果已经看到,请跳过它
  • 否则,生成它并将其添加到已见过的值集中
function* uniqIter(a) {
    let seen = new Set();

    for (let x of a) {
        if (!seen.has(x)) {
            seen.add(x);
            yield x;
        }
    }
}

// example:

function* randomsBelow(limit) {
    while (1)
        yield Math.floor(Math.random() * limit);
}

// note that randomsBelow is endless

count = 20;
limit = 30;

for (let r of uniqIter(randomsBelow(limit))) {
    console.log(r);
    if (--count === 0)
        break
}

// exercise for the reader: what happens if we set `limit` less than `count` and why

分割线

网友回答:

厌倦了看到所有带有for循环或jQuery的坏例子。Javascript现在有完美的工具:排序,映射和减少。

Uniq 在保持现有订单的同时减少

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

更快的 uniq 与排序

可能有更快的方法,但这个相当不错。

var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a > b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

更新 2015: ES6 版本:

在 ES6 中,您有设置和跨页,这使得删除所有重复项变得非常容易和高性能:

var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

根据出现次数排序:

有人询问如何根据唯一名称的数量对结果进行排序:

var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

var uniq = names
  .map((name) => {
    return {count: 1, name: name}
  })
  .reduce((a, b) => {
    a[b.name] = (a[b.name] || 0) + b.count
    return a
  }, {})

var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b])

console.log(sorted)

模板简介:该模板名称为【从JS数组中删除重复值?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【JavaScript】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 285次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 03-18
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
html5 单页式简历模板 微信素材 微信图片 微信模板 微信公众平台 微信文章 企业网站 响应式 自适应
您可能会喜欢的其他模板