likes
comments
collection
share

数组扁平化的几种方法

作者站长头像
站长
· 阅读数 26

在数据处理领域,面对多维度数组的挑战,数组扁平化技术显得尤为重要,它也是面试中常常被问到的点,它能够将多层次的嵌套数组简化为单一维度的线性结构,便于进一步的数据操作和分析。

1. flat() 方法

自ES2019起引入的flat()方法,是实现数组扁平化的直接而高效的途径。此方法能够直接将嵌套数组展平为一维数组,其使用简洁明了。

代码示例:

const arr = [1, [2, [3, 4]]];
const flattened = arr.flat(Infinity);
console.log(flattened); // 输出: [1, 2, 3, 4]
  • 优点: 使用简便,语义清晰,内置支持确保兼容性。
  • 缺点: 在处理极大规模或深度极深的数组时,性能可能略逊于定制解决方案。
  • 适用场景: 适用于大多数现代Web开发和Node.js环境,尤其适合处理中等规模或深度有限的数组扁平化需求。

2. 递归方法

递归是解决数组扁平化的经典方案,通过自调用来逐层解析嵌套数组。

直接递归示例:

function flattenArray(arr) {
  let result = [];
  arr.forEach(item => {
    if (Array.isArray(item)) {
      result = result.concat(flattenArray(item));
    } else {
      result.push(item);
    }
  });
  return result;
}

解释:

  1. 函数定义:function flattenArray(arr) 定义了flattenArray函数,它接受一个参数arr,这个参数应该是一个数组,可能包含数字、字符串或其他数据类型,也包括子数组。

  2. 初始化结果数组:let result = []; 创建一个空数组result,这个数组将用于存放扁平化后的结果。

  3. 遍历输入数组:arr.forEach(item => { ... }) 使用forEach方法遍历输入的数组arr中的每一项,并将当前项赋值给变量item

  4. 判断当前项是否为数组:

    • 如果item是数组(Array.isArray(item)返回true),则递归调用flattenArray(item)来展平这个子数组,并使用concat方法将其结果连接到result数组上。这样可以处理任意深度的嵌套数组。
    • 如果item不是数组,说明它是一个基本类型的元素(如数字、字符串等),直接使用result.push(item);将其添加到结果数组result中。
  5. 返回结果:return result; 在遍历完输入数组的所有元素并处理完毕后,返回最终的扁平化后的数组result

  • 优点: 通用性强,能处理任意深度的嵌套。
  • 缺点: 深度过大时可能导致栈溢出,且频繁的数组拼接可能影响性能。
  • 适用场景: 当需要兼容所有类型和深度的数组,且对性能要求不极端时。

3. reduce()与递归结合

通过将reduce()方法与递归思想结合,可以在保持代码简洁的同时,实现数组的扁平化。

示例:

function flattenWithReduce(arr) {
  return arr.reduce((acc, val) => 
    Array.isArray(val) ? acc.concat(flattenWithReduce(val)) : acc.concat(val), []
  );
}

解释:

  1. 函数定义:function flattenWithReduce(arr) 定义了该函数,接收一个参数arr,表示需要被扁平化的多维数组。

  2. reduce方法应用:return arr.reduce(...) 对输入数组arr应用reduce方法。reduce方法会对数组中的每个元素执行一个由您提供的 reducer 函数(在这里是一个箭头函数),最终结果是将数组简化为单个输出值。这里的输出值就是我们想要的扁平化后的数组。

  3. Reducer 函数:(acc, val) => ... 是 reducer 函数,它有两个参数:

    • acc(accumulator 累加器):在每次迭代中累加的结果,初始化为空数组[](作为reduce方法的第二个参数给出)。
    • val(currentValue 当前值):当前正在处理的数组元素。
  4. 条件判断:Array.isArray(val) ? ... : ... 检查当前元素val是否为数组。

    • 如果val是数组,执行acc.concat(flattenWithReduce(val))

      • 递归调用flattenWithReduce(val)来扁平化这个子数组。
      • 使用concat方法将扁平化后的子数组连接到累加器acc上,保持结果数组的累积。
    • 如果val不是数组(即是一个基本类型的值),执行acc.concat(val)

      • 直接将该值通过concat方法添加到累加器acc中。
  5. 返回结果:经过reduce方法的处理,最终返回的是一个扁平化后的数组

  • 优点: 代码更为精炼。
  • 缺点: 同样面临递归性能问题。
  • 适用场景: 强调代码优雅和可读性的项目,且数组规模适中。

