在使用 n8n 建立自動化工作流程時,Code 節點是最靈活且強大的工具之一。它讓我們能用 JavaScript 處理複雜的資料轉換和過濾邏輯。然而,當我嘗試使用 filter() 方法過濾資料時,卻遇到了一個令人困惑的問題:無論如何調整過濾邏輯,所有資料都沒有被移除。
這個問題的根源出在哪裡?答案就藏在 n8n 的資料包裝機制中。
問題根源:n8n 的資料包裝機制
n8n Item 資料結構
當你在 n8n Code 節點中使用 $input.all() 取得資料時,每一個 item 並不是你期望的純資料物件,而是被 n8n 包裝過的特殊結構:
{ "json": { "工作表名稱": [...] // 你的實際資料 }, "index": 0, "pairedItem": { ... }}陷阱分析
看看這段有問題的程式碼:
// ❌ 錯誤寫法const result = data.filter(item => { const itemKey = Object.keys(item)[0]; // 取得的是 "json",不是 "目標工作表" return itemKey !== targetSheet; // 永遠為 true,因為 "json" !== "目標工作表"});為什麼沒有過濾掉任何資料?
讓我們用表格來對比預期與實際的情況:
| 情況 | 預期取得的 Key | 實際取得的 Key | 判斷結果 | 是否保留 |
|---|---|---|---|---|
| 目標 Sheet | "目標工作表" | "json" | "json" !== "目標工作表" → true | ✅ 保留 |
| 其他 Sheet | "其他工作表名稱" | "json" | "json" !== "目標工作表" → true | ✅ 保留 |
因為 Object.keys(item)[0] 抓到的永遠是 "json" 這個包裝層的 key,而不是你真正想要比對的資料 key,所以判斷永遠為 true,導致所有資料都被保留。
解決方案一:修正分離式寫法
如果你想繼續使用 $input.all() 獲取資料,只需要多進入一層 .json 來取得真正的資料 Key。
❌ 錯誤程式碼
// 取得要比對的 Sheet Nameconst targetSheet = $('格式轉換1').first().json.query.sheet_name;
// 解析資料let data = $input.all();
// 使用 filter 過濾const result = data.filter(item => { const itemKey = Object.keys(item)[0]; // ❌ 抓到 "json" return itemKey !== targetSheet;});
return result;✅ 正確程式碼
// 1. 取得要比對的 Sheet Nameconst targetSheet = $('格式轉換1').first().json.query.sheet_name;
// 2. 解析資料let data = $input.all();
// 3. 使用 filter 過濾const result = data.filter(item => { // 檢查 item.json 是否有內容,避免空資料報錯 if (!item.json) return false;
// ✅ 關鍵修正:要從 item.json 裡面抓 Key const itemKey = Object.keys(item.json)[0];
// 邏輯:保留 Key 不等於 targetSheet 的項目 return itemKey !== targetSheet;});
return result;關鍵修正點將
Object.keys(item)[0]改為Object.keys(item.json)[0],這樣才能取得真正的資料 key(例如"工作表名稱"),而不是包裝層的 key("json")。
解決方案二:合併式處理(JSON.parse + filter)
如果你的資料來源是字串格式(例如從其他節點傳遞過來的 JSON 字串),可以先用 JSON.parse() 解析,再進行過濾。這種方式的優勢是資料結構更簡潔,不需要處理 n8n 的包裝層。
資料結構的關鍵差異
當你使用 JSON.parse 解析字串時,得到的是純 JavaScript 物件陣列,沒有 n8n 自動加上的 .json 外殼:
| 方法 | 資料結構 | 取得 Key 的方式 |
|---|---|---|
$input.all() | { json: { "工作表名稱": [...] }, index: 0, ... } | Object.keys(item.json)[0] |
JSON.parse() | { "工作表名稱": [...] } | Object.keys(item)[0] |
✅ 完整程式碼
// 1. 取得要比對的 Sheet Nameconst targetSheet = $('格式轉換1').first().json.query.sheet_name;
// 2. 解析資料// 取得輸入的字串並轉成 JavaScript 陣列let rawString = $input.first().json.data;let parsedData = JSON.parse(rawString);
// 3. 使用 filter 過濾// 注意:parsedData 是純 JS 陣列,沒有 n8n 的 .json 外殼,所以直接操作 itemconst result = parsedData.filter(item => { // 取得該項目的 Key (例如 "工作表名稱") const itemKey = Object.keys(item)[0];
// 邏輯:保留 Key 不等於 targetSheet 的項目 return itemKey !== targetSheet;});
// 4. 回傳結果return result;為什麼程式碼不一樣?
- 分開寫時(使用
$input.all()):資料是 n8n 的 Item 格式,結構是{ json: { "工作表名稱": ... } },所以必須寫item.json。- 合併寫時(使用
JSON.parse):資料是你剛解析出來的純變數,結構直接是{ "工作表名稱": ... },所以直接用item即可。
兩種方案的適用場景
| 方案 | 適用情境 | 優點 | 缺點 |
|---|---|---|---|
| 方案一 修正 $input.all() | 直接處理 n8n 節點間傳遞的資料 | 原生支援,無需額外解析 | 需要理解 n8n 包裝層 |
方案二JSON.parse + filter | 資料來源是 JSON 字串 需要更靈活的資料處理 | 資料結構簡潔,易於理解 | 需要額外解析步驟 |
進階技巧:處理空陣列輸出
在 n8n Code 節點中,如果你需要回傳一個空陣列的 JSON 檔案(例如當沒有資料需要處理時),不能直接回傳 [],而是需要遵循 n8n 的回傳格式:
❌ 錯誤寫法
return []; // n8n 會將這視為沒有輸出✅ 正確寫法
return [ { json: { text: '[]', // 檔案內容就是這兩個字元 }, },];為什麼要這樣寫?n8n 要求所有輸出都必須是 Item 陣列格式,即使是空資料也需要包裝成
{ json: {...} }的結構。如果直接回傳[],n8n 會認為沒有任何輸出,後續節點可能無法正常執行。
總結與最佳實踐
關鍵概念回顧
- n8n Item 結構:每個 item 都被包裝成
{ json: {...}, index: 0, pairedItem: {...} }格式 - 取得資料 Key 的正確方式:
- 使用
$input.all():Object.keys(item.json)[0] - 使用
JSON.parse():Object.keys(item)[0]
- 使用
- 空陣列輸出:必須回傳
[{ json: { text: '[]' } }]格式
何時使用哪種方案?
graph TD A[需要過濾 n8n 資料] --> B{資料來源是?} B -->|n8n 節點輸出| C[使用 $input.all<br/>取得 item.json] B -->|JSON 字串| D[使用 JSON.parse<br/>直接操作物件] C --> E[過濾完成] D --> E E --> F{有資料輸出?} F -->|是| G[正常回傳 result] F -->|否| H[回傳空陣列格式<br/>{json: {text: '[]'}}]n8n Code 節點開發建議
- 總是先確認資料結構:用
console.log(JSON.stringify(item, null, 2))檢查資料層級 - 善用空值檢查:在存取
item.json前先確認它存在 - 理解包裝機制:記住 n8n 的資料永遠有一層
json外殼 - 遵循回傳格式:所有輸出都應該是
[{ json: {...} }]陣列格式
希望這篇文章能幫助你避開 n8n Code 節點的資料結構陷阱!如果你在使用 n8n 時遇到其他問題,歡迎交流。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。