xDocxDoc
AI
前端
后端
iOS
Android
Flutter
AI
前端
后端
iOS
Android
Flutter
  • map()和filter()配合的实用技巧

map()和filter()配合的实用技巧

一、两层嵌套数组的过滤

1.1 ❌ 臃肿方式:双重循环与临时变量

// 原始数据:二维用户数组
const users2Level = [
  [{name: 'John', age: 25}, {name: 'Jane', age: 30}],
  [{name: 'Bob', age: 35}, {name: 'Alice', age: 20}]
];

// 过滤出年龄大于30的用户
function filter2LevelArrayBulky(array, condition) {
  let result = [];
  for (let i = 0; i < array.length; i++) {
    const innerArray = array[i];
    let innerResult = [];
    for (let j = 0; j < innerArray.length; j++) {
      if (condition(innerArray[j])) {
        innerResult.push(innerArray[j]);
      }
    }
    if (innerResult.length > 0) {
      result.push(innerResult);
    }
  }
  return result;
}

const filteredBulky = filter2LevelArrayBulky(users2Level, user => user.age > 30);
console.log(filteredBulky);
// 输出: [[{name: 'Bob', age: 35}]]

缺点分析:

  • 代码冗长,需要手动管理循环和索引
  • 需要创建多个临时变量(innerArray, innerResult)
  • 可读性差,容易出错

1.2 ✅ 简洁方式:结合 map 和 filter

const users2Level = [
  [{name: 'John', age: 25}, {name: 'Jane', age: 30}],
  [{name: 'Bob', age: 35}, {name: 'Alice', age: 20}]
];

const filteredConcise = users2Level.map(innerArray => 
  innerArray.filter(user => user.age > 30)
).filter(innerArray => innerArray.length > 0);

console.log(filteredConcise);
// 输出: [[{name: 'Bob', age: 35}]]

优点分析:

  • 代码简洁易读
  • 使用高阶函数,避免手动循环
  • 链式调用,逻辑清晰

1.3 ✅ 更优方式:单次 reduce 完成

const filteredOptimal = users2Level.reduce((acc, innerArray) => {
  const filteredInner = innerArray.filter(user => user.age > 30);
  if (filteredInner.length > 0) {
    acc.push(filteredInner);
  }
  return acc;
}, []);

console.log(filteredOptimal);
// 输出: [[{name: 'Bob', age: 35}]]

优点分析:

  • 只需一次遍历外部数组
  • 避免创建中间数组

二、多层嵌套数组的过滤

当遇到三层或更多层嵌套时,问题变得更加复杂。

2.1 ❌ 臃肿方式:深度嵌套循环

// 三层嵌套数组
const users3Level = [
  [
    [{name: 'John', age: 25}, {name: 'Jane', age: 30}]
  ],
  [
    [{name: 'Bob', age: 35}, {name: 'Alice', age: 20}],
    [{name: 'Tom', age: 40}, {name: 'Jerry', age: 28}]
  ]
];

function filter3LevelArrayBulky(array, condition) {
  let result = [];
  for (let i = 0; i < array.length; i++) {
    const middleArray = array[i];
    let middleResult = [];
    for (let j = 0; j < middleArray.length; j++) {
      const innerArray = middleArray[j];
      let innerResult = [];
      for (let k = 0; k < innerArray.length; k++) {
        if (condition(innerArray[k])) {
          innerResult.push(innerArray[k]);
        }
      }
      if (innerResult.length > 0) {
        middleResult.push(innerResult);
      }
    }
    if (middleResult.length > 0) {
      result.push(middleResult);
    }
  }
  return result;
}

const filtered3LevelBulky = filter3LevelArrayBulky(users3Level, user => user.age > 30);
console.log(filtered3LevelBulky);
// 输出: [[[[{name: 'Bob', age: 35}]], [[{name: 'Tom', age: 40}]]]]

缺点分析:

  • 代码极度臃肿,难以维护
  • 循环嵌套深度随数组维度增加
  • 容易引入边界错误

2.2 ✅ 简洁方式:递归处理

function filterNestedArray(array, predicate) {
  return array.reduce((acc, item) => {
    // 如果是数组,递归处理
    if (Array.isArray(item)) {
      const filteredSubarray = filterNestedArray(item, predicate);
      if (filteredSubarray.length > 0) {
        acc.push(filteredSubarray);
      }
    } 
    // 如果是对象且符合条件,直接保留
    else if (predicate(item)) {
      acc.push(item);
    }
    return acc;
  }, []);
}

