Hàm VBA sắp xếp mảng 2 chiều (sort 2D array) dùng Arraylist và Split [Chia sẻ]

Liên hệ QC

Maika8008

Thành viên gạo cội
Tham gia
12/6/20
Bài viết
4,741
Được thích
5,669
Donate (Momo)
Donate
Giới tính
Nam
Sau khi được gợi ý của @HieuCD về việc dùng Arraylist từ các bài viết của @befaint, tôi cố vận dụng mọi thứ mà tôi có về VBA để giải quyết việc dùng Arraylist (nguyên là để sắp xếp mảng 1 chiều) để sắp xếp mảng 2 chiều. Tất nhiên với khả năng tay ngang của tôi thì chưa đi đến tận cùng của vấn đề nên đôi chỗ sắp xếp vẫn không như ý, không đúng trình tự sắp xếp của Excel. Tôi chia sẻ lên đây mong nhận nhiều góp ý để hoàn thiện code.

Có 2 hàm: 1 cho việc sắp xếp 1 cột và 1 cho việc sắp thêm cột thứ 2. Hàm 2 gọi hàm 1 cho tiện (thiển ý của tôi là vậy). Code chạy nhanh do tận dụng được tốc độ của Arraylist và hàm Split.

Hàm này không dùng để gõ trực tiếp trên trang tính được mà chỉ dùng làm công cụ trung gian trong lập trình xử lý các vấn đề có liên quan đến mảng dữ liệu. File ví dụ kèm theo dành cho ai muốn thử hàm.

Update: Tôi đã giải quyết vấn đề sắp xếp cột số không cùng kích thước.

Mã:
Function SortInArray(ByRef SortArray As Variant, Optional lngColumn As Long = 0, _
                   Optional ByVal Order As Boolean = True)
    On Error Resume Next
    Dim arrTemp, arrRsl, arrRsl2
    Dim ArrList As Object, i As Long, j As Long, k As Long, m As Long, NumVal As Double
    Set ArrList = CreateObject("System.Collections.ArrayList")
    arrTemp = SortArray
    ReDim Preserve arrTemp(1 To UBound(SortArray, 1), 1 To UBound(SortArray, 2) + 1)
    For i = LBound(SortArray, 1) To UBound(SortArray, 1)
        arrTemp(i, UBound(SortArray, 2) + 1) = IIf(IsNumeric(SortArray(i, lngColumn)), Right("00000" & SortArray(i, lngColumn), 6), SortArray(i, lngColumn)) & "|"   'Update 13h58 -10/5/21
        For j = LBound(SortArray, 2) To lngColumn - 1
            arrTemp(i, UBound(SortArray, 2) + 1) = arrTemp(i, UBound(SortArray, 2) + 1) & SortArray(i, j) & "|"
        Next
        For j = lngColumn + 1 To UBound(SortArray, 2)
            arrTemp(i, UBound(SortArray, 2) + 1) = arrTemp(i, UBound(SortArray, 2) + 1) & SortArray(i, j) & "|"
        Next
        ArrList.Add CStr(arrTemp(i, UBound(SortArray, 2) + 1))
    Next
    ArrList.Sort
    If Order = False Then ArrList.Reverse
    ReDim arrRsl2(1 To UBound(SortArray, 1), 1 To UBound(SortArray, 2))
    For i = LBound(SortArray, 1) - 1 To UBound(SortArray, 1) - 1
        arrRsl = Split(ArrList.Item(i), "|")
        For j = LBound(SortArray, 2) To lngColumn - 1
            arrRsl2(i + 1, j) = arrRsl(j)
        Next
        arrRsl2(i + 1, lngColumn) = arrRsl(0)
        For j = lngColumn + 1 To UBound(SortArray, 2)
            arrRsl2(i + 1, j) = arrRsl(j - 1)
        Next
    Next
    SortInArray = arrRsl2
    Set ArrList = Nothing
End Function

Function SortInArray2(ByRef SortArray As Variant, Optional lngColumn As Long = 0, Optional Order As Boolean = True, _
                      Optional lngColumn2 As Long = 0, Optional Order2 As Boolean = True)
Dim SortArr As Variant
Dim i As Long, j As Long, k As Long, D As Long, r As Long
    If lngColumn = 0 Then lngColumn = 1
    SortArray = SortInArray(SortArray, lngColumn, Order)
    ReDim TempArray(1 To UBound(SortArray, 1), 1 To UBound(SortArray, 2))
    For i = LBound(SortArray, 1) To UBound(SortArray, 1)
        k = k + 1
        On Error Resume Next
        If SortArray(i, lngColumn) <> SortArray(i + 1, lngColumn) Then
            If Err.Number = 9 Then
                On Error GoTo 0
                GoTo Sort
            End If