4.直接解构法

直接解构法通过循环和解构赋值巧妙地逐层提取数组元素,直至所有层次都被展平。

示例代码:

function flattenByDestructuring(arr) {
  let result = [];
  while (arr.length) {
    const [first, ...rest] = arr;
    if (Array.isArray(first)) {
      arr = [...first, ...rest];
    } else {
      result.push(first);
      arr = rest;
    }
  }
  return result;
}
  • 优点: 代码直观,避免了复杂的递归调用。
  • 缺点: 在处理深度极大的数组时,循环次数较多,可能影响性能。
  • 适用场景: 面对结构较为复杂但规模适中的数组,追求代码可读性的场合。

5. 解构赋值法

利用解构赋值与循环,可以逐步分解数组,直至完全展平。

示例:

function flattenUsingDestructuring(arr) {
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}
  • 优点: 提高代码可读性,减少递归带来的风险。
  • 缺点: 对于极其庞大的数组,性能表现可能一般。
  • 适用场景: 需要保持代码简洁易懂,且数组结构相对复杂但规模可控的场景。

6. split()toString()组合

这种方法通过将数组转换为字符串,然后分割字符串来间接达到扁平化的目的。

示例代码:

const arr = [1, [2, [3, 4]], 5];  
const flattened = arr.toString().split(',').map(Number);  
console.log(flattened); // 输出: [1, 2, 3, 4, 5]
  • 优点: 实现简单,代码量少。
  • 缺点: 假设所有元素可被顺利转换为字符串且无特殊字符,且需要额外转换类型。
  • 适用场景: 对于数组元素类型单一、无特殊字符,并且对性能要求不高的简单场景。

7. 正则表达式与JSON转换

这种方法利用JSON的序列化和反序列化特性,结合正则表达式去除数组符号,实现扁平化。

示例代码:

function flattenWithRegexAndJSON(arr) {
  const str = JSON.stringify(arr);
  const regex = /[|]/g;
  const flattenedStr = str.replace(regex, '');
  return JSON.parse(`[${flattenedStr}]`);
}

解释:

  1. 函数定义:function flattenWithRegexAndJSON(arr) 定义了该函数,接受一个多维数组arr作为参数。
  2. 字符串化数组:const str = JSON.stringify(arr); 使用JSON.stringify()方法将输入的多维数组转换成JSON格式的字符串。在JSON字符串中,数组元素由逗号分隔,而嵌套数组则通过额外的方括号表示。
  3. 正则表达式准备:const regex = /[|]/g; 定义了一个正则表达式对象,用于匹配特定字符。
  4. 替换操作:const flattenedStr = str.replace(regex, ''); 应用正则表达式regex来查找并替换字符串中的特定字符(理论上应为方括号等表示嵌套结构的字符,但这里用'|'示意),将其替换为空字符串,试图以此达到“扁平化”的效果。但请注意,由于正则表达式的错误选择,此步骤在实际应用中无法正确工作。
  5. 反序列化并返回:return JSON.parse([${flattenedStr}]); 将经过替换操作的字符串flattenedStr前后加上方括号,构成一个新的JSON字符串,然后使用JSON.parse()将其解析回JavaScript数组。这一步假设了替换后的字符串能正确表示一个扁平化的数组结构,但在实际情况下,特别是由于第3步的正则表达式设置不当,这种方法很可能无法正确处理所有类型的嵌套数组。
  • 优点: 创新思路,适用于某些特定的场景。
  • 缺点: 性能较差,且处理过程较为复杂,不直接直观。
  • 适用场景: 特殊情况下作为一种思路展示,但通常不推荐作为常规手段使用。

结论

选择最合适的数组扁平化策略,需根据实际应用场景、数据规模以及对性能的要求综合考虑。对于大多数日常开发,flat()方法因其实现简单、效率较高,成为首选。然而,在特定环境下,如需兼容旧浏览器或处理特殊结构的数组时,采用递归、reduce()结合或其他创新方法可能更为合适。掌握多种技巧,灵活应用,方能高效应对数据处理中的多样挑战。

转载自:https://juejin.cn/post/7371384453359190031
评论
请登录