实例需求:现需要将原料配方成分表按照“原料含量”从高到低排序,如下图所示。由于表格中包含合并单元格,因此Excel的排序功能无法正常。
示例代码如下。
Sub demo()
Dim data(), i, idx, k, s
lstRow = Cells(Rows.Count, 2).End(xlUp).Row
ReDim data(1 To lstRow, 1)
idx = 1
For i = 3 To lstRow
If Cells(i, 1) <> "" Then
data(idx, 0) = Cells(i, "D")
data(idx, 1) = Cells(i, "D").MergeArea.EntireRow.Address
idx = idx + 1
End If
Next
idx = idx - 1
For k = 1 To idx
p_val = data(k, 0)
For s = k + 1 To idx
If data(s, 0) > p_val Then
tmp = data(k, 1)
data(k, 0) = data(s, 0): data(k, 1) = data(s, 1)
data(s, 0) = p_val: data(s, 1) = tmp
p_val = data(k, 0)
End If
Next
Next
anchor = lstRow + 2
For k = 1 To idx
Set r = Range(data(k, 1))
Application.CutCopyMode = False
r.Copy Cells(anchor, 1)
Cells(anchor, 1) = k
anchor = anchor + r.Rows.Count
Next
Range("3:" & lstRow + 1).Delete
End Sub
【代码解析】
第3行代码获取最后数据行的行号。
第4行代码声明动态数组。
第6~12行代码循环读取数据,保存在数组data中,第一个元素为D列的数值(排序依据),第二个元素为该原料所在行,例如第3个原料所在行为6:8
。
第9行代码中使用MergeArea
获取D列单元格的合并单元格区域,EntireRow
代表整行单元格区域。
第13行代码将变量idx减一,变量的值为数据表中原料的个数。
第14~24行代码使用双重循环冒泡法排序(需要详细学习此算法的同学,可以自行搜索相关资料)。
第15行代码读取第k个元素(D列值)用于作为排序判断的基准。
第16~23行代码冒泡排序确保把待排序数据(数组中第k个元素至最后一个元素)中的最大一个排列调整到第k个位置。
第19~20行代码用于交换数组的值,由于data是二维数组,所以需要使用一个临时变量tmp作为中转。
排序完成之后,接下来是就需要调整数据表中相应数据行的次序,由于数组中记录的使用每个原料所有行号,剪切操作将导致其他数据行号的相应变化,所以最方便的操作方式是将按照排序后的结果将数据复制到一个空白区域,然后再删除原数据区域。
第25行代码设置保存数据的目标区域起始单元格(下文称为锚点单元格)行号。
第26~31行代码循环拷贝数据行。
第27行代码清空系统剪贴板。
第28行代码将数据行拷贝到锚点单元格。
第29行代码设置原料序号。
第30行代码计算下一个锚点单元格的行号。
第32行代码删除原数据表。