WPS JS宏:通用数据分组求和






function demo123() {// ========== 第1步:选择数据区域(包含标题行) ==========var dataRng = null;try {dataRng = Application.InputBox("请框选数据区域(第一行为列标题,以下为数据行)","选择数据区域",null, null, null, null, null, 8);} catch (e) {alert("输入框调用失败,请确认 WPS 版本支持。");return;}if (dataRng === false || dataRng === null) return;if (typeof dataRng === "string") {try { dataRng = Range(dataRng); } catch (e) { alert("无效的区域地址:" + dataRng); return; }}if (!dataRng || typeof dataRng.Address !== "function") {alert("未获取到有效的数据区域,请重新选择。");return;}// 读取数据并转为二维数组var arr = dataRng.Value();if (arr === null || arr === undefined) { alert("所选区域无数据"); return; }// 若为单个值,转为二维if (!Array.isArray(arr)) { arr = [[arr]]; }else if (arr.length > 0 && !Array.isArray(arr[0])) { arr = [arr]; }if (arr.length === 0 || !Array.isArray(arr[0])) { alert("数据格式异常"); return; }var totalRows = arr.length;var colCount = arr[0].length;var startCol = dataRng.Column; // 数据区域起始列号(1-based)// 至少需要一行标题 + 一行数据,且至少2列if (totalRows < 2) {alert("数据区域至少需要一行标题和一行数据。");return;}if (colCount < 2) {alert("数据区域至少需要2列。");return;}// 分离标题行和数据行var headerRow = arr[0]; // 标题行(一维数组)var dataRows = arr.slice(1); // 数据行(二维数组)// ========== 第2步:选择分类列 ==========var classRng = null;try {classRng = Application.InputBox("请点击分类列(分组依据)中的任意单元格(含标题行)","选择分类列",null, null, null, null, null, 8);} catch (e) { alert("输入框调用失败"); return; }if (classRng === false || classRng === null) return;if (typeof classRng === "string") {try { classRng = Range(classRng); } catch (e) { alert("无效的单元格地址"); return; }}if (!classRng || typeof classRng.Address !== "function") {alert("未获取到有效的分类列单元格");return;}var classColAbs = classRng.Column;var classColIdx = classColAbs - startCol;if (classColIdx < 0 || classColIdx >= colCount) {alert("分类列不在所选数据区域内,请重新选择。");return;}var classHeader = headerRow[classColIdx]; // 获取分类列标题// ========== 第3步:选择求和列 ==========var sumRng = null;try {sumRng = Application.InputBox("请点击求和列(数值列)中的任意单元格(含标题行)","选择求和列",null, null, null, null, null, 8);} catch (e) { alert("输入框调用失败"); return; }if (sumRng === false || sumRng === null) return;if (typeof sumRng === "string") {try { sumRng = Range(sumRng); } catch (e) { alert("无效的单元格地址"); return; }}if (!sumRng || typeof sumRng.Address !== "function") {alert("未获取到有效的求和列单元格");return;}var sumColAbs = sumRng.Column;var sumColIdx = sumColAbs - startCol;if (sumColIdx < 0 || sumColIdx >= colCount) {alert("求和列不在所选数据区域内,请重新选择。");return;}var sumHeader = headerRow[sumColIdx]; // 获取求和列标题// ========== 第4步:分组汇总(使用用户指定的列,从数据行开始) ==========// 提取唯一分类值(过滤空值)var groupSet = new Set();for (var i = 0; i < dataRows.length; i++) {var val = dataRows[i][classColIdx];if (val !== undefined && val !== "") groupSet.add(val);}var group = [...groupSet];if (group.length === 0) { alert("分类列没有有效数据"); return; }// 构建结果二维数组,先放入表头(动态获取)var result = [];result.push([classHeader, sumHeader]); // 表头行// 对各分组求和for (var gIdx = 0; gIdx < group.length; gIdx++) {var g = group[gIdx];var sum = 0;for (var i = 0; i < dataRows.length; i++) {if (dataRows[i][classColIdx] == g) {var num = Number(dataRows[i][sumColIdx]);if (!isNaN(num)) sum += num;}}result.push([g, sum]);}// ========== 第5步:选择存放起始单元格 ==========var targetRng = null;try {targetRng = Application.InputBox("请选择结果存放的起始单元格(如 E1,表头将写入该行)","选择目标单元格",null, null, null, null, null, 8);} catch (e) { alert("输入框调用失败"); return; }if (targetRng === false || targetRng === null) return;if (typeof targetRng === "string") {try { targetRng = Range(targetRng); } catch (e) { alert("无效的单元格地址"); return; }}if (!targetRng || typeof targetRng.Address !== "function") {alert("未获取到有效的目标单元格");return;}// 写入结果(包含动态表头)targetRng.Resize(result.length, 2).Value2 = result;alert("汇总完成!结果已从 " + targetRng.Address() + " 开始输出。\n表头为:" + classHeader + " | " + sumHeader);}
夜雨聆风