# 数组去重

原文地址 (opens new window)

# 双重循环

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr) {
  let res = [];
  let i;
  let j;
  for (i = 0; i < arr.length; i++) {
    for (j = 0; j < res.length; j++) {
      if (res[j] === arr[i]) {
        break;
      }
    }
    if (j === res.length) {
      res.push(arr[i]);
    }
  }
  return res;
}
console.log(unique(arr));

# 循环 + indexOf

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr) {
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) {
      res.push(arr[i]);
    }
  }
  return res;
}
console.log(unique(arr));

# 排序 + 去重

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr) {
  let res = [];
  // 注意,sort排序并不总是正确的
  let sortArr = arr.sort();
  for (let i = 0; i < sortArr.length; i++) {
    if (i === 0 || sortArr[i] !== sortArr[i - 1]) {
      res.push(sortArr[i]);
    }
  }
  return res;
}
console.log(unique(arr));

# 封装 unique 函数

结合前面的方法,增加一个 isSorted 参数判断参数 arr 是否已排序

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr, isSorted) {
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    if (isSorted) {
      if (i === 0 || arr[i] !== arr[i - 1]) {
        res.push(arr[i]);
      }
    } else {
      if (res.indexOf(arr[i]) === -1) {
        res.push(arr[i]);
      }
    }
  }
  return res;
}
console.log(unique(arr));

# 增强 unique 函数

  • 新增功能:不区分大小写的去重。
  • 思路:增加一个自定义处理函数 processFn,对大小写进行处理
  • tip:之所以将 processFn 作为参数传入而不是写在 unique 函数内,是因为 processFn 还可以对每个元素进行任意处理,而不仅仅是区分大小写。
let arr = [1, 2, 'B', 'a', 'A', 'b'];
function unique(arr, isSorted, processFn) {
  let res = [];
  let temp = [];
  for (let i = 0; i < arr.length; i++) {
    // 根据是否区分大小写来处理当前的值
    const computed = processFn ? processFn(arr[i]) : arr[i];
    if (isSorted) {
      if (i === 0 || computed !== temp) {
        res.push(arr[i]);
        temp = computed;
      }
    } else if (processFn) {
      // 没排序,大小写不敏感
      // 使用temp来保存处理成不区分大小写后的所有元素
      if (temp.indexOf(computed) === -1) {
        temp.push(computed);
        res.push(arr[i]);
      }
    } else {
      // 没排序且大小写敏感
      if (res.indexOf(arr[i]) === -1) {
        res.push(arr[i]);
      }
    }
  }
  return res;
}
console.log(
  unique(arr, false, item => {
    return typeof item === 'string' ? item.toLowerCase() : item;
  })
);

# filter

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr) {
  return arr.filter((item, index, list) => {
    return list.indexOf(item) === index;
  });
}
console.log(unique(arr));

排序后去重

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr) {
  // 注意,sort排序并不总是正确的
  return arr
    .concat()
    .sort()
    .filter((item, index, list) => {
      return !index || item !== list[index - 1];
    });
}
console.log(unique(arr));

# map 结构(object)

let arr = [1, 2, 1, 4, '1', 'b', 'b', { name: 'foo' }, { name: 'foo' }, { name: 'baz' }];
function unique(arr) {
  // 也可以使用 new Map() 和 new WeakMap()
  // Map的键除了string可以是其他任意类型的值
  // WeakMap的键只能是对象(null除外),且键的引用是弱引用,不参与垃圾回收
  let map = {};
  let res = [];
  for (let i = 0; i < arr.length; i++) {
    // 区分 1 和 ”1“,使用类型+值的字符串作为key
    // let key = typeof arr[i] + arr[i]
    // typeof arr[i] + arr[i] 不能处理对象,比如:
    // typeof { name: 'foo' } + { name: 'foo' } -> "object" + { name: 'foo' }.toString() -> "object[object Object]"
    // typeof { name: 'bar' } + { name: 'bar' } -> "object" + { name: 'bar' }.toString() -> "object[object Object]"
    // 另外对象的形式一致认为相同,比如 { name: 'foo' } 等于 { name: 'foo' },否则所有对象都不全等
    // 因此使用 JSON.stringify(arr[i])
    let key = typeof arr[i] + JSON.stringify(arr[i]);
    // 排除原型链上的同名属性
    if (!map.hasOwnProperty(key)) {
      map[key] = true;
      res.push(arr[i]);
    }
  }
  return res;
}
console.log(unique(arr));

# ES6 的 Set

let arr = [1, 2, 1, 4, 'a', 'b', 'b'];
function unique(arr) {
  // return [...new Set(arr)]
  return Array.from(new Set(arr));
}
console.log(unique(arr));