老生常谈问题之数组去重

2017-09-28  本文已影响77人  埃米莉Emily

排序去重

思路:

function unique (arr){
    this.sort()
    let res = [this[0]]
    for (var i = 1; i < this.length; i++) {
      if (this[i] !== res[res.length - 1]) {
      res.push(this[i]);
      }
    }
    return res
}
const arr = [1, 'a', 'm', 'c', 'd', 'e', 'e', 1, 0, -1]
alert(unique(arr))

双层循环去重

思路:

let array = [1, 'a', 'm', 'c', 'd', 'e', 'e', 1, 0, -1]

function unique(array) {
    // res用来存储结果
    let res = []
    for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
        for (var j = 0, resLen = res.length; j < resLen; j++ ) {
            if (array[i] === res[j]) {
                break
            }
        }
        if (j === resLen) {
            res.push(array[i])
        }
    }
    return res
}

console.log(unique(array))

indexOf 去重

思路:

const array = [1, 'a', 'm', 'c', 'd', 'e', 'e', 1, 0, -1]

function unique(array) {
    var res = [];
    for (var i = 0, len = array.length; i < len; i++) {
        var current = array[i];
        if (res.indexOf(current) === -1) {
            res.push(current)
        }
    }
    return res;
}

console.log(unique(array))

unique API

思路:

var array1 = [1, 2, '1', 2, 1];
var array2 = [1, 1, '1', 2, 2];

// 第一版
function unique(array, isSorted) {
    var res = [];
    var seen = [];

    for (var i = 0, len = array.length; i < len; i++) {
        var value = array[i];
        if (isSorted) {
            if (!i || seen !== value) {
                res.push(value)
            }
            seen = value;
        }
        else if (res.indexOf(value) === -1) {
            res.push(value);
        }        
    }
    return res;
}

console.log(unique(array1)); // [1, 2, "1"]
console.log(unique(array2, true)); // [1, "1", 2]

优化

尽管 unqique 已经可以试下去重功能,但是为了让这个 API 更加强大,我们来考虑一个需求:

新需求:字母的大小写视为一致,比如'a'和'A',保留一个就可以了!

虽然我们可以先处理数组中的所有数据,比如将所有的字母转成小写,然后再传入unique函数,但是有没有方法可以省掉处理数组的这一遍循环,直接就在去重的循环中做呢?让我们去完成这个需求:

const array3 = [1, 1, 'a', 'A', 2, 2];

// 第二版
// iteratee 英文释义:迭代 重复
function unique(array, isSorted, iteratee) {
    let res = [];
    let seen = [];

    for (var i = 0, len = array.length; i < len; i++) {
        let value = array[i];
        let computed = iteratee ? iteratee(value, i, array) : value;
        if (isSorted) {
            if (!i || seen !== value) {
                res.push(value)
            }
            seen = value;
        }
        else if (iteratee) {
            if (seen.indexOf(computed) === -1) {
                seen.push(computed);
                res.push(value);
            }
        }
        else if (res.indexOf(value) === -1) {
            res.push(value);
        }        
    }
    return res;
}

console.log(unique(array3, false, function(item) {
    return typeof item == 'string' ? item.toLowerCase() : item
}))

在这一版也是最后一版的实现中,函数传递三个参数:

array:表示要去重的数组,必填

isSorted:表示函数传入的数组是否已排过序,如果为 true,将会采用更快的方法进行去重

iteratee:传入一个函数,可以对每个元素进行重新的计算,然后根据处理的结果进行去重

filter

ES5 提供了 filter 方法,我们可以用来简化外层循环:

比如使用 indexOf 的方法:

const array = [1, 2, 1, 1, '1']

function unique(array) {
    return array.filter(function(item, index, array){
        return array.indexOf(item) === index;
    })
}

console.log(unique(array))

排序去重的方法:

const array = [1, 2, 1, 1, '1']

function unique(array) {
    return array.concat().sort().filter(function(item, index, array){
        return !index || item !== array[index - 1]
    })
}

console.log(unique(array))

ES6

随着 ES6 的到来,去重的方法又有了进展,比如我们可以使用 Set 和 Map 数据结构,以 Set 为例,ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

是不是感觉就像是为去重而准备的?让我们来写一版:

const array = [1, 2, 1, 1, '1']

function unique(array) {
   return Array.from(new Set(array))
}

console.log(unique(array))

甚至可以再简化下:

function unique(array) {
    return [...new Set(array)];
}

还可以再简化下:

var unique = (a) => [...new Set(a)]

此外,如果用 Map 的话:

function unique (arr) {
    const seen = new Map()
    return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}

JavaScript 的进化

我们可以看到,去重方法从原始的 14 行代码到 ES6 的 1 行代码,其实也说明了 JavaScript 这门语言在不停的进步,相信以后的开发也会越来越高效。

上一篇 下一篇

猜你喜欢

热点阅读