前端多个数组合并的多种方式

36 阅读8分钟

前端多个数组合并的多种方式

1. concat() 方法 - 不改变原数组

// 基础用法
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr3 = [7, 8, 9]

// 合并两个数组
const result1 = arr1.concat(arr2)
console.log(result1) // [1, 2, 3, 4, 5, 6]

// 合并多个数组
const result2 = arr1.concat(arr2, arr3)
console.log(result2) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 合并数组和值
const result3 = arr1.concat(4, 5, arr2)
console.log(result3) // [1, 2, 3, 4, 5, 4, 5, 6]

// 注意:不改变原数组
console.log(arr1) // [1, 2, 3]
console.log(arr2) // [4, 5, 6]

2. 扩展运算符 Spread Operator (ES6+)

// ES6+ 扩展运算符
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr3 = [7, 8, 9]

// 合并数组
const result1 = [...arr1, ...arr2]
console.log(result1) // [1, 2, 3, 4, 5, 6]

// 合并多个数组
const result2 = [...arr1, ...arr2, ...arr3]
console.log(result2) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 在中间插入
const result3 = [0, ...arr1, 4, ...arr2, 7]
console.log(result3) // [0, 1, 2, 3, 4, 4, 5, 6, 7]

// 与 concat 比较
const result4 = [...arr1, ...arr2, ...arr3]
const result5 = arr1.concat(arr2, arr3)
console.log(result4) // 同 result5

3. push() 方法 - 原地修改

// push 方法(修改原数组)
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr3 = [7, 8, 9]

// 使用 apply 合并
Array.prototype.push.apply(arr1, arr2)
console.log(arr1) // [1, 2, 3, 4, 5, 6]

// 使用扩展运算符
arr1.push(...arr3)
console.log(arr1) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 封装函数
function mergeArraysInPlace(target, ...arrays) {
  arrays.forEach(arr => {
    target.push(...arr)
  })
  return target
}

const target = [1, 2, 3]
mergeArraysInPlace(target, [4, 5], [6, 7, 8])
console.log(target) // [1, 2, 3, 4, 5, 6, 7, 8]

4. reduce() 方法