Sort:       If k > 1 Then
                ReDim SortArr(1 To k, 1 To UBound(SortArray, 2))
                For r = 1 To k
                    For j = LBound(SortArray, 2) To UBound(SortArray, 2)
                        SortArr(r, j) = SortArray(i - k + r, j)
                    Next
                Next
                SortArr = SortInArray(SortArr, lngColumn2, Order2)
                For D = i - k + 1 To i
                    For j = LBound(SortArray, 2) To UBound(SortArray, 2)
                        SortArray(D, j) = SortArr(D + k - i, j)
                    Next
                Next
                k = 0
            Else
                k = 0
            End If
        End If
    Next
    SortInArray2 = SortArray
End Function
 

File đính kèm

  • Danh so Thu tu_VD.xlsm
    24.1 KB · Đọc: 27
Lần chỉnh sửa cuối:
Sau khi được gợi ý của @HieuCD về việc dùng Arraylist từ các bài viết của @befaint, tôi cố vận dụng mọi thứ mà tôi có về VBA để giải quyết việc dùng Arraylist (nguyên là để sắp xếp mảng 1 chiều) để sắp xếp mảng 2 chiều. Tất nhiên với khả năng tay ngang của tôi thì chưa đi đến tận cùng của vấn đề nên sắp xếp cột số không cùng kích thước vẫn không như ý. Tôi chia sẻ lên đây mong nhận nhiều góp ý để hoàn thiện code.

Có 2 hàm: 1 cho việc sắp xếp 1 cột và 1 cho việc sắp thêm cột thứ 2. Hàm 2 gọi hàm 1 cho tiện (thiển ý của tôi là vậy). Code chạy nhanh do tận dụng được tốc độ của Arraylist và hàm Split.

Hàm này không dùng để gõ trực tiếp trên trang tính được mà chỉ dùng làm công cụ trung gian trong lập trình xử lý các vấn đề có liên quan đến mảng dữ liệu. File ví dụ kèm theo dành cho ai muốn thử hàm.
Mã:
Function SortInArray(ByRef SortArray As Variant, Optional lngColumn As Long = 0, _
                   Optional ByVal Order As Boolean = True)
    On Error Resume Next
    Dim arrTemp, arrRsl, arrRsl2
    Dim ArrList As Object, i As Long, j As Long, k As Long, m As Long, NumVal As Double
    Set ArrList = CreateObject("System.Collections.ArrayList")
    arrTemp = SortArray
    ReDim Preserve arrTemp(1 To UBound(SortArray, 1), 1 To UBound(SortArray, 2) + 1)
    For i = LBound(SortArray, 1) To UBound(SortArray, 1)
        arrTemp(i, UBound(SortArray, 2) + 1) = SortArray(i, lngColumn) & "|"
        For j = LBound(SortArray, 2) To lngColumn - 1
            arrTemp(i, UBound(SortArray, 2) + 1) = arrTemp(i, UBound(SortArray, 2) + 1) & SortArray(i, j) & "|"
        Next
        For j = lngColumn + 1 To UBound(SortArray, 2)
            arrTemp(i, UBound(SortArray, 2) + 1) = arrTemp(i, UBound(SortArray, 2) + 1) & SortArray(i, j) & "|"
        Next
        ArrList.Add CStr(arrTemp(i, UBound(SortArray, 2) + 1))
    Next
    ArrList.Sort
    If Order = False Then ArrList.Reverse
    ReDim arrRsl2(1 To UBound(SortArray, 1), 1 To UBound(SortArray, 2))
    For i = LBound(SortArray, 1) - 1 To UBound(SortArray, 1) - 1
        arrRsl = Split(ArrList.Item(i), "|")
        For j = LBound(SortArray, 2) To lngColumn - 1
            arrRsl2(i + 1, j) = arrRsl(j)
        Next
        arrRsl2(i + 1, lngColumn) = arrRsl(0)
        For j = lngColumn + 1 To UBound(SortArray, 2)
            arrRsl2(i + 1, j) = arrRsl(j - 1)
        Next
    Next
    SortInArray = arrRsl2
    Set ArrList = Nothing
End Function

Function SortInArray2(ByRef SortArray As Variant, Optional lngColumn As Long = 0, Optional Order As Boolean = True, _
                      Optional lngColumn2 As Long = 0, Optional Order2 As Boolean = True)
