Skip to content

uniqueBy

uniqueBy 对象去重

用于对“对象数组”进行去重,支持按指定键(点路径)或选择器函数生成唯一键进行去重;可选择保留第一次或最后一次出现的元素。

一、引入与使用

支持两种方式引入:

ts
// 方式一:按需子路径引入(推荐)
import { uniqueBy } from '@cuixingjian/cui-utils/uniqueBy'

// 方式二:从包入口命名导出引入
import { uniqueBy } from '@cuixingjian/cui-utils'

二、使用示例

ts
import { uniqueBy } from '@cuixingjian/cui-utils/uniqueBy'

const list = [
  { id: 1, name: 'cui', info: { tag: 'ui' } },
  { id: 2, name: 'cui' },
  { id: 1, name: 'cui', info: { tag: 'framework' } },
]

// 1) 按单键去重(保留首次出现)
const byId = uniqueBy(list, 'id')
// 结果: [{ id:1, name:'cui', info:{tag:'ui'} }, { id:2, name:'cui' }]

// 2) 按多键组合去重(id + name)
const byIdName = uniqueBy(list, ['id', 'name'])
// 结果: 与 byId 相同,因为 name 相同

// 3) 选择器函数生成唯一键(自定义逻辑)
const bySelector = uniqueBy(list, (item) => `${item.id}-${item.name}`)

// 4) 保留最后一次出现(覆盖先前元素)
const keepLast = uniqueBy(list, 'id', { keep: 'last' })
// 结果: id=1 的元素取最后一个,即 info.tag 为 'framework'

// 5) 无选择器/键时:原始值按值去重,对象按 JSON 字符串去重
const nums = uniqueBy([1, 1, 2, 2, 3]) // [1,2,3]
const objs = uniqueBy([{ a: 1 }, { a: 1 }]) // [{ a:1 }]

三、API

方法说明类型
uniqueBy对对象数组进行去重并返回新数组<T>(list: T[], selector?: ((item: T) => unknown) | string | string[], options?: UniqueByOptions) => T[]

参数

参数名说明类型默认值
list需要去重的数组T[]
selector唯一键生成方式:函数、单键(点路径)或多键数组((item: T) => unknown) | string | string[]
options去重选项UniqueByOptions{ keep: 'first' }

UniqueByOptions

字段说明类型默认值
keep重复项保留策略:first 保留首次;last 保留最后一次'first' | 'last''first'

返回值

名称说明类型
result去重后的新数组T[]

四、注意事项

  • selector 为字符串时支持点路径(如 "info.tag")。为多键数组时会按顺序拼接值生成唯一键。
  • 未指定 selector 时,对象通过 JSON.stringify 的结果去重,属性顺序会影响结果;如需稳健的深度比较,请使用选择器函数明确生成唯一键。
  • 该函数不修改原数组,返回新数组。

五、源码

展开查看
ts
// 源码来自 @cuixingjian/cui-utils/uniqueBy
export type KeySelector<T> = (item: T) => unknown

export interface UniqueByOptions {
  keep?: 'first' | 'last'
}

function getByPath(obj: any, path: string): unknown {
  const parts = path.split('.')
  let cur = obj
  for (const p of parts) {
    if (cur == null) return undefined
    cur = cur[p]
  }
  return cur
}

export function uniqueBy<T>(
  list: T[],
  selector?: KeySelector<T> | string | string[],
  options: UniqueByOptions = {}
): T[] {
  const { keep = 'first' } = options
  if (!Array.isArray(list) || list.length === 0) return []

  const map = new Map<unknown, T>()

  const makeKey = (item: T): unknown => {
    if (typeof selector === 'function') {
      return selector(item)
    }
    if (typeof selector === 'string') {
      return getByPath(item as any, selector)
    }
    if (Array.isArray(selector)) {
      return selector.map((k) => getByPath(item as any, k)).join('|')
    }
    if (item === null) return item
    const t = typeof item
    if (t === 'object') return JSON.stringify(item)
    return item as unknown
  }

  for (const it of list) {
    const key = makeKey(it)
    if (!map.has(key)) {
      map.set(key, it)
    } else if (keep === 'last') {
      map.set(key, it)
    }
  }

  return Array.from(map.values())
}