JavaScript Array.prototype.flatMap ():數組 “扁平化 + 映射” 的高效組合拳

在 JavaScript 數組處理中,我們經常需要先對每個元素進行轉換(映射),再將結果 “鋪平”(扁平化)。比如將數組中的每個字符串按空格拆分,然后合并成一個新數組。傳統做法是先用map()轉換,再用flat()扁平化,但這樣會創建中間數組,影響效率。ES2019 引入的flatMap()方法,就像一套 “組合拳”,將映射和扁平化兩步操作合并為一,既簡潔又高效。今天,我們就來解鎖這個提升數組處理效率的實用方法。

一、認識 flatMap ():映射與扁平化的 “二合一” 工具

flatMap()是數組原型上的方法,它的作用可以概括為:先對數組中的每個元素執行映射操作(類似map()),再對結果執行一層扁平化(類似flat(1)。簡單說,flatMap()等價于map() followed by flat(1),但性能更優。

1.1 與 map () + flat () 的對比

// 原始數組
const sentences = ["Hello world", "I love JavaScript", "flatMap is useful"];// 方法1:map() + flat()
const words1 = sentences.map((sentence) => sentence.split(" ")) // 先映射:拆分成子數組.flat(); // 再扁平化:合并子數組// 方法2:flatMap()
const words2 = sentences.flatMap((sentence) => sentence.split(" "));
console.log(words1); // ["Hello", "world", "I", "love", "JavaScript", "flatMap", "is", "useful"]
console.log(words2); // 同上,結果完全一致

兩者結果相同,但flatMap()只遍歷一次數組,且不創建中間數組(map()的結果),因此在處理大型數組時性能更優。

1.2 基礎語法:簡潔的回調函數

flatMap()的語法與map()類似,接收一個回調函數和可選的this指向:

array.flatMap(callback(element[, index[, array]])[, thisArg])
  • callback:對每個元素執行的函數,返回一個數組(或其他可迭代對象),該數組會被扁平化一層。

  • thisArg:執行callback時的this值。

  • 返回值:經過映射和扁平化后的新數組。

示例:將數組元素翻倍并拆分

const numbers = [1, 2, 3];// 映射:每個元素翻倍后放在數組中;扁平化:合并子數組
const result = numbers.flatMap((num) => [num * 2, num * 3]);
console.log(result); // [2, 3, 4, 6, 6, 9]

回調函數返回[num * 2, num * 3]flatMap()會將這些子數組合并成一個數組。

二、核心特性:只扁平化一層的 “精準控制”

flatMap()的扁平化能力是有限的 —— 它只會將映射結果扁平化一層,不會遞歸處理深層嵌套的數組。這一點與flat(depth)不同(flat()可指定深度),也是flatMap()的關鍵特性。

2.1 與 flat () 不同的扁平化深度

const arr = [1, [2, [3]], 4];// flatMap():只扁平化一層
const result1 = arr.flatMap((item) => {// 映射:原樣返回元素(模擬不映射,只看扁平化效果)return item;
});// flat(1):同樣扁平化一層
const result2 = arr.flat(1);// flat(2):扁平化兩層
const result3 = arr.flat(2);console.log(result1); // [1, 2, [3], 4](僅扁平一層)
console.log(result2); // [1, 2, [3], 4](與flatMap結果一致)
console.log(result3); // [1, 2, 3, 4](深層扁平)

可以看到,flatMap()的扁平化效果等價于flat(1),無法處理深層嵌套。如果需要深層扁平化,仍需在flatMap()之后調用flat(depth)

2.2 過濾元素的 “小技巧”

利用flatMap()只扁平化一層的特性,我們可以在映射時返回空數組[],實現 “過濾” 效果(空數組會被扁平化為空,相當于刪除元素):

const numbers = [1, 2, 3, 4, 5, 6];// 保留偶數,過濾奇數(奇數返回空數組)
const evenNumbers = numbers.flatMap((num) => {return num % 2 === 0 ? [num] : [];
});console.log(evenNumbers); // [2, 4, 6]

這比filter() + map()的組合更簡潔(numbers.filter(num => num % 2 === 0).map(num => num)),且只遍歷一次數組。

三、實戰場景:flatMap () 的高效應用

flatMap()在需要同時進行映射和扁平化的場景中表現出色,以下是幾個典型案例:

3.1 拆分字符串并去重

處理包含多個標簽的字符串數組,拆分后去重:

const tagGroups = ["html,css", "javascript,html", "css,react"];// 拆分所有標簽并去重
const uniqueTags = [...new Set(tagGroups.flatMap((group) => group.split(",")))];console.log(uniqueTags); // ["html", "css", "javascript", "react"]
  • flatMap()先將每個字符串按,拆分,再合并成一個標簽數組。

  • Set去重后轉回數組,實現高效處理。

3.2 生成新的對象數組

將用戶數組轉換為 “用戶名 - 郵箱” 對數組:

const users = [{ name: "Alice", emails: ["alice@a.com", "alice.work@a.com"] },{ name: "Bob", emails: ["bob@b.com"] },
];// 生成 [{ name: "Alice", email: "alice@a.com" }, ...]
const userEmails = users.flatMap((user) =>user.emails.map((email) => ({ name: user.name, email: email }))
);console.log(userEmails);/*
[{ name: "Alice", email: "alice@a.com" },{ name: "Alice", email: "alice.work@a.com" },{ name: "Bob", email: "bob@b.com" }
]
*/

flatMap()先對每個用戶映射出包含多個郵箱對象的數組,再合并成一個數組,避免了map()后額外的flat()操作。

3.3 處理樹形結構數據

將嵌套的評論數據 “鋪平” 為一維數組:

const comments = [{id: 1,text: "第一條評論",replies: [{ id: 11, text: "回復1" },{ id: 12, text: "回復2" },],},{id: 2,text: "第二條評論",replies: [],},
];// 提取所有評論(包括回復)的id和text
const allComments = comments.flatMap((comment) => [// 先包含當前評論{ id: comment.id, text: comment.text },// 再包含所有回復(會被扁平化)...comment.replies.map((reply) => ({ id: reply.id, text: reply.text })),
]);console.log(allComments);/*
[{ id: 1, text: "第一條評論" },{ id: 11, text: "回復1" },{ id: 12, text: "回復2" },{ id: 2, text: "第二條評論" }
]
*/

通過flatMap()將嵌套的回復與主評論合并,一步完成映射和扁平化。

3.4 數據轉換與過濾結合

處理訂單數據,提取有效商品并轉換格式:

const orders = [{ id: 1, products: ["apple", "banana"], valid: true },{ id: 2, products: ["orange"], valid: false }, // 無效訂單{ id: 3, products: ["grape", "mango"], valid: true },
];// 提取有效訂單的商品,格式化為 { orderId, product }
const validProducts = orders.flatMap((order) => {// 過濾無效訂單(返回空數組)if (!order.valid) return [];// 映射有效商品return order.products.map((product) => ({orderId: order.id,product: product,}));
});console.log(validProducts);/*
[{ orderId: 1, product: "apple" },{ orderId: 1, product: "banana" },{ orderId: 3, product: "grape" },{ orderId: 3, product: "mango" }
]
*/

一次遍歷完成過濾和轉換,效率高于filter() + map() + flat()的組合。

四、避坑指南:使用 flatMap () 的注意事項

4.1 不要期望深層扁平化

flatMap()只能扁平化一層,對于深層嵌套的數組,需要額外處理:

const deepArray = [1, [2, [3, [4]]]];// 錯誤:flatMap()無法深層扁平化
const result1 = deepArray.flatMap((item) => item);
console.log(result1); // [1, 2, [3, [4]]](僅扁平一層)// 正確:先flatMap()再flat()
const result2 = deepArray.flatMap((item) => item).flat(2);
console.log(result2); // [1, 2, 3, 4]

4.2 回調函數需返回可迭代對象

flatMap()的回調函數應返回數組或其他可迭代對象(如字符串、Set等),否則會將返回值包裝成數組再扁平化:

const numbers = [1, 2, 3];// 回調返回非數組(數字)
const result = numbers.flatMap((num) => num * 2);
console.log(result); // [2, 4, 6]// 等價于:[ [2], [4], [6] ].flat() → [2,4,6]

雖然返回非數組也能工作,但建議始終返回數組,明確意圖。

4.3 性能考量:大型數組的處理

flatMap()map() + flat()高效,但在處理超大型數組時,仍需注意:

  • 避免在回調函數中執行復雜操作(會增加單次迭代耗時)。

  • 如需多次處理,考慮分批次處理(避免阻塞主線程)。

4.4 瀏覽器兼容性

flatMap()兼容所有現代瀏覽器(Chrome 69+、Firefox 62+、Safari 12+、Edge 79+),但 IE 完全不支持。如需兼容舊瀏覽器,可使用 Polyfill:

// flatMap()的簡易Polyfill
if (!Array.prototype.flatMap) {Array.prototype.flatMap = function (callback, thisArg) {return this.map(callback, thisArg).flat(1);};
}

五、總結

flatMap()作為map()flat(1)的組合,雖然功能看似簡單,卻在數組處理中有著不可替代的價值:

  • 簡潔高效:一行代碼完成映射和扁平化,減少中間數組創建。

  • 靈活多用:既能轉換數據,又能過濾元素,還能處理嵌套結構。

  • 性能更優:比map() + flat()少一次遍歷,適合大型數組。

在實際開發中,當你需要對數組元素進行轉換并希望結果是一維數組時,flatMap()往往是最佳選擇。無論是處理字符串拆分、對象轉換,還是嵌套數據鋪平,它都能讓代碼更簡潔、高效。

記住:好的工具能讓復雜問題變簡單,flatMap()就是這樣一個 “四兩撥千斤” 的數組方法。下次處理數組時,不妨想想:這個場景能用flatMap()嗎?

你在項目中用過flatMap()解決什么問題?歡迎在評論區分享你的經驗~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95550.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95550.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95550.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

區塊鏈與元宇宙:數字資產的守護者

1 區塊鏈支撐元宇宙數字資產的底層邏輯1.1 不可篡改性構建信任基石區塊鏈的不可篡改性為元宇宙數字資產提供了堅實的信任基礎。其核心在于分布式賬本技術,當一筆數字資產交易發生時,會被打包成區塊并廣播至網絡中的所有節點。每個節點都會對這筆交易進行…

Linux軟件編程:進程和線程(進程)

進程一、基本概念進程:是程序動態執行過程,包括創建、調度、消亡程序:存放在外存的一段數據的集合二、進程創建(一)進程空間分布每個進程運行起來后,操作系統開辟0-4G的虛擬空間進程空間:用戶空…

Mybatis學習筆記(五)

分頁插件與性能優化 分頁插件配置 簡要描述:MybatisPlus分頁插件是基于物理分頁實現的高性能分頁解決方案,支持多種數據庫的分頁語法,能夠自動識別數據庫類型并生成對應的分頁SQL。 核心概念: 物理分頁:直接在SQL層面進…

企業可商用的conda:「Miniforge」+「conda-forge」

文章目錄一、徹底卸載現有 Anaconda/Miniconda二、安裝 Miniforge(推薦)macOS/Linux檢查Windows檢查三、將通道固定為 conda-forge(嚴格優先)四、驗證是否仍引用 Anaconda 源五、常見問題(FAQ)六、參考命令…

Flutter ExpansionPanel組件(可收縮的列表)

可以展開或者收縮的面板組件,收縮面板組件效果由ExpansionPanelList組件和ExpansionPanel組件共同完成。 ExpansionPanelList屬性說明屬性說明children子元素expansionCallback設置回調事件ExpansionPanel屬性說明headerBuilder收縮的標題body內容isExpanded設置內容…

C/C++ 進階:深入解析 GCC:從源碼到可執行程序的魔法四步曲

引言距離上一篇博客更新已經過去了大概一兩周的時間,而對于 Linux 系統的基本指令以及 Shell 編程的學習其實基本講解完畢,Linux基礎一塊的知識就將告一段落了,如果有細節性的知識,我也會及時分享給各位,作為一名正在攀…

云服務器運行持續強化學習COOM框架的問題

1 環境要求 下載地址:https://github.com/TTomilin/COOM tensorflow 2.11以上 python 3.9以上 tensorflow2.12.0,需要安裝tensorflow-probability0.19 2 修改代碼 COOM/wrappers/reward.py 將 from gym import RewardWrapper修改為 from gymnasium impor…

MyBatis Interceptor 深度解析與應用實踐

MyBatis Interceptor 深度解析與應用實踐 一、MyBatis Interceptor概述 1.1 什么是MyBatis Interceptor MyBatis Interceptor,也稱為MyBatis 插件,是 MyBatis 提供的一種擴展機制,用于在 MyBatis 執行 SQL 的過程中插入自定義邏輯。它類似…

【自動化測試】Web自動化測試 Selenium

🔥個人主頁: 中草藥 🔥專欄:【Java】登神長階 史詩般的Java成神之路 測試分類 了解各種各樣的測試方法分類,不是為了墨守成規按照既定方法區測試,而是已了解思維為核心,并了解一些專業名詞 根…

2025 電賽 C 題完整通關攻略:從單目標定到 2 cm 測距精度的全流程實戰

摘要 2025 年全國大學生電子設計競賽 C 題要求“僅用一顆固定攝像頭”在 5 s 內完成 100 cm~200 cm 距離、誤差 ≤2 cm 的單目測距,并實時顯示功耗。本文整合國一選手方案、CSDN 高分博文、B 站實測視頻及官方說明,給出從硬件選型→離線標定→在線算法→…

Day 10: Mini-GPT完整手寫實戰 - 從組件組裝到文本生成的端到端實現

Day 10-2: Mini-GPT完整手寫實戰 - 從組件組裝到文本生成的端到端實現 ?? 今日學習目標 掌握GPT架構組裝:將Transformer組件組裝成完整的生成模型 理解生成式預訓練:掌握自回歸語言建模的核心機制 端到端代碼實現:從數據預處理到模型訓練的完整流程 文本生成實戰:訓練Mi…

深入解析Prompt緩存機制:原理、優化與實踐經驗

深入解析Prompt緩存機制:原理、優化與實踐經驗 概述 在大型語言模型應用中,API請求的延遲和成本始終是開發者關注的核心問題。Prompt緩存(Prompt Caching)技術通過智能地復用重復內容,有效減少了API響應時間和運行成本…

CV 醫學影像分類、分割、目標檢測,之【3D肝臟分割】項目拆解

CV 醫學影像分類、分割、目標檢測,之【3D肝臟分割】項目拆解第1行:from posixpath import join第2行:from torch.utils.data import DataLoader第3行:import os第4行:import sys第5行:import random第6行&a…

Mybatis學習筆記(七)

Spring Boot集成 簡要描述:MyBatis-Plus與Spring Boot的深度集成,提供了自動配置、啟動器等特性,大大簡化了配置和使用。 核心概念: 自動配置:基于條件的自動配置機制啟動器:簡化依賴管理的starter配置屬性…

機器人伴侶的智能升級:Deepoc具身智能模型如何重塑成人伴侶體驗

引言:機器人伴侶市場的技術變革需求隨著人工智能技術的飛速發展和人們情感需求的多元化,機器人成人伴侶市場正在經歷前所未有的增長。傳統機器人伴侶已經能夠滿足基礎的交互需求,但在智能化、情感化和個性化方面仍存在明顯不足。這正是深算紀…

metabase基礎使用技巧 (dashboard, filter)

這是metabase系列分享文章的第2部分。本文將介紹metabase的基礎概念和使用介紹 question question是metabase中提供的通過UI化操作就能實現簡單的 快捷 直接的BI查詢。 點擊右側的New -> Question即可創建Question,可以理解為一個格式化的查詢: 這里…

機器人成人伴侶的智能化升級:Deepoc具身模型賦能沉浸式體驗

引言:成人機器人市場的技術革新需求隨著人工智能和機器人技術的快速發展,成人陪伴機器人行業正經歷從簡單機械運動到智能化交互的轉型。據市場研究數據顯示,全球成人機器人市場規模預計將在2026年突破100億美元,年復合增長率保持在…

Go語言企業級權限管理系統設計與實現

最近跟著學長再寫河南師范大學附屬中學圖書館的項目,學長交給了我一個任務,把本項目的權限管理給吃透,然后應用到下一個項目上。 我當然是偷著樂吶,因為讀代碼的時候,總是莫名給我一種公費旅游的感覺。 本來就想去了解…

Java應用快速部署Tomcat指南

將Java應用部署到Apache Tomcat服務器是開發Web應用過程中常見的任務。Tomcat是一個免費且開源的Servlet容器,它為Java應用提供了運行環境。本文將介紹如何準備你的Java應用,并將其部署到Tomcat服務器上。 Java 應用部署 tomcat 的根目錄結構 Tomcat中默認網站根目錄是$CAT…

Java 學習筆記(基礎篇2)

1. 分支結構① if 語句:(1) 雙分支:if (條件) {// 語句體1 } else {// 語句體2 }(2) 多分支if (條件1) {// 語句體1 } else if (條件2) {// 語句體2 } else {// 語句體N }② switch 語句:(1) 語法:如果都不是(default&…