Dim SortArr As Variant
Dim i As Long, j As Long, k As Long, D As Long, r As Long
    If lngColumn = 0 Then lngColumn = 1
    SortArray = SortInArray(SortArray, lngColumn, Order)
    ReDim TempArray(1 To UBound(SortArray, 1), 1 To UBound(SortArray, 2))
    For i = LBound(SortArray, 1) To UBound(SortArray, 1)
        k = k + 1
        On Error Resume Next
        If SortArray(i, lngColumn) <> SortArray(i + 1, lngColumn) Then
            If Err.Number = 9 Then
                On Error GoTo 0
                GoTo Sort
            End If
Sort:       If k > 1 Then
                ReDim SortArr(1 To k, 1 To UBound(SortArray, 2))
                For r = 1 To k
                    For j = LBound(SortArray, 2) To UBound(SortArray, 2)
                        SortArr(r, j) = SortArray(i - k + r, j)
                    Next
                Next
                SortArr = SortInArray(SortArr, lngColumn2, Order2)
                For D = i - k + 1 To i
                    For j = LBound(SortArray, 2) To UBound(SortArray, 2)
                        SortArray(D, j) = SortArr(D + k - i, j)
                    Next
                Next
                k = 0
            Else
                k = 0
            End If
        End If
    Next
    SortInArray2 = SortArray
End Function
Bạn xem giúp lỗi này với

1620627715676.png
 
Upvote 0
Bạn xem bài này thử có giúp ích được gì không.
"Hệ thống phải cài đặt .NET Framework v1.1 trở lên."

Chứ theo như tôi thấy máy tôi không cần thư viện gì thêm
 
Upvote 0
Chả có cái máy tính hệ điều hành Windows nào lại không có sẵn .NET Framework cả.
Windows 98 còn có nữa là.
Bó chiếu.
 
Upvote 0
Ôi ArrayList hay thế mà sử dụng bất tiện vậy nhỉ.
Nếu bạn có hứng thú với dạng code này thì hãy tham khảo đoạn code này.
Dạng code này chỉ viết để tập thể dục cho bộ não thôi chứ không ứng dụng nhiều và cũng không quá cần thiết.
Nếu cần cứ chèn thêm một sheet tạm, sort trên sheet rồi lấy dữ liệu lên xử lý tiếp, xóa sheet tạm....
Mã:
Sub ArraySort()
Dim Data(), Temp As String
Dim FirsrtRow As Long, FirstCol As String, SortOrder()
Dim TotalCols As Byte, Row As Long, J As Long
SortOrder = Array(1, 2) 'Thay đổi chỗ này để chọn những cột và thứ tự sort'
With Sheets("Nguon")
   Data = .Range("A3", .[M65536].End(3)).Value
End With
TotalCols = UBound(Data, 2)
ReDim Preserve Data(1 To UBound(Data), 1 To (TotalCols + 1))
For Row = 1 To UBound(Data, 1)
   For J = 0 To UBound(SortOrder)
      If IsDate(Data(Row, SortOrder(J))) Then
         Temp = Temp & CLng(Data(Row, SortOrder(J)))
      Else
         Temp = Temp & Space(2) & Format(Data(Row, SortOrder(J)), String(15, "0"))
      End If
   Next
   Data(Row, TotalCols + 1) = Temp
   Temp = Empty
Next
QuickSort Data, LBound(Data), UBound(Data)
Sheets("Dich").[A3].Resize(UBound(Data), TotalCols) = Data
End Sub
 

File đính kèm

  • SortInArrFinal.xlsm
    40.4 KB · Đọc: 27
Upvote 0
Nếu bạn có hứng thú với dạng code này thì hãy tham khảo đoạn code này.
Dạng code này chỉ viết để tập thể dục cho bộ não thôi chứ không ứng dụng nhiều và cũng không quá cần thiết.
Nếu cần cứ chèn thêm một sheet tạm, sort trên sheet rồi lấy dữ liệu lên xử lý tiếp, xóa sheet tạm....
Mã:
Sub ArraySort()
Dim Data(), Temp As String
Dim FirsrtRow As Long, FirstCol As String, SortOrder()
Dim TotalCols As Byte, Row As Long, J As Long
SortOrder = Array(1, 2) 'Thay đổi chỗ này để chọn những cột và thứ tự sort'
With Sheets("Nguon")
   Data = .Range("A3", .[M65536].End(3)).Value
