JS `flat()` 与 `flatMap()`:扁平化数组与映射后扁平化数组

嘿,各位靓仔靓女们,今天咱们来聊聊JavaScript里两个神奇的“扁平化大师”——flat()flatMap()。 别担心,我保证用最通俗易懂的方式,把它们扒个底朝天,让你以后再也不怕那些嵌套的数组了!

一、flat(): 数组的“夷为平地”神功

想象一下,你手里拿着一个俄罗斯套娃,一层套一层,打开起来贼麻烦。 flat() 函数的作用就类似于把这个套娃给拆了,把所有的小娃娃都放到一个平面上,这样你就可以一次性看到所有的小娃娃了!

1. 语法:

array.flat([depth])
  • array: 不用说,就是你要扁平化的数组。
  • depth: (可选)指定扁平化的深度。 默认值是 1Infinity 表示无限深度,也就是说,不管你的数组嵌套多少层,都能给你一次性扁平化到底!

2. 简单示例:

const arr1 = [1, 2, [3, 4]];
console.log(arr1.flat()); // 输出: [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
console.log(arr2.flat()); // 输出: [1, 2, 3, 4, [5, 6]]  (默认深度为1,只扁平化一层)
console.log(arr2.flat(2)); // 输出: [1, 2, 3, 4, 5, 6]  (指定深度为2,扁平化两层)

const arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
console.log(arr3.flat(Infinity)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] (无限深度,全部扁平化)

3. flat() 的一些小脾气:

  • 跳过空位: flat() 会自动跳过数组中的空位 (empty slots)。

    const arr4 = [1, 2, , 4, 5]; // 注意中间有一个空位
    console.log(arr4.flat()); // 输出: [1, 2, 4, 5]
  • 不改变原数组: flat() 不会修改原始数组,而是返回一个新的扁平化后的数组。

    const arr5 = [1, [2, 3]];
    const flattenedArr = arr5.flat();
    console.log(arr5); // 输出: [1, [2, 3]]  (原数组不变)
    console.log(flattenedArr); // 输出: [1, 2, 3]

4. flat() 的实际应用场景:

  • 处理嵌套的数据结构: 比如从 API 返回的嵌套 JSON 数据,你需要把它们展开成一个平面数组进行处理。
  • 简化复杂的数据操作: 有些算法需要处理扁平化的数据,使用 flat() 可以简化代码逻辑。

二、flatMap(): “映射 + 扁平化” 的组合拳

flatMap() 就像一个武林高手,它不仅会“夷为平地”,还会先给你来一套“乾坤大挪移” (映射),然后再把结果扁平化。 简单来说,它相当于先执行 map(),再执行 flat(1)

1. 语法:

array.flatMap(callback(currentValue[, index[, array]])[, thisArg])
  • callback: 一个函数,用来处理数组中的每一个元素,并返回一个新的值(可以是数组)。
  • currentValue: 当前正在处理的元素的值。
  • index: (可选)当前元素的索引。
  • array: (可选)正在操作的数组。
  • thisArg: (可选)执行 callback 函数时 this 的值。

2. 简单示例:

const arr6 = [1, 2, 3];
const result1 = arr6.flatMap(x => [x * 2]);
console.log(result1); // 输出: [2, 4, 6]  (相当于先 map 成 [[2], [4], [6]],再 flat(1) 成 [2, 4, 6])

const arr7 = ["it's Sunny in", "", "California"];
const result2 = arr7.flatMap(x => x.split(" "));
console.log(result2); // 输出: ["it's", "Sunny", "in", "", "California"] (先 split 成数组,再扁平化)

3. flatMap() 的一些特点:

  • 始终只扁平化一层: 无论 callback 函数返回的数组嵌套多少层,flatMap() 都只会扁平化一层。

    const arr8 = [1, 2, 3];
    const result3 = arr8.flatMap(x => [[x * 2]]); // callback 返回嵌套数组
    console.log(result3); // 输出: [[2], [4], [6]] (只扁平化一层)
  • 更简洁: 相比于先 map()flat()flatMap() 可以用一行代码完成相同的操作,代码更简洁易读。

