基于node編寫excel讀取和導出的小工具
背景
領(lǐng)導給同事安排了一個體力活,根據(jù)表1(7k+條)篩選表2(4W+條),然后合并兩個表相同的數(shù)據(jù)。聽起來是挺簡單的需求,加個外表篩選就完事了么,基礎(chǔ)的excel操作他還是會的,但是仔細研究了下,發(fā)現(xiàn)行不通,F(xiàn)ILTER和MATCH都會有一定的局限。并且最后的和并新表也是個困難。于是來求助,看能不能寫個小腳本,跑一下解決問題。 我了解了下需求,覺得可行,于是便有了這個腳本
需求描述
表1格式
姓名 | 年齡 | 住址 |
---|---|---|
張三 | 20 | 上海外灘18號 |
李四 | 21 | 上海浦東新區(qū) |
王五 | 22 | 御青路1號 |
。。。 | 。。。 | 。。。 |
表2格式
昵稱 | 注冊時間 | 住址 |
---|---|---|
二蛋 | 2021 | 18號 |
雙下巴大圓臉 | 2015 | 上海 |
狗子 | 2022 | 御青路 |
。。。 | 。。。 | 。。。 |
他的需求,就是根據(jù)表2的住址模糊篩選出表格1住址列所有匹配的數(shù)據(jù)。然后合并信息。
介紹結(jié)束、開始碼代碼
1、 node讀取excel并解析數(shù)據(jù)
我這里是用了node的xlsx
// 安裝xlsx npm install --save xlsx
xlsx本身提供了一個讀取文件方法,就不需要用fs的讀取了,核心代碼如下
const workbook = xlsx.readFile(filePath); // 我這里只需要第一個,對于多個sheet的情況,這里加個循環(huán)就好了 const sheetName = workbook.SheetNames[0]; const sheet = workbook.Sheets[sheetName]; // 將表內(nèi)容轉(zhuǎn)換為 JSON 數(shù)據(jù) const data = xlsx.utils.sheet_to_json(sheet);
這里推薦使用vscode的調(diào)試模式,可以很方便的查看變量的實際值,不用console一遍一遍查看
2、 過濾數(shù)據(jù),并合并需要的數(shù)據(jù)
拿到表1和表2的數(shù)據(jù)后,就是過濾數(shù)據(jù)的邏輯了
// 過濾文件 const processingData = ({filterFileData,resourceFileData}) => { // 創(chuàng)建一個包含所有 filterFileData.地址 的正則表達式 const areaPattern = new RegExp(filterFileData.map(item => item['地址']).join('|')); // 記錄匹配結(jié)果 const matches = resourceFileData.reduce((acc, item2) => { const match = item2['地址'].match(areaPattern); if (match) { // 找到匹配的 arr1 項 const matchedItem1 = filterFileData.find(item1 => item1['地址'] === match[0]); acc.push({ filterFileData: matchedItem1, resourceFileData: item2 }); } return acc; }, []); return matches }
對于這種大數(shù)據(jù)過濾,盡可能的減少循環(huán)套循環(huán),我這里是直接把表2住址拼接了一個正則,用match去校驗表1的地址列,是否包含表2字符串,有更好的方案,可以在評論區(qū)打出來交流下
3、 導出excel
在第二步,我把匹配成功的表1和表2數(shù)據(jù)都存在了一個數(shù)組,然后再第三步使用,這里就是拼數(shù)據(jù)和寫數(shù)據(jù)了
// 生成篩選后的excel const writeExcel = ({writeData,resourceFileName}) => { const data = writeData.map((item, index) => { const {filterFileData,resourceFileData} = item return { '序號': index + 1, '所屬區(qū)縣': filterFileData['所屬區(qū)縣'], '戶號': resourceFileData['戶號'], '戶名': resourceFileData['戶名'], '戶號地址': resourceFileData['地址'], '機構(gòu)全稱': filterFileData['機構(gòu)全稱'], '機構(gòu)類型': filterFileData['機構(gòu)類型'], '機構(gòu)地址': filterFileData['地址'], } }) // 將數(shù)據(jù)轉(zhuǎn)換為 worksheet const worksheet = xlsx.utils.json_to_sheet(data); // 創(chuàng)建一個新的 workbook,并附加 worksheet const workbook = xlsx.utils.book_new(); xlsx.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); let outputFileName = `${resourceFileName.split('.')[0]}-篩選結(jié)果.xlsx` let outputFilePath = path.join(outputFileDirPath,`./${outputFileName}`) // 保存 Excel 文件 xlsx.writeFile(workbook, outputFilePath); console.log(`導出文件成功,地址在:${outputFilePath}`) }
完整代碼
// 1、讀取filterFile和resourceFile目錄下的文件 // 2、過濾源數(shù)據(jù) // 3、將過濾好的數(shù)據(jù),寫入一個excel const fs = require("fs"); const xlsx = require("xlsx"); const path = require("path"); const resourceFileDirPath = path.join(__dirname, "./resourceFile"); const filterFileName = "filter.xlsx"; const filterFileDirPath = path.join(__dirname, `./filterFile/${filterFileName}`); const outputFileDirPath = path.join(__dirname, `./outputFile`); // 讀取excel文件 const readFile = (filePath) => { return new Promise((resolve, reject) => { const workbook = xlsx.readFile(filePath); // 暫時只解析第一個sheet const sheetName = workbook.SheetNames[0]; const sheet = workbook.Sheets[sheetName]; // 將表內(nèi)容轉(zhuǎn)換為 JSON 數(shù)據(jù) const data = xlsx.utils.sheet_to_json(sheet); resolve(data); }); }; // 過濾文件 const processingData = ({filterFileData,resourceFileData}) => { // 創(chuàng)建一個包含所有 filterFileData.地址 的正則表達式 const areaPattern = new RegExp(filterFileData.map(item => item['地址']).join('|')); // 記錄匹配結(jié)果 const matches = resourceFileData.reduce((acc, item2) => { const match = item2['地址'].match(areaPattern); if (match) { // 找到匹配的 arr1 項 const matchedItem1 = filterFileData.find(item1 => item1['地址'] === match[0]); acc.push({ filterFileData: matchedItem1, resourceFileData: item2 }); } return acc; }, []); return matches } // 生成篩選后的excel const writeExcel = ({writeData,resourceFileName}) => { const data = writeData.map((item, index) => { const {filterFileData,resourceFileData} = item return { '序號': index + 1, '所屬區(qū)縣': filterFileData['所屬區(qū)縣'], '戶號': resourceFileData['戶號'], '戶名': resourceFileData['戶名'], '戶號地址': resourceFileData['地址'], '機構(gòu)全稱': filterFileData['機構(gòu)全稱'], '機構(gòu)類型': filterFileData['機構(gòu)類型'], '機構(gòu)地址': filterFileData['地址'], } }) // 將數(shù)據(jù)轉(zhuǎn)換為 worksheet const worksheet = xlsx.utils.json_to_sheet(data); // 創(chuàng)建一個新的 workbook,并附加 worksheet const workbook = xlsx.utils.book_new(); xlsx.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); let outputFileName = `${resourceFileName.split('.')[0]}-篩選結(jié)果.xlsx` let outputFilePath = path.join(outputFileDirPath,`./${outputFileName}`) // 保存 Excel 文件 xlsx.writeFile(workbook, outputFilePath); console.log(`導出文件成功,地址在:${outputFilePath}`) } const main = async () => { console.time("總用時"); const files = fs.readdirSync(resourceFileDirPath); let filterFile = await readFile(filterFileDirPath); // 過濾掉沒有地址 和指定關(guān)鍵字的文件 filterFile = filterFile.filter(item => { let area = item['地址'] let blackList = ['0','無', '無地址'] return area && !blackList.includes(area) }) console.log(files); for (const fileName of files) { console.time(`${fileName}用時:`); console.log(`正在讀取文件${fileName}`) // 讀取待篩選數(shù)據(jù) let resourceFile = await readFile( path.resolve(resourceFileDirPath, fileName) ); console.log(`讀取文件成功,正在解析文件`) // 解析文件 const writeData = processingData({ filterFileData: filterFile, resourceFileData: resourceFile, }) console.log(`解析文件成功,正在導出文件`) // 寫入數(shù)據(jù) writeExcel({ writeData, resourceFileName: fileName }) console.timeEnd(`${fileName}用時:`); } console.timeEnd("總用時"); }; // 執(zhí)行 try { main(); } catch (error) { console.log('error',error) }
結(jié)語
感覺沒有合并這個要求的話,excel是不是可以直接篩選出來了?對excel函數(shù)不熟悉,嘗試了幾下沒有實現(xiàn)。
到此這篇關(guān)于基于node編寫excel讀取和導出的小工具的文章就介紹到這了,更多相關(guān)node excel讀取和導出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Node.JS獲取GET,POST數(shù)據(jù)之queryString模塊使用方法詳解
本文將詳細介紹nodeJS中的queryString模塊使用方法,包括Node.JS獲取GET,POST數(shù)據(jù)的方法,需要的朋友可以參考下2020-02-02node.js中express中間件body-parser的介紹與用法詳解
這篇文章主要給大家介紹了關(guān)于node.js中express中間件body-parser的相關(guān)資料,文章通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。2017-05-05nodejs實現(xiàn)的http、https 請求封裝操作示例
這篇文章主要介紹了nodejs實現(xiàn)的http、https 請求封裝操作,結(jié)合實例形式分析了node.js針對http、https 請求的封裝與使用相關(guān)操作技巧,需要的朋友可以參考下2020-02-02