[excelvba]自定义排序的三种方法

medik medik     2022-12-20     570

关键词:

诸君好,前前期我们聊了VBA编程和数据的常规排序……VBA常用小代码105:Rang对象的排序操作……
今天我们再聊下自定义排序……
何谓自定义排序,就是按指定的顺序对数据源进行排序呗……

今一共分享了三种方法。
第1种方法是系统自带的OrderCustom,优点是代码简洁,缺点是自定义序列有字符长度限制(255个)。
第2种方法是字典+数组设置序列号,再使用了辅助列进行排序。优点是不会破坏单元格的形式和结构,比如单元格中存在的公式、背景等。
第3种方法是只使用字典+数组,借助简单桶排序的技巧,直接对数据在数组中进行排序。优点是效率较高,缺点是会破坏单元格的结构,比如消除公式等。
(第1种建议掌握,第2种建议了解,第3种……能懂就懂,不懂先放着吧~)

举个例子。
如下图所示,A:C列是数据源。
现需要根据E列所指定的部门先后顺序,对数据源进行重新排序,如果部门不在指定序列内,则排放在数据源末尾。
技术图片
排序结果如下图。
技术图片

第1种方法代码如下:

Sub FreeSort()
‘eh技术论坛 VBA编程学习与实践 看见星光
Dim n&, rng As Range
Set rng = Range("e2:e" & Cells(Rows.Count, "e").End(xlUp).Row)
Application.AddCustomList (rng)
‘增加一个自定义序列,该参数除了支持单元格对象,也支持数组。
n = Application.CustomListCount
‘自定义序列的数目
Range("a:c").Sort key1:=[a1], order1:=xlAscending, Header:=xlYes, ordercustom:=n + 1
‘使用自定义排序,ordercustom指定使用哪个自定义序列排序。
‘当使用自定义排序时,需要将OrderCustom参数设置为指定的序列在自定义列表中的顺序加1
Application.DeleteCustomList n
‘删除新增的自定义序列
End Sub


第2种方法代码如下:

Sub DicSort()
Dim d As Object, r, i&, arr, brr
Set d = CreateObject("ing.dictionary")
r = Range("e2:e" & Cells(Rows.Count, "e").End(xlUp).Row).Value
For i = 1 To UBound(r)
d(r(i, 1)) = i ‘目标序列循环装入字典,序号作为item
Next
arr = Range("a2:c" & Cells(Rows.Count, 1).End(xlUp).Row)
‘数据源装入数组arr
ReDim brr(1 To UBound(arr), 1 To 1)
‘声明数组brr装原部门在指定序列中的序号
For i = 1 To UBound(arr)
If d.exists(arr(i, 1)) Then
brr(i, 1) = d(arr(i, 1)) ‘将原部门在指定序列中的序列号装入brr
Else
brr(i, 1) = "指定序列不存在"
End If
Next
[d:d].Insert
‘在D列插入一列
[d2].Resize(UBound(brr), 1) = brr
‘新的序列号放入D列
Range("a:d").Sort key1:=[d1], order1:=xlAscending, Header:=xlYes ‘D列升序排序
[d:d].Delete ‘删除D列
Set d = Nothing
End Sub


第3种方法代码如下:

Sub DicArrSort()
‘eh技术论坛公众号 VBA编程学习与实践 看见星光
Dim d As Object, i&, n&, x&, k&, j&
Dim r, arr, brr, crr
Set d = CreateObject("ing.dictionary")
‘后期绑定字典
r = Range("e2:e" & Cells(Rows.Count, "e").End(xlUp).Row).Value
For i = 1 To UBound(r)
d(r(i, 1)) = i ‘目标序列循环装入字典,序号作为item
Next
arr = Range("a2:c" & Cells(Rows.Count, 1).End(xlUp).Row)
‘数据源装入数组
ReDim brr(1 To d.Count + 1, 1 To 1)
‘brr数组用于按序号装数组arr的行号,类似于桶排序的桶
For i = 1 To UBound(arr)
If d.exists(arr(i, 1)) Then
‘如果字典中存在相关部门……
n = d(arr(i, 1))
‘该部门在指定序列中的序号
brr(n, 1) = brr(n, 1) & "," & i
‘将该部门在arr中的行号装入数组brr对应的序号行
Else
brr(UBound(brr), 1) = brr(UBound(brr), 1) & "," & i
‘如果字典中不存在,放入数组brr最后一行
End If
Next
ReDim crr(1 To UBound(arr), 1 To UBound(arr, 2))
‘数组crr放排序后的结果
For i = 1 To UBound(brr)
If brr(i, 1) <> "" Then
‘如果不为空,则有符合指定排序条件的关键词
r = Split(brr(i, 1), ",")
‘将brr该位置储存的行号取出
For x = 1 To UBound(r)
k = k + 1 ‘累加行
For j = 1 To UBound(arr, 2)
crr(k, j) = arr(r(x), j)
‘遍历指定行位置数组arr的值移到crr
Next
Next
End If
Next
Range("a2:c" & Cells(Rows.Count, 1).End(xlUp).Row) = crr
‘将数组crr排序后的结果放回单元格区域
Set d = Nothing ‘释放字典
Erase arr: Erase brr: Erase crr
‘释放数组
End Sub