4. flatMap() 的实际应用场景:

  • 处理一对多的关系: 比如一个用户有多个订单,你可以用 flatMap() 把所有用户的订单提取到一个数组中。
  • 过滤和转换数据: 你可以用 flatMap() 同时过滤掉一些元素,并对剩下的元素进行转换。

    const sentences = ["This is a sentence.", "Another one!"];
    const words = sentences.flatMap(sentence => sentence.split(" "))
                          .filter(word => word.length > 0); // 过滤掉空字符串
    
    console.log(words); // 输出: ["This", "is", "a", "sentence.", "Another", "one!"]

三、flat() vs flatMap(): 差异对比

为了让你更清楚地了解这两个函数的区别,我们用一个表格来总结一下:

特性 flat() flatMap()
功能 将嵌套的数组扁平化。 先对数组中的每个元素执行 callback 函数(映射),然后将结果扁平化。
扁平化深度 可以指定扁平化的深度,默认为 1,可以设置为 Infinity 表示无限深度。 始终只扁平化一层。
是否需要 callback 不需要。 需要,用于处理数组中的每个元素。
相当于 无。 相当于 array.map(callback).flat(1)
应用场景 处理嵌套的数据结构,简化复杂的数据操作。 处理一对多的关系,过滤和转换数据。
示例 [1, [2, 3]].flat() => [1, 2, 3] [1, 2, 3].flatMap(x => [x * 2]) => [2, 4, 6]

四、 兼容性问题

虽然 flat()flatMap() 是 ES2019 (ES10) 引入的特性,但现在主流的浏览器都已经支持了它们。 不过,为了兼容一些老旧的浏览器,你可能需要使用 polyfill。 你可以使用 Babel 或者 Core-js 来添加 polyfill,这样即使在不支持 flat()flatMap() 的浏览器上,你的代码也能正常运行。

五、 自己动手实现 flat()flatMap()

为了更深入地理解 flat()flatMap() 的原理,我们可以尝试自己动手实现它们。

1. 实现 flat():

function myFlat(arr, depth = 1) {
  const result = [];

  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];

    if (Array.isArray(item) && depth > 0) {
      // 如果是数组,并且深度大于0,则递归调用 myFlat()
      result.push(...myFlat(item, depth - 1));
    } else {
      // 否则,直接将元素添加到结果数组中
      result.push(item);
    }
  }

  return result;
}

// 测试
const arr9 = [1, 2, [3, 4, [5, 6]]];
console.log(myFlat(arr9)); // 输出: [1, 2, 3, 4, [5, 6]]
console.log(myFlat(arr9, 2)); // 输出: [1, 2, 3, 4, 5, 6]
console.log(myFlat(arr9, Infinity)); // 输出: [1, 2, 3, 4, 5, 6]

2. 实现 flatMap():

function myFlatMap(arr, callback) {
  const result = [];

  for (let i = 0; i < arr.length; i++) {
    const mappedValue = callback(arr[i], i, arr); // 执行 callback 函数
    if (Array.isArray(mappedValue)) {
      // 如果 callback 返回的是数组,则将数组中的元素添加到结果数组中
      for (let j = 0; j < mappedValue.length; j++) {
        result.push(mappedValue[j]);
      }
    } else {
      // 否则,直接将 callback 返回的值添加到结果数组中
      result.push(mappedValue);
    }
  }

  return result;
}

// 测试
const arr10 = [1, 2, 3];
const result4 = myFlatMap(arr10, x => [x * 2]);
console.log(result4); // 输出: [2, 4, 6]

const arr11 = ["it's Sunny in", "", "California"];
const result5 = myFlatMap(arr11, x => x.split(" "));
console.log(result5); // 输出: ["it's", "Sunny", "in", "", "California"]

六、 总结

flat()flatMap() 是 JavaScript 中非常实用的数组方法,可以帮助我们更方便地处理嵌套的数据结构。 flat() 专注于扁平化数组,而 flatMap() 则将映射和扁平化组合在一起,提供更强大的功能。 掌握了这两个函数,你就能够轻松应对各种复杂的数组操作了!

希望今天的讲座对你有所帮助。 记住,熟能生巧,多写代码,多实践,你也能成为数组处理的大师! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注