End With
TotalCols = UBound(Data, 2)
ReDim Preserve Data(1 To UBound(Data), 1 To (TotalCols + 1))
For Row = 1 To UBound(Data, 1)
   For J = 0 To UBound(SortOrder)
      If IsDate(Data(Row, SortOrder(J))) Then
         Temp = Temp & CLng(Data(Row, SortOrder(J)))
      Else
         Temp = Temp & Space(2) & Format(Data(Row, SortOrder(J)), String(15, "0"))
      End If
   Next
   Data(Row, TotalCols + 1) = Temp
   Temp = Empty
Next
QuickSort Data, LBound(Data), UBound(Data)
Sheets("Dich").[A3].Resize(UBound(Data), TotalCols) = Data
End Sub
File này của bạn trước đây khi tìm hiểu tôi có biết đến và đã tải về và xem rồi nhưng do không hiểu cách dùng và nhu cầu của tôi là sắp xếp nhiều cột (mà siêu cao thủ gọi là tầng thì phải) theo các lựa chọn tăng hoặc giảm.. không biết cách làm của bạn vận hành theo cơ chế thế nào vậy?
Bài đã được tự động gộp:

Trước đây tôi có quan tâm đến vấn đề và đã tìm kiếm cũng thấy chủ đề này, nhưng không tải được file về:
Bạn nào có up lại giúp mình với,
cảm ơn
 
Lần chỉnh sửa cuối:
Upvote 0
Cập nhật:
Theo cách sắp xếp phổ biến của các thành viên khác trên GPE: sắp nhiều cột, lựa chọn mảng nguồn có tiêu đề hoặc không.
Hiện tại kết quả trả về là mảng không có tiêu đề.

Dữ liệu đủ để chạy và sub thử hàm có trong file đính kèm
 

File đính kèm

  • Test_SortArray2D.xlsm
    72.3 KB · Đọc: 19
Upvote 0
Cập nhật:
Theo cách sắp xếp phổ biến của các thành viên khác trên GPE: sắp nhiều cột, lựa chọn mảng nguồn có tiêu đề hoặc không.
Hiện tại kết quả trả về là mảng không có tiêu đề.

Dữ liệu đủ để chạy và sub thử hàm có trong file đính kèm
Dữ liệu số giới hạn 5 chữ số e rằng hơi thiếu
Các cột không liên quan đến cột sort bạn cho vào SortArr sẽ làm chậm tốc độ
Mình tạo sheet3 để bạn so sánh kết quả của code và sort của Excel

Chạy sub test trong sheet3 sao đó sort thủ công trong sheet
Mã:
Sub TESTSort()
    Dim Arr, arr2
    Sheet3.Range("H12:L20").ClearContents
    Arr = Sheet3.Range("A1:E7")
    Arr = SortInArray2D(Arr, True, 5, 1)
    Sheet3.Range("H12").Resize(UBound(Arr, 1), UBound(Arr, 2)) = Arr
End Sub

Sub TESTSort2()
    Dim Arr, arr2
    Sheet3.Range("H12:L20").ClearContents
    Arr = Sheet3.Range("A1:E9")
    Arr = SortInArray2D(Arr, True, 5, 1)
    Sheet3.Range("H12").Resize(UBound(Arr, 1), UBound(Arr, 2)) = Arr
End Sub
 

File đính kèm

  • Test_SortArray2D.xlsm
    75 KB · Đọc: 9
Upvote 0
Cập nhật:
- Sắp xếp dữ liệu số đến 12 chữ số
- Sửa code theo gợi ý thứ 2 của bài #11 (chỉ cải thiện tốc độ chừng 1-2%)
- Chỉnh sửa để sắp xếp được các lỗi #N/A, #NAME... (sắp được và hiển thị kiểu Error 2049... chứ không hiện #N/A,... được)
 

File đính kèm

  • Test_SortArray2D.xlsm
    86.1 KB · Đọc: 25
Upvote 0
Các bác cho e hỏi Code của bác MaiKa vs code của bác HieuCD trong bài
Mã:
https://www.giaiphapexcel.com/diendan/threads/function-sort-m%E1%BA%A3ng-2-chi%E1%BB%81u.155499/
này thì code nào cho tốc độ nhanh hơn vậy các bác?
 
Upvote 0
Các bác cho e hỏi Code của bác MaiKa vs code của bác HieuCD trong bài
Mã:
https://www.giaiphapexcel.com/diendan/threads/function-sort-m%E1%BA%A3ng-2-chi%E1%BB%81u.155499/
này thì code nào cho tốc độ nhanh hơn vậy các bác?
Bạn đè (code) 2 ổng ra kiểm tra liền luôn đi bạn, dùng timer xem như thế nào rồi chia sẻ lên đây để mọi người có thêm thông tin.
 
Upvote 0
. . . . .
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT
Back
Top Bottom