const filtered3LevelRecursive = filterNestedArray(users3Level, user => user.age > 30);
console.log(filtered3LevelRecursive);
// 输出: [[[[{name: 'Bob', age: 35}]], [[{name: 'Tom', age: 40}]]]]

优点分析:

  • 代码简洁,适用于任意深度嵌套
  • 逻辑清晰,易于维护
  • 可处理混合型嵌套结构(数组和对象混合)

2.3 ✅ 替代方案:使用 flat 方法先扁平化再过滤

// 先完全扁平化,再过滤,最后尝试恢复结构(适用某些场景)
const fullyFlattened = users3Level.flat(Infinity);
const filteredFlattened = fullyFlattened.filter(user => user.age > 30);
console.log(filteredFlattened);
// 输出: [{name: 'Bob', age: 35}, {name: 'Tom', age: 40}]

适用场景:

  • 不需要保持原始嵌套结构时
  • 只需获取所有符合条件的叶子节点

局限性:

  • 丢失了原始嵌套结构信息
  • 无法区分元素来自哪一层级

三、处理复杂对象结构

当数组中包含复杂对象时,可能需要根据对象属性进行过滤。

3.1 ✅ 简洁方式:递归处理对象数组

function filterNestedObjectArray(array, predicate) {
  return array.reduce((acc, item) => {
    if (Array.isArray(item)) {
      // 如果是数组,递归处理
      const filteredArray = filterNestedObjectArray(item, predicate);
      if (filteredArray.length > 0) {
        acc.push(filteredArray);
      }
    } else if (item !== null && typeof item === 'object') {
      // 如果是对象,检查是否满足条件
      if (predicate(item)) {
        acc.push(item);
      }
      // 递归处理对象属性值
      Object.values(item).forEach(value => {
        if (Array.isArray(value)) {
          const filteredValue = filterNestedObjectArray(value, predicate);
          if (filteredValue.length > 0) {
            acc.push(filteredValue);
          }
        }
      });
    }
    return acc;
  }, []);
}

// 复杂嵌套结构示例
const complexNestedArray = [
  {name: 'John', age: 25, tags: ['active', 'new']},
  [
    {name: 'Jane', age: 30, tags: ['active', 'vip']},
    {name: 'Bob', age: 35, tags: ['inactive', 'vip']}
  ],
  {name: 'Alice', age: 20, tags: ['new']}
];

const filteredComplex = filterNestedObjectArray(complexNestedArray, item => 
  item.age > 25 && item.tags && item.tags.includes('vip')
);
console.log(filteredComplex);
// 输出: [{name: 'Jane', age: 30, tags: ['active', 'vip']}, {name: 'Bob', age: 35, tags: ['inactive', 'vip']}]

四、性能优化建议

处理大型多层嵌套数组时,性能很重要:

  1. 避免不必要的递归:确保递归有明确的终止条件,防止无限递归。
  2. 使用迭代替代深度递归:对于特别深的嵌套结构,考虑使用迭代方式避免栈溢出。
  3. 利用现代JS引擎优化:现代JavaScript引擎对内置方法如 map、filter、reduce 进行了优化。
  4. 考虑使用专用库:对于特别复杂的操作,考虑使用 Lodash 等库的 _.flattenDeep 和 _.filter 方法。

五、总结对比

方法适用场景优点缺点
多重循环简单固定层次结构直观易懂代码臃肿,难以维护
map+filter 链式调用简单到中等复杂度代码简洁,可读性好创建中间数组,性能一般
递归处理任意深度嵌套结构灵活性强,代码简洁深度过大可能栈溢出
先扁平化再过滤不需要保持原始结构非常简单直接丢失嵌套结构信息

选择哪种方式取决于你的具体需求:

  • 对于简单固定层次的数组,使用 map 和 filter 组合。
  • 对于深度嵌套且层次不确定的数组,使用递归方法。
  • 当你只关心数据本身而不需要保持原始结构时,可以先扁平化再过滤。

提示:在实际项目中,对于特别复杂的数据过滤需求,可以考虑使用函数式编程库如 Lodash,它提供了 _.filter、_.flattenDeep 等强大工具,能简化你的代码。

最后更新: 2025/9/2 12:38