【Node.js】写一个数据自动整理成表格的脚本

news2025/2/24 21:07:00

前言

企业项目进行数据埋点后,埋点事件名需要整理成Excel表格便于统计,目标是将下图左侧数据转化成下图右侧的Excel表格:

考虑到左侧埋点数据是随项目迭代增加的,埋点数据每增加一次我就要把数据一条一条的Ctrl+C/V复制粘贴至Excel表格内。

懒,不想这样玩,于是我写了一个自动帮我整理成表格的脚本。

脚本实现

实现流程

  • Node.js生成Excel表格工具库技术选型
  • 单独复制一份埋点数据出来,保证它的变动不会影响业务相关埋点逻辑
  • 整理埋点数据成我们需要的数据结构

分成三步走

技术选型

Node.js操作Excel表格工具库有:

  • exceljs
  • excellentexport
  • node-xlsx
  • xlsx-template

仅罗列以上四个。

选择的角度有以下几点:

  • 学习成本低,文档API简单易用,仅生成表格即可,其他功能并不需要,所以API越简单越好
  • 生成Excel表格需要提供的数据结构简单,便于实现
  • 能导出xlsx表格,满足最基本要求

node-xlsx最贴近以上要求,首选使用它。

node-xlsx官方生成Excel表格给出的代码块:

import xlsx from 'node-xlsx';
// Or var xlsx = require('node-xlsx').default;

const data = [
  [1, 2, 3],
  [true, false, null, 'sheetjs'],
  ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'],
  ['baz', null, 'qux'],
];
var buffer = xlsx.build([{name: 'mySheetName', data: data}]); // Returns a buffer

生成表格数据data是二维数组,对应表格的行列。data.length 为表格行数,data[0].length 为表格的列数;data[0][0]对应至表格的第一行第一列的值,data[0][1]对应至表格的第一行第二列的值。

所以将埋点数据整理为一个二维数组即可,二维数组数据结构整理容易实现。

复制埋点数据

埋点数据统一放置在buryData.js 文件,但不能随意改动它,所以将该文件单独再复制一份出来。

buryData.js

export default {
    version1: 'v1.5.3',
    bury1: 'ding提醒',
    bury2: '审批-筛选',
    bury3: '任务-点击任务标题打开任务详情',
    bury4: '任务详情弹框-点击详情tab',
    bury5: '任务详情弹框-点击日志记录tab',
    bury6: '任务详情弹框-点击工作总结tab',
    bury7: '任务详情弹框-点击动态tab',
    //...
}

buryData.js复制出来文件命名为bury.js,还有一个问题:bury.js需要执行它,拿到它导出的数据对象,导出数据是使用ES6模块化语法,这边需要将ES6模块化转化成CommonJs模块化,将export default {} 替换成module.exports ={}即可做到。

Node.js fs模块+正则替换是可以达成以上目的,但为了更快捷,我选择使用工具库magic-string

magic-string它是操作字符串库,它可以帮我去掉写正则替换字符串的步骤。

const path = require('path');
const magicString = require('magic-string')
const fs = require('fs');

//buryData.js 文件路径
const buryFile = path.join(__dirname, '../src/lib/buryData.js')

const getBuryContent = (filePath) => {
    const content = fs.readFileSync(filePath, 'utf8')
    //将export default 替换成module.exports =
    const s = new magicString(content)
    s.replace('export default', 'module.exports = ')
    return s.toString()
}

(async () => {
    const str = getBuryContent(buryFile)
    //将替换后的内容写入至bury.js文件
    const copyFilePath = path.join(__dirname, '/bury.js')
    fs.writeFileSync(copyFilePath, str)
    //动态导入bury.js 获取埋点数据
    const { default: data } = await import(copyFilePath)
})()

生成二维数组

上文已提及,node-xlsx生成表格需要先将数据整理成二维数组。

export default {
    version1: 'v1.5.3',
    bury1: 'ding提醒',
    /...
    version2: 'v1.5.4',
    bury21: '通讯录人员列表',
    //..
}

以上数据整理成:

[
  ['v1.5.3','v1.5.4'],
  ['ding提醒','通讯录人员列表'],
  //...
]

首先,将数据全部存放至一个Map对象中。因为埋点数据是一个对象,其中version1、version2 表示版本号,随项目迭代版本号会增多version3、version4……以version进行划分Map 值。

const _ = require('lodash');
//...

const getFormatDataMap = (data) => {
    let version
    const map = new Map();
    _.forIn(data, (value, key) => {
        if (key.includes('version')) {
            version = value
            !map.has(version) && map.set(version, [value])
            return
        }
        const mapValue = map.get(version)
        mapValue.push(value)
    })
    return map
}

