问题

给定对象数组,例如:

[
  { name: 'neal', age: '18' },
  { name: 'neal', age: '18' },
  { name: 'jane', age: '20' },
]

name 与 age 都相同 视为同一业务键重复,给元素打上 flag: true/false(或返回重复组)。

单字段可用 Set;多字段要把字段合成键。

方法一:双循环(直观)

function markDuplicates(collection) {
  const n = collection.length;
  for (let i = 0; i < n; i++) {
    let count = 0;
    for (let j = 0; j < n; j++) {
      if (
        collection[i].name === collection[j].name &&
        collection[i].age === collection[j].age
      ) {
        count++;
      }
    }
    collection[i].flag = count > 1;
  }
  return collection;
}

时间 (O(n^2)),小数据够用。

方法二:哈希键(推荐)

function markDuplicates(collection) {
  const keyOf = (o) => `${o.name}\0${o.age}`;
  const freq = new Map();
  for (const o of collection) {
    const k = keyOf(o);
    freq.set(k, (freq.get(k) || 0) + 1);
  }
  for (const o of collection) {
    o.flag = freq.get(keyOf(o)) > 1;
  }
  return collection;
}

时间 (O(n))。键分隔符要选对象字段中不会出现的,或用 JSON.stringify([name, age])

方法三:groupBy

lodash/underscore 的 groupBy 一次只按一字段时,需要嵌套或自定义 iteratee:

_.groupBy(collection, (o) => `${o.name}|${o.age}`);

长度 > 1 的组即重复。

注意

小结

多属性判重 = 定义复合键 + 计数/分组。生产优先哈希,面试可先写双循环再优化。

业务场景

flag 只是一种展示方式;更常见是直接 filter 掉重复或弹出错误列表。复合键设计要稳定:字段顺序固定、空值策略明确(null'' 是否等同)。