// 使用 reduce 合并多个数组
const arrays = [  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

// 方法1: 使用 concat
const result1 = arrays.reduce((acc, curr) => acc.concat(curr), [])
console.log(result1) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 方法2: 使用扩展运算符
const result2 = arrays.reduce((acc, curr) => [...acc, ...curr], [])
console.log(result2) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 带条件的合并
const arrays2 = [  [1, 2, 3],
  [4, 5],
  [6, 7, 8, 9]
]

const result3 = arrays2.reduce((acc, curr, index) => {
  if (index % 2 === 0) {  // 只合并偶数索引的数组
    return [...acc, ...curr]
  }
  return acc
}, [])
console.log(result3) // [1, 2, 3, 6, 7, 8, 9]

5. flat() 方法 (ES2019)

// flat 方法 - 扁平化数组
const nestedArrays = [  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

// 默认扁平化一层
const result1 = nestedArrays.flat()
console.log(result1) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 多层嵌套数组
const deeplyNested = [  [1, 2],
  [3, [4, 5]],
  [[6], [7, 8, 9]]
]

// 扁平化所有层
const result2 = deeplyNested.flat(Infinity)
console.log(result2) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 指定扁平化层数
const result3 = deeplyNested.flat(2)
console.log(result3) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

6. 对象数组合并(根据ID等)

// 根据ID合并对象数组
const arr1 = [
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Charlie', age: 35 }
]

const arr2 = [
  { id: 2, city: 'New York', job: 'Engineer' },
  { id: 3, city: 'London', job: 'Designer' },
  { id: 4, city: 'Tokyo', job: 'Developer' }
]

// 方法1: 使用 Map
function mergeObjectArrays(arr1, arr2, key = 'id') {
  const map = new Map()
  
  // 合并所有对象
  ;[...arr1, ...arr2].forEach(item => {
    const itemKey = item[key]
    if (map.has(itemKey)) {
      // 合并相同key的对象
      map.set(itemKey, { ...map.get(itemKey), ...item })
    } else {
      map.set(itemKey, item)
    }
  })
  
  return Array.from(map.values())
}

const merged1 = mergeObjectArrays(arr1, arr2, 'id')
console.log(merged1)
/*
[
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30, city: 'New York', job: 'Engineer' },
  { id: 3, name: 'Charlie', age: 35, city: 'London', job: 'Designer' },
  { id: 4, city: 'Tokyo', job: 'Developer' }
]
*/

// 方法2: 使用 reduce
const merged2 = [...arr1, ...arr2].reduce((acc, obj) => {
  const existing = acc.find(item => item.id === obj.id)
  if (existing) {
    Object.assign(existing, obj)
  } else {
    acc.push(obj)
  }
  return acc
}, [])

7. 数组合并并去重

// 合并并去重
const arr1 = [1, 2, 3, 4, 5]
const arr2 = [4, 5, 6, 7, 8]
const arr3 = [7, 8, 9, 10]

// 方法1: Set
const result1 = [...new Set([...arr1, ...arr2, ...arr3])]
console.log(result1) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 方法2: filter
const result2 = arr1.concat(
  arr2.filter(item => !arr1.includes(item)),
  arr3.filter(item => !arr1.includes(item) && !arr2.includes(item))
)
console.log(result2) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 方法3: reduce
const allArrays = [arr1, arr2, arr3]
const result3 = allArrays.reduce((acc, curr) => {
  curr.forEach(item => {
    if (!acc.includes(item)) {
      acc.push(item)
    }
  })
  return acc
}, [])
console.log(result3) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 对象数组去重合并
const objArr1 = [{id: 1}, {id: 2}]
const objArr2 = [{id: 2}, {id: 3}]

const mergedObjArray = [...objArr1, ...objArr2].reduce((acc, obj) => {
  if (!acc.some(item => item.id === obj.id)) {
    acc.push(obj)
  }
  return acc
}, [])
console.log(mergedObjArray) // [{id: 1}, {id: 2}, {id: 3}]

8. 异步数组合并(Promise)

// 异步数组合并
async function mergeAsyncArrays() {
  // 模拟异步获取数组
  const getArray1 = () => Promise.resolve([1, 2, 3])
  const getArray2 = () => Promise.resolve([4, 5, 6])
  const getArray3 = () => Promise.resolve([7, 8, 9])
  
  // 方法1: Promise.all
  const [arr1, arr2, arr3] = await Promise.all([
    getArray1(),
    getArray2(),
    getArray3()
  ])
  const result1 = [...arr1, ...arr2, ...arr3]
  console.log('方法1:', result1)
  
  // 方法2: 链式调用
  const result2 = []
  const arrays = [getArray1, getArray2, getArray3]
  for (const getArray of arrays) {
    const arr = await getArray()
    result2.push(...arr)
  }
  console.log('方法2:', result2)
  
  // 方法3: reduce + async
  const result3 = await arrays.reduce(async (accPromise, getArray) => {
    const acc = await accPromise
    const arr = await getArray()
    return [...acc, ...arr]
  }, Promise.resolve([]))
  console.log('方法3:', result3)
  
  return result1
}

mergeAsyncArrays()

9. Vue 3 响应式数组合并

<template>
  <div>
    <h3>数组1: {{ array1 }}</h3>
    <h3>数组2: {{ array2 }}</h3>
    <h3>合并结果: {{ mergedArray }}</h3>
    
    <button @click="addToArray1">向数组1添加</button>
    <button @click="addToArray2">向数组2添加</button>
    <button @click="mergeArrays">合并数组</button>
    <button @click="reset">重置</button>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'

// 响应式数组
const array1 = ref([1, 2, 3])
const array2 = ref([4, 5, 6])

// 方法1: 使用计算属性(自动响应)
const mergedArray = computed(() => [...array1.value, ...array2.value])

// 方法2: 手动合并
const manualMerge = ref([])

const mergeArrays = () => {
  // 使用扩展运算符
  manualMerge.value = [...array1.value, ...array2.value]
  
  // 或使用 concat
  // manualMerge.value = array1.value.concat(array2.value)
}

// 添加元素
const addToArray1 = () => {
  array1.value.push(array1.value.length + 1)
}

const addToArray2 = () => {
  array2.value.push(array2.value.length + 4)
}

// 重置
const reset = () => {
  array1.value = [1, 2, 3]
  array2.value = [4, 5, 6]
  manualMerge.value = []
}

// 监听数组合并
watch([array1, array2], () => {
  console.log('数组变化,自动合并:', [...array1.value, ...array2.value])
}, { deep: true })
</script>

10. 性能优化的大数组合并

// 大数组合并的性能优化
class ArrayMerger {
  // 使用 Uint8Array 等类型化数组(性能更高)
  static mergeTypedArrays(arrays) {
    // 计算总长度
    const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0)
    
    // 创建类型化数组
    const result = new Uint8Array(totalLength)
    
    // 手动复制
    let offset = 0
    for (const arr of arrays) {
      result.set(arr, offset)
      offset += arr.length
    }
    
    return result
  }
  
  // 流式合并(处理超大数组)
  static *mergeStreaming(arrays) {
    for (const arr of arrays) {
      for (const item of arr) {
        yield item
      }
    }
  }
  
  // 分块合并
  static mergeLargeArrays(arrays, chunkSize = 10000) {
    const result = []
    
    for (const arr of arrays) {
      // 分块处理
      for (let i = 0; i < arr.length; i += chunkSize) {
        const chunk = arr.slice(i, i + chunkSize)
        result.push(...chunk)
      }
    }
    
    return result
  }
}

// 使用示例
const largeArray1 = new Uint8Array(1000000).fill(1)
const largeArray2 = new Uint8Array(1000000).fill(2)

console.time('mergeTypedArrays')
const merged = ArrayMerger.mergeTypedArrays([largeArray1, largeArray2])
console.timeEnd('mergeTypedArrays')
console.log('合并后长度:', merged.length)

// 流式处理
const arrays = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
const streamResult = Array.from(ArrayMerger.mergeStreaming(arrays))
console.log('流式合并:', streamResult)

11. 多维数组合并

// 多维数组合并
const matrix1 = [
  [1, 2],
  [3, 4]
]

const matrix2 = [
  [5, 6],
  [7, 8]
]

// 水平合并(增加列)
function mergeHorizontally(matrix1, matrix2) {
  return matrix1.map((row, i) => [...row, ...(matrix2[i] || [])])
}

console.log('水平合并:')
console.log(mergeHorizontally(matrix1, matrix2))
// [[1, 2, 5, 6], [3, 4, 7, 8]]

// 垂直合并(增加行)
function mergeVertically(matrix1, matrix2) {
  return [...matrix1, ...matrix2]
}

console.log('垂直合并:')
console.log(mergeVertically(matrix1, matrix2))
// [[1, 2], [3, 4], [5, 6], [7, 8]]

// 深度合并
const deepArray1 = [[[1, 2], [3, 4]], [[5, 6]]]
const deepArray2 = [[[7, 8]], [[9, 10], [11, 12]]]

function deepMergeArrays(arr1, arr2) {
  if (Array.isArray(arr1[0]) && Array.isArray(arr2[0])) {
    // 如果都是数组,递归合并
    return arr1.map((subArr, i) => 
      deepMergeArrays(subArr, arr2[i] || [])
    )
  } else {
    // 否则直接合并
    return [...arr1, ...arr2]
  }
}

console.log('深度合并:')
console.log(deepMergeArrays(deepArray1, deepArray2))

12. 实用工具函数

// 数组合并工具类
class ArrayMergeUtils {
  /**
   * 合并多个数组
   * @param {...Array} arrays 要合并的数组
   * @returns {Array} 合并后的数组
   */
  static merge(...arrays) {
    return [].concat(...arrays)
  }
  
  /**
   * 深度合并数组(递归)
   */
  static deepMerge(...arrays) {
    return arrays.reduce((acc, curr) => {
      if (Array.isArray(curr)) {
        return [...acc, ...this.deepMerge(...curr)]
      }
      return [...acc, curr]
    }, [])
  }
  
  /**
   * 按条件合并数组
   * @param {Array} arrays 数组
   * @param {Function} condition 条件函数
   */
  static mergeByCondition(arrays, condition) {
    return arrays.reduce((acc, curr, index) => {
      if (condition(curr, index, arrays)) {
        return [...acc, ...curr]
      }
      return acc
    }, [])
  }
  
  /**
   * 交错合并(zip merge)
   * @example mergeInterleave([1,3,5], [2,4,6]) => [1,2,3,4,5,6]
   */
  static mergeInterleave(...arrays) {
    const maxLength = Math.max(...arrays.map(arr => arr.length))
    const result = []
    
    for (let i = 0; i < maxLength; i++) {
      for (const arr of arrays) {
        if (i < arr.length) {
          result.push(arr[i])
        }
      }
    }
    
    return result
  }
  
  /**
   * 分组合并
   */
  static mergeInGroups(arrays, groupSize = 2) {
    const result = []
    
    for (let i = 0; i < arrays.length; i += groupSize) {
      const group = arrays.slice(i, i + groupSize)
      const merged = this.merge(...group)
      result.push(merged)
    }
    
    return result
  }
}

// 使用示例
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr3 = [7, 8, 9]

console.log('基本合并:', ArrayMergeUtils.merge(arr1, arr2, arr3))
console.log('交错合并:', ArrayMergeUtils.mergeInterleave(arr1, arr2, arr3))
console.log('按条件合并:', 
  ArrayMergeUtils.mergeByCondition([arr1, arr2, arr3], (arr, index) => index % 2 === 0)
)

// 分组合并
const allArrays = [arr1, arr2, arr3, [10, 11, 12]]
console.log('分组合并:', ArrayMergeUtils.mergeInGroups(allArrays, 2))
// [[1,2,3,4,5,6], [7,8,9,10,11,12]]

13. TypeScript 泛型版本

// TypeScript 泛型工具函数
class ArrayMergerTS {
  /**
   * 合并多个数组
   */
  static merge<T>(...arrays: T[][]): T[] {
    return ([] as T[]).concat(...arrays)
  }
  
  /**
   * 合并并去重
   */
  static mergeUnique<T>(...arrays: T[][]): T[] {
    return Array.from(new Set(this.merge(...arrays)))
  }
  
  /**
   * 根据键合并对象数组
   */
  static mergeObjectsByKey<T extends Record<string, any>>(
    arrays: T[][],
    key: keyof T
  ): T[] {
    const map = new Map<any, T>()
    
    arrays.flat().forEach(item => {
      const itemKey = item[key]
      if (map.has(itemKey)) {
        map.set(itemKey, { ...map.get(itemKey)!, ...item })
      } else {
        map.set(itemKey, item)
      }
    })
    
    return Array.from(map.values())
  }
  
  /**
   * 条件合并
   */
  static mergeIf<T>(
    arrays: T[][],
    predicate: (item: T, index: number, array: T[]) => boolean
  ): T[] {
    return arrays.reduce((acc, curr) => {
      const filtered = curr.filter(predicate)
      return [...acc, ...filtered]
    }, [] as T[])
  }
}

// 使用示例
const nums1: number[] = [1, 2, 3]
const nums2: number[] = [4, 5, 6]
const mergedNums = ArrayMergerTS.merge(nums1, nums2)
// 类型推断为 number[]

const objArr1 = [{ id: 1, name: 'Alice' }]
const objArr2 = [{ id: 1, age: 25 }, { id: 2, name: 'Bob' }]
const mergedObjs = ArrayMergerTS.mergeObjectsByKey([objArr1, objArr2], 'id')
// 正确合并对象

总结

常用方法对比:

方法是否修改原数组性能适用场景
concat()❌ 不修改良好基本合并,兼容性好
扩展运算符 ...❌ 不修改良好ES6+,代码简洁
push(...arr)✅ 修改良好原地合并,修改原数组
reduce()取决于实现一般复杂合并逻辑
flat()❌ 不修改良好多维数组合并

选择建议:

  1. 简单合并:使用扩展运算符 [...arr1, ...arr2]
  2. 兼容性要求:使用 concat()
  3. 原地修改:使用 push(...arr)
  4. 多维合并:使用 flat()
  5. 对象合并:使用 Mapreduce
  6. 大数组合并:分块处理或使用类型化数组
  7. 去重合并:使用 Set