【前端】vue数组去重的3种方法

'# 【前端】vue数组去重的3种方法

一、背景与问题

在Vue开发中,数组去重是一个高频操作场景。无论是用户输入数据的处理、API返回数据的清洗,还是组件间数据传递的规范化,都需要对数组进行去重处理。但实际开发中,开发者常遇到以下问题:

  1. 简单类型数组去重有效,但对象数组去重失败
  2. 重复元素的识别逻辑不严谨导致遗漏
  3. 大数据量场景下性能问题
  4. Vue响应式更新机制导致的去重失效
  5. 去重逻辑破坏原始数据结构

理解这些本质问题,需要从数据结构原理和Vue响应式系统的底层机制出发。

二、基本原理

1. 去重算法原理

数组去重的核心是识别重复元素,其本质是集合操作。常见实现方式包括:

  • Set结构:利用哈希表特性,自动去重
  • 双指针法:通过比较元素值进行去重
  • 归并法:通过累积器逐步构建唯一集合

2. Vue响应式机制

Vue通过Object.defineProperty(Vue2)或Proxy(Vue3)实现响应式,但存在以下限制:

  • 对数组的变异方法(push/pop/shift等)会触发更新
  • 直接设置索引(arr[0] = 'new')不会触发更新
  • 替换整个数组(arr = [])会触发更新

三、环境准备

# 创建Vue项目
npm create vue@latest

项目结构建议:

src/
├── components/
│   └── UniqueList.vue
├── utils/
│   └── arrayUtils.js
├── App.vue
└── main.js

四、核心实现

方法一:使用Set结构(适用于简单类型)

// utils/arrayUtils.js
export function uniqueSet(arr) {
  return [...new Set(arr)];
}

关键点解释:

  • Set构造函数自动去重,但仅能处理可哈希类型(字符串、数字、Symbol等)
  • 对象数组需转换为字符串处理

方法二:filter+includes组合(适用于小数据量)

export function uniqueFilter(arr) {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}

性能分析:

  • 时间复杂度O(n²)
  • 适合数据量小于1000的场景
  • 对象数组需重写比较函数

方法三:reduce+JSON.stringify(适用于对象数组)

export function uniqueReduce(arr) {
  return arr.reduce((acc, curr) => {
    const key = JSON.stringify(curr);
    if (!acc.includes(key)) {
      acc.push(curr);
    }
    return acc;
  }, []);
}

注意事项:

  • 转换为字符串可能导致精度丢失(如数字的1e-16)
  • 对象属性顺序不同会导致字符串不同

五、完整案例

1. 实现一个可交互的去重组件

<!-- components/UniqueList.vue -->
<template>
  <div>
    <input v-model="newItem" placeholder="输入内容" />
    <button @click="addItem">添加</button>
    <ul>
      <li v-for="(item, index) in uniqueList" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script>
import { uniqueSet, uniqueFilter } from '../utils/arrayUtils';

export default {
  data() {
    return {
      newItem: '',
      originalList: [],
      uniqueList: []
    };
  },
  methods: {
    addItem() {
      if (this.newItem.trim()) {
        this.originalList = [...this.originalList, this.newItem];
        this.uniqueList = uniqueSet([...this.originalList]);
        this.newItem = '';
      }
    }
  }
};
</script>

关键点解析:

  • 使用Set方法处理字符串数组
  • 通过数组赋值触发Vue响应式更新
  • 保留原始数组用于后续处理

2. 对象数组去重示例

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 1, name: 'Alice' }
];

// 使用reduce方法去重
const uniqueUsers = users.reduce((acc, curr) => {
  const key = JSON.stringify(curr);
  if (!acc.includes(key)) {
    acc.push(curr);
  }
  return acc;
}, []);

console.log(uniqueUsers.length); // 输出2

六、源码解析

Set方法源码实现