题外话:
之前我们讲过,数组和字典是VBA处理数据的最佳利器,这是由于数组可以提高计算效率,字典可以关联多个数据源构建各种关系,因此这里再次对学习VBA的童鞋们提个小建议,不要在单元格工作簿等对象上浪费太多时间,那是熟能生巧的事物,数组和字典才是学习VBA的核心要义哦。

实现自定义view的三种方式(代码片段)

...nDraw方法来实现测量、布局以及draw流程。所以,在实现自定义view的三种方式中,这一种相对比较简单。   &nbs 查看详情

wpf自定义控件的三种方式(代码片段)

简介: 某些场景下,我们确实需要创建新的控件。此时,理解WPF不同控件的创建方法就显得非常重要。WPF提供3个用于创建控件的方法,每个方法都提供不同的灵活度。WPF控件可以通过数据模型(DataTemplateÿ... 查看详情

数组的三种随机排序方法

...面是完整代码)  递归的方法是利用递归函数的自调,定义一个随机数index(因为定了向下取整,所以范围为0~8)作 查看详情

vbscript在excelvba中复制和粘贴的三种方法。请注意,`range.copy`和`range.pastespecial`使用剪贴板,因此请避免复制任何内容(代码片段)

查看详情

c++中定义比较函数的三种方法

...知道集合中的对象,那个在前那个在后。因此,学会如何定义比较方法是非常重要的。  C++模板库的许多容器需要相关类型有序,例如set<T>和priority_queue<T>。  这篇文章旨在告诉大家如何为一个类定义一个排序方法... 查看详情

java线程的三种命名方法

...Thread(Runnabler,Stringname)2Threadt=newThread(()->{},"甲");3.使用自定义的线程类,在实例化线程对象的同时,进行名称的赋值1MyThreadt=newMyThread("甲");23 查看详情

中间定宽,两边自适应布局的三种实现方法

中间定宽,两边自适应布局的三种实现方法1.浮动加定位<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>中间定宽,两边自适应</title><styletype="text/css">html,body,div{height:100%;}.p 查看详情

hive自定义函数的三种类型

类型一UDF(User-Defined-Function)一进一出类型二UDAF(User-DefinedAggregationFunction)聚集函数,多进一出类似于:count/max/min类型三UDTF(User-DefinedTable-GeneratingFunctions) 查看详情

js对象定义的最常用的三种方法

定义对象:属性和方法的结合体(变量和函数的结合体) 1.(***)varobj={} 2.varobj=newObject(); 3.使用function定义对象具体例子分别为:1//1.定义obj的对象2//obj:对象名3varobj={4//height,weight,sex称为对象的属性5height:170,//使用,分隔属性和... 查看详情

jsoop中的三种继承方法

...两方,发生在两个类之间)>一、通过object实现继承1:定义父类 functionParent(){} 2:定义子类 funtion 查看详情

49多线程创建的三种方式之继承thread类(代码片段)

...就会执行线程中的内容。使用Thread创建线程的步骤:1.自定义一个类,继承java.lang包下的Thread类2.重写run方法3.将要在线程中执行的代码编写在run方法中4.创建上面自定义类的对象5.调用start方法启动线程packagecom.sutaoyu.Thread;//1.自定... 查看详情

javascript定义类(class)的三种方法

将近20年前,Javascript诞生的时候,只是一种简单的网页脚本语言。如果你忘了填写用户名,它就跳出一个警告。如今,它变得几乎无所不能,从前端到后端,有着各种匪夷所思的用途。程序员用它完成越来越庞大的项目。Javascrip... 查看详情

细说mysql创建表的三种方法(代码片段)

...数据表;MySQL则实现了三种创建表的方法,支持自定义表结构或者通过复制已有的表结构来创建新表,本文给大家分别介绍一下这些方法的使用和注意事项。如果 查看详情

django中创建对象的三种方法(代码片段)

一、直接创建对象person=Person()person.p_name=‘mozili‘person.save()二、使用create()函数创建person=Person.objects.create(p_name=‘limozi‘,p_age=20,p_sex=False)person.save()returnHttpResponse(‘创建成功!‘)三、在模型中自定义函数创建对象model 查看详情

我熟知的三种三栏网页宽度自适应布局方法

一、前言在如今各个分辨率显示器N足鼎立的时期,页面采用流动性布局(亦可称自适应布局)不失为一个好选择。当然,具体实现不是那么容易,需要一定的css功力和实践经验。本文不讲细节,只讲外部的自适应架构,这也是... 查看详情

vue的三种通信方式(代码片段)

...组件使用父组件数据渲染)a)在子组件中添加propsprops:[自定义prop名字]b)在子组件中把自定义prop当data使用即可c)找到父组件模板中的子组件标签,添加属性<子组件标签:自定义prop名字="父组件data中的数据"></子组件标签>&nb... 查看详情

android常用对话框大全自定义dialog的三种方式(theme,popupwindow,dialog)(代码片段)

AndroidDialog自定义的方式有很多,例如:Theme、Dialog、PopupWindow、AlertDialog、View等方式。Android常用对话框系列文章【Android】常用对话框大全(一)AndroidDialog【Android】常用对话框大全(二)MaterialDialog【Android... 查看详情

自定义动态组件,剩下的三种周期函数(代码片段)

周期函数在vue中有一个生命周期,它可以收集子孙组件中的错误信息,进行捕获err具体的错误此信息是最为关键vm发生错误的虚拟dom对象info相关提示信息errorCaptured(err,vm,info)console.log(err,vm,info)子孙组件错误的捕获,它... 查看详情