(async () => {
    const str = getBuryContent(buryFile)
    const copyFilePath = path.join(__dirname, '/bury.js')
    fs.writeFileSync(copyFilePath, str)
    const { default: data } = await import(copyFilePath)
    
    //新增
    const map = getFormatDataMap(data)
})()

getFormatDataMap 函数执行后,返回的数据是:

{
'v1.5.3'=>['v1.5.3','ding提醒' //...]
'v1.5.4'=>['v1.5.4','通讯录人员列表' //...]
}


然后,需要知道表格最大行数,表格列数即为map.size(),最大行数通过获取Map.values()获取所有的值values,遍历values获取values内存放的每一个数组的长度,长度统一用另一个数组lens临时记录,遍历结束后比较lens中的数值得到最大的值,

MAX_LEN 即为表格最大的行数,也是values存放的所有数组中长度最大的值。

const _ = require('lodash');
//...
const getMergeArr = (map) => {
    const values = _.toArray(map.values())
    const lens = []
    //获取长度,长度值统一存放至lens数组中
    values.forEach((value) => { lens.push(value.length) })
    //比较
    const MAX_LEN = _.max(lens)
    
    return getTargetItems({ mapValue: values, forNum: MAX_LEN })
}

(async () => {
    const str = getBuryContent(buryFile)
    const copyFilePath = path.join(__dirname, '/bury.js')
    fs.writeFileSync(copyFilePath, str)
    const { default: data } = await import(copyFilePath)
    const map = getFormatDataMap(data)
    //新增
    const table = getMergeArr(map)
})()

最后,以valuesMAX_LEN进行双循环。表格列数map.size()可获取,但为了方便直接mapValue.length,两者是相等的。

有了表格列数即可创建二维数组的第二层数组,new Array(len).fill(' ')第二层数组长度即为mapValue.length,创建时数组内的值先统一填充为' '

const getTargetItems = ({ mapValue, forNum }) => {
    const len = mapValue.length
    const targetItems = []
    mapValue.forEach((v, i) => {
        for (let index = 0; index < forNum; index++) {
            const element = v[index];
            let targetItem = targetItems[index]
            if (!targetItem) {
            //创建数组,值先统一填充为' '
                targetItem = new Array(len).fill(' ')
            }
            /**
            如果当前index大于数组v的长度,这时获取值v[index]为undefined。
            为undefined的话直接跳过,保持targetItem[i]为' '
            */
            targetItem[i] = element ? element : ' '
            targetItems[index] = targetItem
        }
    })
    return targetItems
}

完成二维数组的转化,数据结构为下图:

生成表格

数据已完成,留下的就是写入数据生成表格,直接复制node-xlsx演示的代码下来。

//...

(async () => {
    const str = getBuryContent(buryFile)
    const copyFilePath = path.join(__dirname, '/bury.js')
    fs.writeFileSync(copyFilePath, str)
    const { default: data } = await import(copyFilePath)
    const map = getFormatDataMap(data)
    const table = getMergeArr(map)

    //写入数据,生成表格,返回buffer数据
    const buffer = xlsx.build([{ name: '埋点', data: table }])
    
    const outPath = path.join(__dirname, '/bury.xlsx')
    
    //bury.js文件可以删除,bury.xlsx如果已存在就先删了
    fs.existsSync(outPath) && fs.unlinkSync(outPath)
    fs.existsSync(copyFilePath) && fs.unlinkSync(copyFilePath)
    
    //创建一个bury.xlsx文件,将得到的buffer写入
    fs.writeFileSync(outPath, buffer)
})()

脚本完。

完整源码:

const path = require('path');
const fs = require('fs');
const xlsx = require('node-xlsx');
const magicString = require('magic-string')
const _ = require('lodash');
const buryFile = path.join(__dirname, '../src/lib/buryData.js')
const getBuryContent = (filePath) => {
    const content = fs.readFileSync(filePath, 'utf8')
    const s = new magicString(content)
    s.replace('export default', 'module.exports = ')
    return s.toString()
}
const getFormatDataMap = (data) => {
    let version
    const map = new Map();
    _.forIn(data, (value, key) => {
        if (key.includes('version')) {
            version = value
            !map.has(version) && map.set(version, [value])
            return
        }
        const mapValue = map.get(version)
        mapValue.push(value)
    })
    return map
}
const getTargetItems = ({ mapValue, forNum }) => {
    const len = mapValue.length
    const targetItems = []
    mapValue.forEach((v, i) => {
        for (let index = 0; index < forNum; index++) {
            const element = v[index];
            let targetItem = targetItems[index]
            if (!targetItem) {
                targetItem = new Array(len).fill(' ')
            }
            targetItem[i] = element ? element : ' '
            targetItems[index] = targetItem
        }
    })
    return targetItems
}
const getMergeArr = (map) => {
    const values = _.toArray(map.values())
    const lens = []
    values.forEach((value) => { lens.push(value.length) })
    const MAX_LEN = _.max(lens)
    return getTargetItems({ mapValue: values, forNum: MAX_LEN })
}
(async () => {
    const str = getBuryContent(buryFile)
    const copyFilePath = path.join(__dirname, '/bury.js')
    fs.writeFileSync(copyFilePath, str)
    const { default: data } = await import(copyFilePath)
    const map = getFormatDataMap(data)
    const table = getMergeArr(map)
    debugger
    const buffer = xlsx.build([{ name: '埋点', data: table }])
    const outPath = path.join(__dirname, '/bury.xlsx')
    fs.existsSync(outPath) && fs.unlinkSync(outPath)
    fs.existsSync(copyFilePath) && fs.unlinkSync(copyFilePath)
    fs.writeFileSync(outPath, buffer)
})()

去掉空行,一百行以内。

总结

Node.js可使用的场景非赏多,不单单是用于服务器接口的开发,我们还能通过写脚本的形式解决生活中重复性的工作,凭藉js的语法简单及强大的生态,前端不必学习shell、python等,仅使用js就可以搞定爬虫、自动化脚本等场景。

如果我的文章对你有帮助,你的👍就是对我的最大支持_

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

春节要放假了,外贸人你准备好了吗?

一转眼&#xff0c;老外们的假期即将结束&#xff0c;而我们的春节假期即将开启。有人会想&#xff1a;工作一年了&#xff0c;好不容易春节放个假&#xff0c;我是不是应该好好休息了&#xff1f;对别人是&#xff01;但是对于我们外贸人&#xff0c;并不是&#xff01;很多外…

第十三届蓝桥杯C++B组省赛 J 题——砍竹子(AC)

1.砍竹子 1.题目描述 这天&#xff0c;小明在砍竹子&#xff0c;他面前有 nnn 棵竹子排成一排&#xff0c;一开始第 iii棵竹子的高度为 hihihi。 他觉得一棵一棵砍太慢了&#xff0c;决定使用魔法来砍竹子。 魔法可以对连续的一段相同高度的竹子使用&#xff0c;假设这一段…

数学基本算法

欧几里得算法 求两个数的最大公约数&#xff1a; /**** param a 整数* param b 整数* return 两个整数的最大公约数*/public static int gcd(int a,int b){return b0?a:gcd(b,a%b);} 扩展欧几里得 /*** * param a * param b (a,b)两个整数* param x * param y (x,y)…

LeNet5模型与全连接模型的差异

1 问题深度学习训练过程中&#xff0c;有很多的训练模型&#xff0c;我们组就在思考LeNet模型与之前运用的全连接模型在训练精度损失与验证精度损失上有什么差别&#xff1f;2 方法这是LeNet模型的主要代码&#xff0c;对数据进行两成卷积与两次池化之后再建立三成全连接即可。…

Oracle数据库的监控指标

一、Oracle 监控指标 Oracle数据库常见性能指标主要有:当前登录数、非阻塞锁数、当前死锁数、阻塞锁数、当前锁数、会话数等内容,如下图所示。 注意:以下是 Oracle 监控所需要的指标,具体根据需要监控哪些、超阈值而定。 1.1 新建主机群组 1.2 新建模板 1.3 创建主机 …

Qt 6.4.2在Windows上安装过程及简单验证

Qt是一个跨平台的C开发库&#xff0c;用来开发图形用户界面(Graphical User Interface, GUI)。它支持Windows、Linux、macOS、Android、iOS、QNX等平台&#xff0c;一个框架、一套代码库、任意平台部署。Qt有开源和商业两种许可。 Qt从5.15开始官方不再直接提供开源的exe…

H5——连连看小游戏实现思路及源码

部门要求推广新产品用连连看小游戏的方式&#xff0c; 设计那边UI还没有排期&#xff0c;先撸个功能demo&#xff0c;正好记录一下&#xff1b; 连连看都玩过&#xff0c;程序的关键在于判断连续点击的两张图片是否能够消除&#xff0c;两个图片消除的条件有两个&#xff1a; …

【3 - 特征工程】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

Blog Navigation

Blog Navigation Linux Kernel 0x01_LinuxKernel_内核的启动&#xff08;一&#xff09;之启动前准备[2022-7-5]0x02_LinuxKernel_内核的启动&#xff08;二&#xff09;SMP多核处理器启动过程分析 [TODO]0x21_LinuxKernel_内核活动&#xff08;一&#xff09;之系统调用 [20…

【OpenGL学习】OpenGL窗口创建

OpenGL窗口创建 本节介绍如何在利用配置好的GLFW和GLAD进行一个简单窗口的绘制&#xff0c;首先明白在OpenGL中进行图形绘制到窗口的整个流程&#xff1a; 初始化GLFW创建窗口window创建OpenGL上下文初始化Glad渲染循环结束绘制 初始化GLFW 然后我们从上面的流程逐步进行窗…

知微传感3D相机SDK开发文档一查找相机

知微传感3D相机SDK开发文档一查找相机 主要对知微传感3D相机的SDK文件做一个介绍&#xff0c;大家一个界面显示程序包含图像的获得&#xff0c;相机的查找和链接 查找相机&#xff0c;通过UDP广播形式查找局域网相机链接相机&#xff0c;通过IP地址链接局域网内的相机获取图片&…

如何提升计算机的运行速度-正确清理C盘的方法

本文是向大家介绍清理C盘的方法&#xff0c;它能够实现给系统盘瘦身&#xff0c;能够带来提升计算机运行速度价值。提升计算机的运行速度-清理C盘的方法1 利用Windows附带的磁盘清理工具鼠标右击【C盘】选择【属性】选择【磁盘清理】勾选需要清理的文件&#xff0c;点击【确定】…

2023 hgame --- week1 wp

文章目录Miscsign ine99p1ant_want_girlfriend神秘的海报Where am IWebClassic Childhood GameBecome A MemberGuess Who I AmShow Me Your BeautyCryptoRSABe Stream神秘的电话兔兔的车票Retest_your_IDAeasyasmencodeeasyenca_cup_of_teaPwntest_nciotHelp the uncle who can…

【阶段三】Python机器学习29篇:机器学习项目实战:DBSCAN算法的基本原理与DBCSAN新闻聚类分群模型

本篇的思维导图: DBSCAN模型 DBSCAN(全称为Density-Based Spatial Clustering of Applications with Noise)是一种以密度为基础的空间聚类算法,可以用密度的概念剔除不属于任一类别的噪声点。该算法将簇定义为密度相连的点的最大集合,将具有足够密度的区域划分为…

rabbitmq命令大全

大家好&#xff0c;我是早九晚十二&#xff0c;目前是做运维相关的工作。写博客是为了积累&#xff0c;希望大家一起进步&#xff01; 我的主页&#xff1a;早九晚十二 文章目录服务管理启动服务后台启动查看状态用户管理新增账号删除用户所有用户:修改密码:清除密码角色管理设…

MinIO安装总结

概述 初学者安装MinIO还是会遇到很多坑的&#xff0c;这里做个总结&#xff0c;希望能够大家可以快速的搭建MinIO环境&#xff0c;可以更好的快速入门。 linux下手动安装(不推荐) cd /opt/testMinIO wget https://dl.min.io/server/minio/release/linux-amd64/minio chmod x…

spark Shuffle

Spark Shuffle的演进及SortShuffle的基本思想 1. 演进 HashShuffle -> HashShuffle 的Consolidate机制 -> SortBasedShuffle -> Tungsten-sort Based Shuffle 2. 各种 Shuffle的介绍 2.1 HashShuffle Spark 之所以一开始就提供基于 Hash 的 Shuffle 实现机制&…

高级驾驶辅助系统(ADAS)——自适应巡航控制系统

一、定速巡航——L1级别 定速巡航系统&#xff08;CCS&#xff1a;Cruise Control&#xff09;&#xff1a;驾驶员设定目标车速&#xff08;手动加速或减速&#xff09;&#xff0c;不能根据实际路况对车辆的行驶状态进行调节或者给予必要的预警提示&#xff0c;缺乏对环境的应…

【信管8.2】建设与管理项目团队

建设与管理项目团队在经过制定人力资源管理计划之后&#xff0c;我们就有了人力资源计划文档&#xff0c;然后通过组建团队也让团队有了正式的成员&#xff0c;接下来要干什么呢&#xff1f;当然就是在项目的开发过程中不断地建设团队和管理团队了。这两个部分的内容也是我们项…

斜方向三消查找算法的原理和实现

本文首发于微信公众号&#xff1a; 小蚂蚁教你做游戏。欢迎关注领取更多学习做游戏的原创教程资料&#xff0c;每天学点儿游戏开发知识。嗨&#xff01;大家好&#xff0c;我是小蚂蚁。上篇文章中我们讲了三消查找算法的原理和实现&#xff0c;在宝石方块中&#xff0c;除了水平…