esProc 利用剪贴板强化 Excel 计算
遇到 Excel 难以实现的复杂或特殊运算时,可先用剪贴板将数据复制到 esProc,利用 esProc 强大的计算能力完成,再用剪贴板返回 Excel。剪贴板方式比传统的 add-ins 方式部署更简单,操作更顺滑,开发时不影响剪贴板正常使用,且支持多个源片区和结果片区。
基本用法#
esProc 提供了函数 clipboard,可实现剪贴板的基本用法。下面用 “各科前 3 名的学生” 为例进行说明。
Excel 中处理前的数据如下,其中 A 列是学生姓名,B-D 列分别是数学、英语、物理成绩。
| | A | B | C | D |
| 1 | name | math | english | physics |
| 2 | lily | 97 | 100 | 99 |
| 3 | Joshua | 100 | 99 | 100 |
| 4 | Sarah | 98 | 99 | 96 |
| 5 | Bertram | 94 | 95 | 85 |
| 6 | Paula | 91 | 88 | 91 |
| 7 | Sophia | 92 | 81 | 76 |
| 8 | Ben | 87 | 80 | 76 |
| 9 | Ruth | 92 | 91 | 87 |
| 10 | Pag | 95 | 87 | 87 |
计算目标:求出每学科成绩前 3 名的学生,并追加到本科目成绩之后。
这个计算目标需要用到记录集合 TopN、按序号拼接等功能,Excel 本身不好实现,但借助 esProc 就容易多了。先在 Excel 中选中源片区(A1:D10),按下 ctrl+C,复制到系统剪贴板,打开集算器 IDE,编写并执行如下脚本:
| | A | B |
| 1 | =clipboard ().import@t () | / 从剪切板读取数据 |
| 2 | =A1.top (-3;math).(name) | /math 前 3 名 |
| 3 | =A1.top(-3;english).(name) | |
| 4 | =A1.top(-3;physics).(name) | |
| 5 | =join@p (A2;A3;A4).export () | / 拼成二维表,转成字串 |
| 6 | =clipboard (A5) | | / 向剪切板写数据 |
| | | | |
上面代码中,clipboard 函数有两种形态,其中无参数调用该函数时,可返回剪贴中的字符串,如 A1 格;以变量或格名为参数调用该函数时,可向剪贴板写入字符串,形如 A6 中的 clipboard (var)。
执行上述脚本后,在 Excel 的 B11 格用 ctrl+V,即可将剪切板中的数据复制到 B11-D13,如下:
| | A | B | C | D |
| | … | … | … | … |
| 10 | Pag | 95 | 87 | 87 |
| 11 | | Joshua | Lily | Joshua |
| 12 | | Sarah | Sarah | Lily |
| 13 | | lily | Joshua | Sarah |
随意编辑脚本#
但是,我们在编辑调试脚本时,保不齐会用一下复制粘贴,这时候就把剪贴板的内容冲掉了,再执行 clipboard () 时, 返回的内容就是刚才编辑的代码,这样就无法完成计算了,只能再回到 Excel 去重新复制,有点麻烦。
针对这个问题,esProc 提供了 clipboard@e () 函数,选项 @e 表示始终返回第一次从 Excel 复制到剪贴板中的数据。下面让我们试一下。
在 “各科前 3 名的学生” 这个例子中,假设我们已经从 Excel 复制了数据,并写完了脚本。此时编辑脚本,将 A3、A4 移动到 B2、B3,再执行脚本时,就会因为 A1 中的 clipbaord () 取到错误数据,而导致 A2 报错。现在修改代码,在 A1 使用刚才提到的 clipboard@e (),则移动代码后可正确执行。编辑后的代码如下:
| | A | B | C |
| 1 | =clipboard@e ().import@t () | / 从剪切板读取数据 | |
| 2 | =A1.top(-3;math).(name) | =A1.top(-3;english).(name) | =A1.top(-3;physics).(name) |
| 3 | =join@p (A2;B2;B3).export () | / 拼成二维表,转成字串 | |
| 4 | =clipboard (A3) | / 向剪切板写数据 | |
多个结果片区#
clipboard (…) 只能返回一个结果,但有些较复杂的运算可能需要返回多个结果,这该怎么办呢?
其实 ,在 esProc 的结果显示区可以直接把多个格值(或变量值)分别复制到剪贴板,并依次返回 Excel。
下面用 “各科前 3 名和每个人的超越目标” 为例进行说明。
计算目标:在学生成绩单的基础上,不仅在各科目下面追加本科目前 3 名的学生,而且需要新加一列 target,计算出比本人总分略高的 3 名学生的名单列表,作为本人应当超越的目标。注意,有些人的超越目标不足 3 人,每个超越目标之间须用 > 号连接。
先在 Excel 中选中源片区(A1:D10),按下 ctrl+C,复制到系统剪贴板,打开 esProc IDE,编写并执行如下脚本:
| | A | B | C |
| 1 | =clipboard@e ().import@t () | 从剪切板获取数据 | |
| 2 | =A1.top(-3;math).(name) | =A1.top(-3;english).(name) | =A1.top(-3;physics).(name) |
| 3 | =join@p (A2;B2;C2) | 片区 1:各科前 3 名 | |
| 4 | =A1.derive (sum (math,english,physics):subtotal) | 每个人的总分 | |
| 5 | =A4.derive (t=subtotal,A4.select (subtotal>t):beforeMe) | 排在本人前面的学生 | |
| 6 | =A5.new (beforeMe.top (3;subtotal).(name).concat (“>”):target) | 片区 2:最近 3 名学生 | |
上述代码中,A3 存储结果片区 1,即本科目前 3 名的学生;A4 存储结果片区 2,即每个人的超越目标。另外应当注意到,代码中无须 export () 和 clipboard (…)。
下面将计算结果返回 Excel。先点击片区 1,再点击右侧对应的 “copy data” 按钮,如下图:
在 Excel 的 B11 格用 ctrl+V ,即可将片区 1 复制到 B11-D13 ,如下:
| | A | B | C | D |
| 1 | name | math | english | physics |
| 2 | Lily | 97 | 100 | 99 |
| 3 | Joshua | 100 | 99 | 100 |
| 4 | Sarah | 98 | 99 | 96 |
| 5 | Bertram | 94 | 95 | 85 |
| 6 | Paula | 91 | 88 | 91 |
| 7 | Sophia | 92 | 81 | 76 |
| 8 | Ben | 87 | 80 | 76 |
| 9 | Ruth | 92 | 91 | 87 |
| 10 | Pag | 95 | 87 | 87 |
| 11 | | Joshua | lily | Joshua |
| 12 | | Sarah | Sarah | lily |
| 13 | | lily | Joshua | Sarah |
再点击脚本中的片区 2,按住 shift 的同时,点击右侧对应的 “copy data” 按钮,之后在 Excel 的的 E1 格按下 ctrl+V,即可将片区 2 连带列名复制到 E1:E10。如下所示:
| | A | B | C | D | E |
| 1 | name | math | english | physics | Target |
| 2 | Lily | 97 | 100 | 99 | Joshua |
| 3 | Joshua | 100 | 99 | 100 | |
| 4 | Sarah | 98 | 99 | 96 | lily>Joshua |
| 5 | Bertram | 94 | 95 | 85 | Sarah>lily>Joshua |
| 6 | Paula | 91 | 88 | 91 | Bertram>Sarah>lily |
| 7 | Sophia | 92 | 81 | 76 | Pag>Ruth>Paula |
| 8 | Ben | 87 | 80 | 76 | Sophia>Pag>Ruth |
| 9 | Ruth | 92 | 91 | 87 | Bertram>Sarah>lily |
| 10 | Pag | 95 | 87 | 87 | Ruth>Paula>Bertram |
| 11 | | Joshua | lily | Joshua | |
| 12 | | Sarah | Sarah | lily | |
| 13 | | lily | Joshua | Sarah | |
在上述操作中,我们可通过 shift 控制计算结果是否带列名,这是 esProc 的独有方式,其他计算工具不具备此项功能。
多个源片区#
解决了多目标的问题后,我们很容易联想到有多个源的问题。计算可能需要使用 Excel 的多个源片区,但 clipboard 只能保持最新复制的那一片,这又该怎么办?
有办法,可以把剪贴板内容直接复杂到格子里面去。
下面用 “查询符合指定条件的订单” 为例进行说明。
Excel 中有两个 sheet,订单明细和员工名单,其中订单明细如下:
| | A | B | C | D | E |
| 1 | OrderID | Client | SellerId | Amount | OrderDate |
| 2 | 1 | WVF Vip | 1 | 440 | 2014-11-03 |
| 3 | 2 | UFS Com | 1 | 1863 | 2015-01-01 |
| 4 | 3 | SWFR | 2 | 1813 | 2014-11-01 |
| 5 | 4 | JFS Pep | 2 | 671 | 2015-01-01 |
| 6 | 5 | DSG | 1 | 3730 | 2015-01-01 |
| 7 | 6 | JFE | 1 | 1445 | 2015-01-01 |
| 8 | 7 | OLF | 3 | 625 | 2015-01-01 |
| 9 | 8 | PAER | 3 | 2490 | 2015-01-01 |
员工名单如下:
| | A | B | C | D | E | F | G |
| 1 | EId | State | Dept | Name | Gender | Salary | Birthday |
| 2 | 2 | New York | Marketing | Ashley | F | 11001 | 1980-07-19 |
| 3 | 3 | New Mexico | Sales | Rachel | F | 9000 | 1970-12-17 |
| 4 | 4 | Texas | HR | Emily | F | 7000 | 1985-03-07 |
| 5 | 5 | Texas | R&D | Ashley | F | 16000 | 1975-05-13 |
| 6 | 6 | California | Sales | Matthew | M | 11000 | 1984-07-07 |
| 7 | 7 | Illinois | Sales | Alexis | F | 9000 | 1972-08-16 |
| 8 | 8 | California | Marketing | Megan | F | 11000 | 1979-04-19 |
| 9 | 1 | Texas | HR | Victoria | F | 3000 | 1983-12-07 |
计算目标:查询出最近 days 天内或订单属于部门列表 depts 的数据,需要的列有订单编号、日期、金额、销售员名字、部门名称。其中 days 是外部参数,每次执行时都可以输入不同的值,比如输入 30 表示查询 30 天内的订单;depts 也是外部参数,比如 [“Markeding”,”Finance”]。这个计算目标涉及动态查询和多键值查询,Excel 本身不好实现,但借助 esProc 就容易多了。
首先,在 Excel 中选定片区 “订单明细”,连带列名一起复制粘贴到 esProc 脚本的 A1 单元格;同样地,将片区 “员工名单” 复制到 esProc 脚本的 A1 单元格。如下图:
注意,一定要进入 A1 格的编辑状态才能进行复制,不然会把剪贴板的内容填进一大片格子,结果可能变成这样:
这就不好了,占有区域太大,会影响代码布局。
如果操作正确,A1/A2 格外观上只呈现很小一部分数据,点击后才会呈现完整数据,这是网格式代码的独特之处,适合在不影响阅读和代码布局的情况下复制大片数据。
复制完两个源片区后,继续编写脚本,期间可多次运行脚本,可手工操作剪贴板。最后代码如下:
| | A | B | C |
| 1 | OrderID Client… | =A1.import@t() | /order list |
| 2 | EId State… | =A2.import@t() | /employee list |
| 3 | =B1.switch(SellerId,B2:EId) | | /Join with EId |
| 4 | =A3.select(OrderDate>=after(date(now()),days*-1)|| depts.pos(SellerId.Dept)) | | /Search data |
| 5 | =A4.new(OrderID,OrderDate,Amount,SellerId.Name:Name,SellerId.Dept:Dept) | | /return fields needed |
完成计算后,可用 “copy data” 按钮将 A5 中的计算结果拷贝回 Excel。有一点需要注意,保存代码时应当把 A1/A2 格的数据清除掉,否则这些大片数据也会被一起保存起来了。
本作品采用《CC 协议》,转载必须注明作者和本文链接