function uniqueSet(arr) {
  const seen = new Set();
  return arr.filter(item => {
    const key = typeof item === 'object' && item !== null 
      ? JSON.stringify(item) 
      : item;
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

关键点:

  • 处理对象数组时进行字符串转换
  • 使用Set保证唯一性
  • 保持原数组顺序

Filter方法源码实现

function uniqueFilter(arr) {
  return arr.filter((item, index, self) => {
    return self.findIndex(el => 
      typeof el === 'object' ? JSON.stringify(el) === JSON.stringify(item) : el === item
    ) === index;
  });
}

优化点:

  • 使用JSON.stringify比较对象
  • 保持原数组顺序
  • 支持复杂类型比较

七、进阶使用

1. 响应式数组去重

// 使用Vue3的ref
import { ref } from 'vue';

const list = ref([]);
const uniqueList = ref([]);

function add(item) {
  list.value = [...list.value, item];
  uniqueList.value = uniqueSet([...list.value]);
}

2. 带状态的去重

const state = reactive({
  list: [],
  uniqueList: [],
  filter: ''
});

function updateFilter(value) {
  state.filter = value;
  state.uniqueList = uniqueFilter(state.list.filter(item => 
    typeof item === 'string' ? item.includes(value) : true
  ));
}

3. 带排序的去重

function uniqueAndSort(arr, key = 'id', order = 'asc') {
  return arr
    .reduce((acc, curr) => {
      const keyVal = typeof key === 'function' ? key(curr) : curr[key];
      if (!acc.some(item => 
        typeof key === 'function' ? key(item) === keyVal : item[key] === keyVal
      )) {
        acc.push(curr);
      }
      return acc;
    }, [])
    .sort((a, b) => {
      const aVal = typeof key === 'function' ? key(a) : a[key];
      const bVal = typeof key === 'function' ? key(b) : b[key];
      return order === 'asc' ? aVal - bVal : bVal - aVal;
    });
}

八、性能与工程实践

1. 性能优化策略

场景优化方案效果
小数据量filter+includes无明显性能损失
大数据量Set结构时间复杂度O(n)
对象数组JSON.stringify转换成本增加但性能稳定
频繁操作延迟去重减少DOM更新次数

2. 响应式优化

  • 使用computed替代手动去重
  • 对数组进行冻结处理
  • 使用v-model双向绑定控制输入

3. 异常处理

try {
  const result = uniqueSet([...list.value]);
  console.log(result);
} catch (e) {
  console.error('去重失败:', e);
  // 处理异常情况,如数据格式错误
}

九、常见问题与踩坑

1. 常见错误示例

// 错误示例:直接修改数组
this.list[0] = 'new value'; // 不会触发更新

解决办法:

// 正确方式:使用变异方法
this.$set(this.list, 0, 'new value');

2. 对象数组去重失败

// 错误示例:直接比较对象引用
const arr = [{id:1}, {id:1}];
arr.filter(item => item.id !== 1); // 返回空数组

解决办法:

// 正确方式:比较对象内容
const arr = [{id:1}, {id:1}];
arr.filter((item, index) => 
  arr.findIndex(el => el.id === item.id) === index
);

3. 响应式更新失效

// 错误示例:直接替换数组
this.list = this.list.filter(...); // 触发更新
this.list = [...this.list]; // 触发更新
this.list = uniqueSet(this.list); // 触发更新

十、最佳实践

1. 使用场景推荐

场景推荐方法原因
简单类型数组Set简洁高效
小数据量filter+includes代码清晰
对象数组reduce+JSON.stringify保证完整性
需要排序uniqueAndSort综合处理
频繁操作computed自动优化

2. 开发规范建议

  • 始终使用JSON.stringify处理对象数组
  • 避免直接修改数组索引
  • 对大数据量使用Set结构
  • 保持去重逻辑与原始数据解耦
  • 在组件销毁时清理相关状态

十一、总结

Vue数组去重的本质是数据结构的集合操作,不同方法适用于不同场景。需要根据数据类型、数据量、业务需求选择合适方案。在开发过程中要特别注意Vue的响应式机制,避免因直接修改数组导致更新失效。对于复杂对象数组,推荐使用JSON.stringify进行转换比较,同时注意处理精度丢失问题。在实际项目中,建议采用computed属性和响应式编程思想,将去重逻辑与组件解耦,提升代码的可维护性。

VUE
最后修改于:2026年10月07日 19:48

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日