ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 1047|回复: 3

[求助] 把药方中每种药材的数量,按多少重新排序

[复制链接]

TA的精华主题

TA的得分主题

发表于 2024-9-3 08:14 | 显示全部楼层 |阅读模式
求各位老师帮忙,写个替换代码,详见:附件,多谢!

测试附件.rar

24.8 KB, 下载次数: 12

TA的精华主题

TA的得分主题

发表于 2024-9-3 14:57 来自手机 | 显示全部楼层
这是把eh当免费的代工地了啊
上次搞了那么久,过了一个月又不满意了?

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-9-4 05:42 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
Meteor-渣渣 发表于 2024-9-3 14:57
这是把eh当免费的代工地了啊
上次搞了那么久,过了一个月又不满意了?

不是不满意呀,如是和上次的一样,还用再求助吗?还有这个论坛不就是一个求助、交流、学习的平台吗?

TA的精华主题

TA的得分主题

 楼主| 发表于 2024-10-7 06:45 | 显示全部楼层
感谢 sunya_0529 老师,写了一个 JSA 代码!
  1. function 药方整理() {
  2.     const units = ['克', '枚', '粒', '片', '只', '条', '个', '寸', '毫升', '杯', '剂'].join('|');        //定义药方中用到的计量单位
  3.     const sortMaterials = (str) => { //定义一个函数,对传入的字符串解析后分组排序,并输出新字符串
  4.         let strStart = /^((\d+)|(.+?:)).+/.test(str) ? str.match(/^((\d+)|(.+?:)).+/)[1] : '';        //定义前导字符串
  5.         let materials = str
  6.             .replace(strStart, '')        //替换掉前导字符串
  7.             .replace('\r', '')        //替换掉换行符
  8.             .replace(/[。、,]$/, '')        //替换掉行末的“。、,”
  9.             .replace(new RegExp(`(\\d*\\.?\\d+(${units})(([^()]+))?)(?=[、,。])`, 'g'), '$1;')        //识别“*克(*)”并在其后插入“;”
  10.             .split(';');        //以“;”为分隔符,把字符串拆分成各组
  11.         let strEnd = materials[materials.length - 1];        //取分组中的最后一个元素,不包括“*单位”的话,就作为收尾字符串,加到结果字符串的后面
  12.         strEnd = new RegExp(`.*\\d*\\.?\\d+(${units}).*`).test(strEnd) ? '' : strEnd.replace(/^[、,。]|[、,。]$/g, '');
  13.         let arrTemp = [];        //定义临时数组,存储整理排序后的药材数据
  14.         if (materials) {
  15.             const obj = materials.reduce((a, c) => {        //在各组药材中循环
  16.                 let arr = c
  17.                     .replace(/^[。、,]/, '')        //替换掉行首的“。、,”
  18.                     .replace(/(,各)(?=\d*\.?\d+)/g, '|')        //将“,各”替换成“|”
  19.                     .replace(/(?<=(.+?)、(?=.+?))/g, '※')        //将“()”内的“、”替换为“※”
  20.                     .replace(/、/g, '|')                //将“、”替换为“|”
  21.                     .replace(/※/g, '、')         //将“()”内的“※”替换为“、”(还原)
  22.                     .replace(new RegExp(`((\\d*\\.?\\d+~)?\\d*\\.?\\d+(${units}))`, 'g'), `|$1`)        //在“[药材名][数量][单位]”的“[数量]”前插入“|”
  23.                     .split('|')        //以“|”为分隔符拆分字符串
  24.                     .filter(Boolean);        //过滤掉空值,将字符串拆分成“[药材名1,药材名2,...,*单位]”的数组
  25.                 let unit = String(arr[arr.length - 1])
  26.                     .replace(/(.+)/g, '')        //替换掉“(*)”
  27.                     .replace(new RegExp(`[^${units}]`, 'g'), '');        //获取单位
  28.                 let amount = String(arr[arr.length - 1]).split(unit)[0];        //获取数量
  29.                 a[unit] ??= {};        //以单位为键
  30.                 arr.slice(0, -1).forEach(v => {
  31.                     a[unit][amount] ??= [];        //以数量为二级对象的键,值为数组,用来储存对应的药材名
  32.                     a[unit][amount].push(v + String(arr[arr.length - 1]).split(unit)[1]);        //将药材名存入数组,同时“*单位”后的内容加在各药材后
  33.                 });
  34.                 return a;
  35.             }, {});
  36.             for (let o in obj) {        //在obj对象中循环               
  37.                 Object.keys(obj[o]).sort((a, b) => {        //对数量进行降序排序
  38.                     return (Number(b) ? b : b.slice(b.lastIndexOf('~') + 1)) - (Number(a) ? a : a.slice(a.lastIndexOf('~') + 1));
  39.                 }).forEach(n => {        //循环读取药材数组,写入临时数组arrTemp
  40.                     let brr = obj[o][n];
  41.                     arrTemp.push(brr.length > 1 ? `${brr.join('、')},各${n}${o}` : `${brr[0]}${n}${o}`);
  42.                 });
  43.             }
  44.             if (strEnd.length) arrTemp.push(strEnd);        //收尾字符串不为''的话,加入临时数组
  45.         }
  46.         return `${strStart}${arrTemp.join(';')}`;        //函数返回拼接完整的字符串
  47.     }

  48.     const res = [];        //定义结果储存数组
  49.     [...ActiveDocument.Paragraphs].forEach((p, i) => {        //在文档中各段落循环
  50.         let strPara = p.Range.Text;        //定义段落文本
  51.         if (strPara !== '\r' && !strPara.startsWith('【整理】')) {
  52.             res.push(strPara);
  53.             if (new RegExp(`(\\d*\\.?\\d+(${units}))+`).test(strPara) && !strPara.startsWith('替换为:')) {
  54.                 let strSub = strPara.match(new RegExp(`[^、,。)]+([^)]*(\\d*\\.?\\d+(${units}))[^(]*)(?=[、,。]?)`, 'g'));
  55.                 if (strSub) strSub = strSub.map(s => {        //剔除子配方的字符串
  56.                     strPara = strPara.replace(s, '');
  57.                     let str1 = s.slice(0, s.indexOf('(') + 1);
  58.                     if (!new RegExp(`.+\\d*\\.?\\d+(${units})($`).test(str1)) {                //调用函数,对“()”内的内容进行整理
  59.                         let str2 = s.slice(s.indexOf('(') + 1, s.indexOf(')'));
  60.                         return `${str1}${sortMaterials(str2)})`;
  61.                     } else {
  62.                         return s;
  63.                     }
  64.                 });
  65.                 res.push(`【整理】——${sortMaterials(strPara)}${strSub ? ';' + strSub.join(';') : ''}。\r`)
  66.             }
  67.         }
  68.     });
  69.     ActiveDocument.Content.Delete();        //清空文档内容
  70.     res.forEach(t => {
  71.         let myRange = ActiveDocument.Paragraphs.Last.Range
  72.         myRange.Text = t + '\r';
  73.         if (t.startsWith('【整理】')) myRange.Font.ColorIndex = 5;
  74.     });
  75. }
复制代码


您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-12-25 15:32 , Processed in 0.031561 second(s), 10 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表