Sort trong mảng Arr (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

NguyenthiH

Thành viên mới đăng ký
Tham gia
11/12/16
Bài viết
965
Được thích
175
Giới tính
Nữ
Em có file có dữ liệu Sheet1!A2:D, Thầy Ba Tê đã giúp em code lọc không trùng bằng DIC ra Mảng Arr, rồi nạp Mảng Arr đó vào ListBox, nhưng chưa Sort tăng dần. Bây giờ em muốn mọi người giúp em Sort mảng Arr tăng dần rồi nạp vào ListBox.
Em cám ơn.
ps: click C29 của sheet!Nhap sẽ hiện Form
 

File đính kèm

Em có file có dữ liệu Sheet1!A2:D, Thầy Ba Tê đã giúp em code lọc không trùng bằng DIC ra Mảng Arr, rồi nạp Mảng Arr đó vào ListBox, nhưng chưa Sort tăng dần. Bây giờ em muốn mọi người giúp em Sort mảng Arr tăng dần rồi nạp vào ListBox.
Em cám ơn.
ps: click C29 của sheet!Nhap sẽ hiện Form
Lập thớt mới thì nêu rõ yêu cầu.

Sắp xếp tăng dần như nào? điều kiện sắp xếp ở cột nào? "Tăng dần" ở đây hiểu là A-Z hay min-max?
 
Upvote 0
Dạ Sắp Xếp tăng dần cột tên hàng hóa đó Anh befaint (theo Alphabel)
 
Upvote 0
Em không rành, nhưng xếp tên hàng hóa trong Form theo thứ tự A,B,C,D...v...v.vv. đó Anh!
như Bánh -> Bột..-> Dầu.....
 
Lần chỉnh sửa cuối:
Upvote 0
(1) code lọc không trùng bằng DIC ra Mảng Arr, (2)rồi nạp Mảng Arr đó vào ListBox, nhưng chưa Sort tăng dần.
Bây giờ em muốn mọi người giúp em Sort mảng Arr tăng dần rồi nạp vào ListBox.
Trình độ như mình, thì mình làm vầy:

Sau (1), mình nạp mảng lên trang tính (1.1)

2. Sắp xếp diễn ra trên trang tính

3. Lấy vùng dữ liệu đã sắp xếp làm nguồn cho ListBox

Hay Nạp vô mảng lại để làm nguồn cho ListBox


Nếu không muốn vòng vô & thô sơ thì tìm trên diễn đàn có bài sắp xếp trong mảng.

Chúc sớm thành công.
 
Upvote 0
Đây là yêu cầu không đơn giản vì tác giả yêu cầu sort theo tên hàng bằng tiếng Việt, kể cả sort trên sheet cũng không có kết quả mong muốn.
Đã sort tiếng Việt thì nên tham khảo các đề tài có liên quan trên GPE.
 
Upvote 0
Em thấy giống hàm Filter2DArray của Thầy Ndu ở bài này.
Nhưng em chả biết làm.
Mong AC gán vào File dùm em ah!
 
Upvote 0
Em có file có dữ liệu Sheet1!A2:D, Thầy Ba Tê đã giúp em code lọc không trùng bằng DIC ra Mảng Arr, rồi nạp Mảng Arr đó vào ListBox, nhưng chưa Sort tăng dần. Bây giờ em muốn mọi người giúp em Sort mảng Arr tăng dần rồi nạp vào ListBox.
Em cám ơn.
ps: click C29 của sheet!Nhap sẽ hiện Form
tạo dùm bạn list đã sort, còn dùng thế nào thì không biết-=09=
 

File đính kèm

Upvote 0
Cám Ơn Anh HieuCD, Anh đã giúp em nhiều lần.
Chúc Anh ngày Vui!
 
Upvote 0
Cám Ơn Anh HieuCD, Anh đã giúp em nhiều lần.
Chúc Anh ngày Vui!
thấy đồ ăn ngon quá nên mới tham gia, không biết có phần không --=0
dữ liệu tạo list không ổn, nếu mới nhập cho những dòng đầu thì không có những sản phẩm mới, bạn phải có chổ lưu trử toàn bộ danh sách các sản phẩm làm căn cứ tạo list
 
Upvote 0
Cám ơn Anh nhiều!
Ý Anh nói em không hiểu
dữ liệu tạo list không ổn, nếu mới nhập cho những dòng đầu thì không có những sản phẩm mới, bạn phải có chổ lưu trử toàn bộ danh sách các sản phẩm làm căn cứ tạo list
mà code Anh hay thiệt, chỉ có một dòng là Sort luôn List
Mã:
Slist.Sort
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn Anh nhiều!
Ý Anh nói em không hiểu

mà code Anh hay thiệt, chỉ có một dòng là Sort luôn List
Mã:
Slist.Sort

Cái đó người ta dùng ArrayList và bản thân nó có chức năng này. Tuy nhiên, thấy dễ ăn vậy chứ thật ra chỉ áp dụng được bài của bạn, List đã được lọc duy nhất rồi nên sort xong còn "mò mò" ra được các phần tử ở cột khác chứ nếu để nguyên cái list chưa lọc duy nhất thì đố Arraylist mần được bài này
Mà cũng thấy lạ thật! Bạn HieuCD dùng ArrayList, bản thân nó cũng có chức năng lọc duy nhất, vậy sao phải thêm Dictionary vào chẳng hóa ra.. thừa???
----------------------
Tôi giải quyết bài toán theo hướng tổng quát: Mặc kệ danh sách có duy nhất hay không vẫn cứ sort được
1> Code trong Module:
Mã:
Function Sort2DArray(ByVal SourceArray, ByVal ColIndex As Long, ByVal Order As Boolean, ByVal HasTitle As Boolean)
  Dim tmpArr, i As Long, j As Long, SortArr, Item1, Item2, firstVal As Double
  Dim Arr, iR As Long, tmp(), n As Long, Chk As Boolean
  On Error Resume Next
  tmpArr = SourceArray: Arr = tmpArr
  ColIndex = ColIndex + LBound(tmpArr, 2) - 1
  firstVal = CDbl(tmpArr(LBound(tmpArr, 1) - HasTitle, ColIndex))
  Chk = firstVal > 0
  For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
    ReDim Preserve tmp(n)
    If Chk Then
      tmp(n) = CDbl(tmpArr(i, ColIndex))
    Else
      tmp(n) = CStr(tmpArr(i, ColIndex))
    End If
    n = n + 1
  Next
  SortArr = Sort1DArray(tmp, Order)
  With CreateObject("Scripting.Dictionary")
    For i = LBound(SortArr) To UBound(SortArr)
      If Chk Then
        If Not .Exists(CDbl(SortArr(i))) Then .Add CDbl(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      Else
        If Not .Exists(CStr(SortArr(i))) Then .Add CStr(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      End If
    Next
    For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
      If Chk Then
        iR = .Item(CDbl(tmpArr(i, ColIndex)))
      Else
        iR = .Item(CStr(tmpArr(i, ColIndex)))
      End If
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(iR, j) = tmpArr(i, j)
      Next
      If Chk Then
        .Item(CDbl(tmpArr(i, ColIndex))) = iR + 1
      Else
        .Item(CStr(tmpArr(i, ColIndex))) = iR + 1
      End If
    Next
  End With
  Sort2DArray = Arr
End Function
Function Sort1DArray(ByVal srcArr, ByVal Order As Boolean)
  Dim Item, tmpArr, Arr1, Arr2, n As Long, m As Long
  tmpArr = srcArr
  With CreateObject("System.Collections.ArrayList")
    For Each Item In tmpArr
      .Add Item
    Next
    .Sort
    Arr1 = .ToArray
    If Order = False Then
      ReDim Arr2(LBound(Arr1) To UBound(Arr1))
      For n = .Count To 1 Step -1
        Arr2(m) = Arr1(n - 1)
        m = m + 1
      Next
      Sort1DArray = Arr2
    Else
      Sort1DArray = Arr1
    End If
  End With
End Function
Function Unique2DArray(ByVal SourceArray, Optional ByVal ColIndex As Variant = 1)
  Dim aSrc, aCol, tmp As String, cItem
  Dim lR As Long, lC As Long, lUB1 As Long, lUB2 As Long, lLB1 As Long, lLB2 As Long, n As Long
  Dim dic As Object
  Set dic = CreateObject("Scripting.Dictionary")
  aSrc = SourceArray: aCol = ColIndex
  If Not IsArray(aCol) Then aCol = Array(aCol)
  lLB1 = LBound(aSrc, 1): lLB2 = LBound(aSrc, 2)
  lUB1 = UBound(aSrc, 1): lUB2 = UBound(aSrc, 2)
  ReDim aDes(1 To lUB2 + 1 - lLB2, 1 To 1)
  For lR = lLB1 To lUB1
    tmp = vbNullString
    If Len(CStr(aSrc(lR, 1))) Then
      For Each cItem In aCol
        tmp = tmp & vbBack & aSrc(lR, cItem)
      Next
      If Not dic.Exists(tmp) Then
        n = n + 1
        dic.Add tmp, lR
        ReDim Preserve aDes(1 To lUB2 + 1 - lLB2, 1 To n)
        For lC = lLB2 To lUB2
          aDes(lC, n) = aSrc(lR, lC)
        Next
      End If
    End If
  Next
  If n Then Unique2DArray = Transpose2DArray(aDes)
End Function
Function Transpose2DArray(ByVal SourceArray)
  Dim aSrc
  Dim lR As Long, lC As Long
  aSrc = SourceArray
  ReDim aDes(LBound(aSrc, 2) To UBound(aSrc, 2), LBound(aSrc, 1) To UBound(aSrc, 1))
  For lR = LBound(aSrc, 1) To UBound(aSrc, 1)
    For lC = LBound(aSrc, 2) To UBound(aSrc, 2)
      aDes(lC, lR) = aSrc(lR, lC)
    Next
  Next
  Transpose2DArray = aDes
End Function
2> Code trong Form:
Mã:
[COLOR=#ff0000]Private Sub Label1_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 1, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub Label2_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 2, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub UserForm_Initialize()
  Dim aSrc, aDes
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  aDes = Unique2DArray(aSrc, 1)
  ListBox1.List = aDes
End Sub[/COLOR]
Code trong Module tuy dài nhưng bạn mặc kệ nó đi, chỉ cần biết áp dụng hàm vào form là đủ (xem và khai triển chỗ màu đỏ là được rồi)
Lưu ý: Ở đây tôi viết cho bạn chức năng sort theo cách bạn bấm vào Label TÊN HÀNG hoặc ĐVT thì nó sẽ sort
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Cám Ơn Thầy Ndu!
Thầy có thể chỉnh dùm code để chọn trong ListBox bang phím lên xuống và nhấn enter thì nạp vào bang tính được không Thầy!
Như file này của Thầy,nhưng Enter thay vì Double Click
http://www.giaiphapexcel.com/forum/content.php?623-Tạo-ứng-dụng-form-hổ-trợ-tìm-kiếm-và-nhập-liệu

Cái nào ra cái đó! Sau khi xong topic này rồi bạn có thể hỏi vấn đề khác ở topic khác
 
Upvote 0
Topic này tới đây là hết rồi đó Thầy ndu Ơi!
Tại vì em có đọc bài "Tạo Ứng dung Form hỗ trợ tìm kiếm và nhập lieu" và em thấy trong file Thầy nạp list cho ListBox là danh sách không trùng, nên em có mở Topic "Lọc không trùng bang Dic" và được anh Hoang2013 và Thầy Ba Tê giúp.
Va Topic "Sort trong mảng Arr" và anh HieuCD và Thầy Ndu giúp.
Vậy em nhờ Thầy giúp chỉnh code để chọn trongListBox bang bàn phím (Lên Xuống) và nhấn Enter thì gán vào bang tính.
 
Lần chỉnh sửa cuối:
Upvote 0
Cái đó người ta dùng ArrayList và bản thân nó có chức năng này. Tuy nhiên, thấy dễ ăn vậy chứ thật ra chỉ áp dụng được bài của bạn, List đã được lọc duy nhất rồi nên sort xong còn "mò mò" ra được các phần tử ở cột khác chứ nếu để nguyên cái list chưa lọc duy nhất thì đố Arraylist mần được bài này
Mà cũng thấy lạ thật! Bạn HieuCD dùng ArrayList, bản thân nó cũng có chức năng lọc duy nhất, vậy sao phải thêm Dictionary vào chẳng hóa ra.. thừa???
----------------------
Tôi giải quyết bài toán theo hướng tổng quát: Mặc kệ danh sách có duy nhất hay không vẫn cứ sort được
1> Code trong Module:
Mã:
Function Sort2DArray(ByVal SourceArray, ByVal ColIndex As Long, ByVal Order As Boolean, ByVal HasTitle As Boolean)
  Dim tmpArr, i As Long, j As Long, SortArr, Item1, Item2, firstVal As Double
  Dim Arr, iR As Long, tmp(), n As Long, Chk As Boolean
  On Error Resume Next
  tmpArr = SourceArray: Arr = tmpArr
  ColIndex = ColIndex + LBound(tmpArr, 2) - 1
  firstVal = CDbl(tmpArr(LBound(tmpArr, 1) - HasTitle, ColIndex))
  Chk = firstVal > 0
  For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
    ReDim Preserve tmp(n)
    If Chk Then
      tmp(n) = CDbl(tmpArr(i, ColIndex))
    Else
      tmp(n) = CStr(tmpArr(i, ColIndex))
    End If
    n = n + 1
  Next
  SortArr = Sort1DArray(tmp, Order)
  With CreateObject("Scripting.Dictionary")
    For i = LBound(SortArr) To UBound(SortArr)
      If Chk Then
        If Not .Exists(CDbl(SortArr(i))) Then .Add CDbl(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      Else
        If Not .Exists(CStr(SortArr(i))) Then .Add CStr(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      End If
    Next
    For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
      If Chk Then
        iR = .Item(CDbl(tmpArr(i, ColIndex)))
      Else
        iR = .Item(CStr(tmpArr(i, ColIndex)))
      End If
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(iR, j) = tmpArr(i, j)
      Next
      If Chk Then
        .Item(CDbl(tmpArr(i, ColIndex))) = iR + 1
      Else
        .Item(CStr(tmpArr(i, ColIndex))) = iR + 1
      End If
    Next
  End With
  Sort2DArray = Arr
End Function
Function Sort1DArray(ByVal srcArr, ByVal Order As Boolean)
  Dim Item, tmpArr, Arr1, Arr2, n As Long, m As Long
  tmpArr = srcArr
  With CreateObject("System.Collections.ArrayList")
    For Each Item In tmpArr
      .Add Item
    Next
    .Sort
    Arr1 = .ToArray
    If Order = False Then
      ReDim Arr2(LBound(Arr1) To UBound(Arr1))
      For n = .Count To 1 Step -1
        Arr2(m) = Arr1(n - 1)
        m = m + 1
      Next
      Sort1DArray = Arr2
    Else
      Sort1DArray = Arr1
    End If
  End With
End Function
Function Unique2DArray(ByVal SourceArray, Optional ByVal ColIndex As Variant = 1)
  Dim aSrc, aCol, tmp As String, cItem
  Dim lR As Long, lC As Long, lUB1 As Long, lUB2 As Long, lLB1 As Long, lLB2 As Long, n As Long
  Dim dic As Object
  Set dic = CreateObject("Scripting.Dictionary")
  aSrc = SourceArray: aCol = ColIndex
  If Not IsArray(aCol) Then aCol = Array(aCol)
  lLB1 = LBound(aSrc, 1): lLB2 = LBound(aSrc, 2)
  lUB1 = UBound(aSrc, 1): lUB2 = UBound(aSrc, 2)
  ReDim aDes(1 To lUB2 + 1 - lLB2, 1 To 1)
  For lR = lLB1 To lUB1
    tmp = vbNullString
    If Len(CStr(aSrc(lR, 1))) Then
      For Each cItem In aCol
        tmp = tmp & vbBack & aSrc(lR, cItem)
      Next
      If Not dic.Exists(tmp) Then
        n = n + 1
        dic.Add tmp, lR
        ReDim Preserve aDes(1 To lUB2 + 1 - lLB2, 1 To n)
        For lC = lLB2 To lUB2
          aDes(lC, n) = aSrc(lR, lC)
        Next
      End If
    End If
  Next
  If n Then Unique2DArray = Transpose2DArray(aDes)
End Function
Function Transpose2DArray(ByVal SourceArray)
  Dim aSrc
  Dim lR As Long, lC As Long
  aSrc = SourceArray
  ReDim aDes(LBound(aSrc, 2) To UBound(aSrc, 2), LBound(aSrc, 1) To UBound(aSrc, 1))
  For lR = LBound(aSrc, 1) To UBound(aSrc, 1)
    For lC = LBound(aSrc, 2) To UBound(aSrc, 2)
      aDes(lC, lR) = aSrc(lR, lC)
    Next
  Next
  Transpose2DArray = aDes
End Function
2> Code trong Form:
Mã:
[COLOR=#ff0000]Private Sub Label1_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 1, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub Label2_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 2, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub UserForm_Initialize()
  Dim aSrc, aDes
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  aDes = Unique2DArray(aSrc, 1)
  ListBox1.List = aDes
End Sub[/COLOR]
Code trong Module tuy dài nhưng bạn mặc kệ nó đi, chỉ cần biết áp dụng hàm vào form là đủ (xem và khai triển chỗ màu đỏ là được rồi)
Lưu ý: Ở đây tôi viết cho bạn chức năng sort theo cách bạn bấm vào Label TÊN HÀNG hoặc ĐVT thì nó sẽ sort

Đúng Thầy Ndu là bậc thầy, luôn luôn chỉ rõ người chưa biết hiểu 1 cách tỷ mỉ và khác người.

Trân trong.

Ngoctoan
 
Upvote 0
Trong Excel gọi là sort Ascending dù cho là text, number, date hay logic. Không gọi là tăng dần thì tôi cũng chả biết gọi sao.
Em thì hiểu từ ngữ đơn giản: Tăng dần tức là từ thấp tới cao, mà thấp với cao là đối chiếu với gốc quy ước để phân mức độ là thấp hay cao (nhỏ hay lớn), áp dụng với con số.

Trong chức năng Excel: "Sort A to Z" có ghi chú thêm "Lowest to highest".
Sort A to Z, tức là căn cứ vào thứ tự các chữ cái trong bảng chữ cái ABC (thứ tự này đã được quy ước chung) để sắp xếp.

Còn chủ thớt viết yêu cầu (bài #1):
nên em chỉ hiểu là áp dụng với con số.

Với trường hợp của chủ topic còn phải xem quy ước thứ tự sắp xếp là như nào: ký tự đặc biệt, số, chữ hoa, chữ thường...
 
Upvote 0
Em thì hiểu từ ngữ đơn giản: Tăng dần tức là từ thấp tới cao, mà thấp với cao là đối chiếu với gốc quy ước để phân mức độ là thấp hay cao (nhỏ hay lớn), áp dụng với con số.

Trong chức năng Excel: "Sort A to Z" có ghi chú thêm "Lowest to highest".
Sort A to Z, tức là căn cứ vào thứ tự các chữ cái trong bảng chữ cái ABC (thứ tự này đã được quy ước chung) để sắp xếp.

Lowest to highest = từ thấp đến cao, không tăng thì làm sao lên cao?
Bổ sung: Ascending và Descending là từ khóa sắp xếp trong VBA của Excel, xin lỗi bài trên nói không rõ.


Sort A to Z, tức là căn cứ vào thứ tự các chữ cái trong bảng chữ cái ABC (thứ tự này đã được quy ước chung) để sắp xếp.
Hai nút nhấn sort nhanh trên ribbon có biểu tượng AZ và ZA, dùng chung cho cả text và number chứ không riêng gì "bảng chữ cái ABC",

Còn thiếu sót của chủ topic chỉ là không nói sort cột nào thôi. Nếu nói rõ cột nào thì dù số hay text mọi người sẽ đều hiểu "tăng dần" là thế nào.
 
Upvote 0
Cái đó người ta dùng ArrayList và bản thân nó có chức năng này. Tuy nhiên, thấy dễ ăn vậy chứ thật ra chỉ áp dụng được bài của bạn, List đã được lọc duy nhất rồi nên sort xong còn "mò mò" ra được các phần tử ở cột khác chứ nếu để nguyên cái list chưa lọc duy nhất thì đố Arraylist mần được bài này
Mà cũng thấy lạ thật! Bạn HieuCD dùng ArrayList, bản thân nó cũng có chức năng lọc duy nhất, vậy sao phải thêm Dictionary vào chẳng hóa ra.. thừa???
dùng thêm Dictionary để ghi nhận item mãng 2 phần tử, sau nầy gán giá trị vào mãng Arr cho gọn, không cần phải split tách 2 phần tử
nếu list chứa các giá trị trùng lập thì làm cho nó không trùng lập bằng cách thêm 1 giá trị không trùng như thứ tự dòng chẳng hạn, lúc đó dùng thêm dictionary để lưu giá trị gốc vào Item, càng tiện lợi cho việc gán lại giá trị mãng
 
Upvote 0
@HieuCD:
Cải tiến code như vầy
Mã:
    Slist.Sort
    ReDim Arr(1 To k, 1 To 2)
    For i = 1 To k
      Tmp = .Item(Slist(i - 1))
      Arr(i, 1) = Tmp(0)
      Arr(i, 2) = Tmp(1)
    Next i
Theo như code nguyen thủy thì phải tra dic 2 lần cho mỗi key

Chú: nếu tôi là bạn thì tôi khong dùng cái biến Tmp. Lần thứ nhất, ở vòng lặp dựng lít, tôi dùng thẳng tên sortKey - vởi vì nó là cái key dùng để sort. Và lần thứ nhì, tức là ở tiểu đoạn code trên, tôi dùng tên listVals, hoặc myDat, myArr...
(Lưu ý là theo tinh thần code nguyên thủy của bạn thì Tmp luôn là sortKey)
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu muốn cải tiến giải thuật đẻ không phải dùng dic thì bạn có thể làm như sau:

Nạp key1 + key2 + chỉ số dòng vào arraylist (đương nhiên số dòng phải theo định dạng 00001,... để dễ tách ra trở lại)
Sort ArayList
Duyệt list, dùng chỉ số dòng để lấy dữ liệu thẳng từ data array. Muốn duy nhất thì chỉ lấy key đầu, lướt qua các key lặp lại.
 
Upvote 0
@HieuCD:
Cải tiến code như vầy
Mã:
    Slist.Sort
    ReDim Arr(1 To k, 1 To 2)
    For i = 1 To k
      Tmp = .Item(Slist(i - 1))
      Arr(i, 1) = Tmp(0)
      Arr(i, 2) = Tmp(1)
    Next i
Theo như code nguyen thủy thì phải tra dic 2 lần cho mỗi key

Chú: nếu tôi là bạn thì tôi khong dùng cái biến Tmp. Lần thứ nhất, ở vòng lặp dựng lít, tôi dùng thẳng tên sortKey - vởi vì nó là cái key dùng để sort. Và lần thứ nhì, tức là ở tiểu đoạn code trên, tôi dùng tên listVals, hoặc myDat, myArr...
(Lưu ý là theo tinh thần code nguyên thủy của bạn thì Tmp luôn là sortKey)
cám ơn bạn, mình chỉnh lại code nhờ bạn góp ý thêm
Mã:
Private Sub Test()
Dim Sarr(), Darr(), Arr(), Slist As Object, ShortKey As String, myDat As Variant, i As Long, k As Long
Set Slist = CreateObject("System.Collections.ArrayList")
Darr = Sheets("Nhap").Range("C2:D" & Sheets("Nhap").Range("C65500").End(xlUp).Row).Value
With CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(Darr)
        ShortKey = Darr(i, 1) & "#" & Darr(i, 2)
        If Not .Exists(ShortKey) Then
            .Add ShortKey, Array(Darr(i, 1), Darr(i, 2))
            Slist.Add ShortKey
        End If
    Next i
    Slist.Sort
    k = .Count
    ReDim Arr(1 To k, 1 To 2)
    For i = 1 To k
        myDat = .Item(Slist(i - 1))
        Arr(i, 1) = myDat(0)
        Arr(i, 2) = myDat(1)
    Next i
End With
End Sub
 
Upvote 0
dùng thêm Dictionary để ghi nhận item mãng 2 phần tử, sau nầy gán giá trị vào mãng Arr cho gọn, không cần phải split tách 2 phần tử
nếu list chứa các giá trị trùng lập thì làm cho nó không trùng lập bằng cách thêm 1 giá trị không trùng như thứ tự dòng chẳng hạn, lúc đó dùng thêm dictionary để lưu giá trị gốc vào Item, càng tiện lợi cho việc gán lại giá trị mãng

Ý tôi là bài này không cần đến Dictionary, chỉ với ArrayList tôi có thể vừa Unique vừa Sort ngon lành
(lưu ý rằng: Chỉ với bài này tôi chứ không phải bài toán tổng quát)
-----------------------------------
Bạn nghiên cứu xem!
 
Upvote 0
Em cứ tưởng Topic này dừng ở Bài #19 rồi chứ.
Không ngờ Topic này nhiều Cao Thủ vào xem và có thể còn nữa.
 
Upvote 0
Em cứ tưởng Topic này dừng ở Bài #19 rồi chứ.
Không ngờ Topic này nhiều Cao Thủ vào xem và có thể còn nữa.

Cái gì liên quan đến Sort mảng thì có thể bàn tiếp nha bạn!
Chuyện của bạn có thể xong nhưng người khác có thể bàn tiếp về giải thuật...
 
Upvote 0
Ý tôi là bài này không cần đến Dictionary, chỉ với ArrayList tôi có thể vừa Unique vừa Sort ngon lành
(lưu ý rằng: Chỉ với bài này tôi chứ không phải bài toán tổng quát)
-----------------------------------
Bạn nghiên cứu xem!
không tìm được cách
chỉ sort, nhưng dữ liệu bị trùng
Mã:
Private Sub Test1()
Dim Sarr(), Darr(), Arr(), Slist As Object, ShortKey As String, myDat As Variant, i As Long, k As Long
Set Slist = CreateObject("System.Collections.ArrayList")
Darr = Sheets("Nhap").Range("C2:D" & Sheets("Nhap").Range("C65500").End(xlUp).Row).Value
    For i = 1 To UBound(Darr)
        ShortKey = Darr(i, 1) & "#" & Darr(i, 2)
        Slist.Add ShortKey
    Next i
    Slist.Sort
    k = Slist.Count
    ReDim Arr(1 To k, 1 To 2)
    For i = 1 To k
        myDat = Split(Slist(i - 1), "#")
        Arr(i, 1) = myDat(0)
        Arr(i, 2) = myDat(1)
    Next i
    Range("G2").Resize(k, 2) = Arr
End Sub
 
Upvote 0
không tìm được cách
chỉ sort, nhưng dữ liệu bị trùng
Mã:
Private Sub Test1()
Dim Sarr(), Darr(), Arr(), Slist As Object, ShortKey As String, myDat As Variant, i As Long, k As Long
Set Slist = CreateObject("System.Collections.ArrayList")
Darr = Sheets("Nhap").Range("C2:D" & Sheets("Nhap").Range("C65500").End(xlUp).Row).Value
    For i = 1 To UBound(Darr)
        ShortKey = [COLOR=#ff0000]Darr(i, 1) & "#" & Darr(i, 2)[/COLOR]
        Slist.Add ShortKey
    Next i
    Slist.Sort
    k = Slist.Count
    ReDim Arr(1 To k, 1 To 2)
    For i = 1 To k
        myDat = Split(Slist(i - 1), "#")
        Arr(i, 1) = myDat(0)
        Arr(i, 2) = myDat(1)
    Next i
    Range("G2").Resize(k, 2) = Arr
End Sub
Gợi ý: Dùng phuong thức Contains để kiểm tra sự tồn tai (ví dụ If Not Slist.Contains(ShortKey) then....) giống phương thức Exists của Dictionary
Chỗ màu đỏ tôi không hiểu sao bạn lại nối chuỗi vậy nữa. Bởi:
- Thứ nhất: Nếu có vài chục cột thì nối đến bao giờ?
- Thứ hai: Người ta cần lọc duy nhất cột thứ nhất thì chỉ xét cột thứ nhất thôi chứ. Nối chuỗi vào như vậy là sai so với yêu cầu rồi
----------------------------------------
Lâu lắm rồi không dùng ArrayList, tự dưng nhắc lại cũng thấy.. ngứa nghề rồi đó nha... Ẹc... Ẹc...
 
Lần chỉnh sửa cuối:
Upvote 0
Gợi ý: Dùng phuong thức Contains để kiểm tra sự tồn tai (ví dụ If Not Slist.Contains(ShortKey) then....) giống phương thức Exists của Dictionary
Chỗ màu đỏ tôi không hiểu sao bạn lại nối chuỗi vậy nữa. Bởi:
- Thứ nhất: Nếu có vài chục cột thì nối đến bao giờ?
- Thứ hai: Người ta cần lọc duy nhất cột thứ nhất thì chỉ xét cột thứ nhất thôi chứ. Nối chuỗi vào như vậy là sai so với yêu cầu rồi
----------------------------------------
,Lâu lắm rồi không dùng ArrayList, tự dưng nhắc lại cũng thấy.. ngứa nghề rồi đó nha... Ẹc... Ẹc...
Cám ơn bạn, 2 sản phẩm cùng tên có thể có qui cách đơn vị tính khác nhau như bánh có thể là cái hoặc thùng, ngoài ra chưa biết cách gán giá trị Item đi theo Key để lấy đơn vị tính theo tên sản phẩm, bạn giúp dùm vấn đề nầy
 
Upvote 0
Cám ơn bạn, 2 sản phẩm cùng tên có thể có qui cách đơn vị tính khác nhau như bánh có thể là cái hoặc thùng
OK, đồng ý với bạn chỗ này!
ngoài ra chưa biết cách gán giá trị Item đi theo Key để lấy đơn vị tính theo tên sản phẩm, bạn giúp dùm vấn đề nầy
Xem code
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc, aTmpArr, item, sTmp As String
  Dim oArrList As Object, oListOrigin As Object
  Dim lR As Long, n As Long, idx As Long
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  ReDim aTmpArr(1 To UBound(aSrc, 1), 1 To 2)
  Set oArrList = CreateObject("System.Collections.ArrayList")
  For lR = 1 To UBound(aSrc, 1)
    sTmp = CStr(aSrc(lR, 1))
    If Len(sTmp) Then
      If Not oArrList.Contains(sTmp) Then
        oArrList.Add sTmp
        n = n + 1
        aTmpArr(n, 1) = sTmp
        aTmpArr(n, 2) = aSrc(lR, 2)
      End If
    End If
  Next
  Set oListOrigin = oArrList.Clone
  oArrList.Sort
  ReDim aDes(1 To oArrList.Count, 1 To 2)
  For lR = 0 To oArrList.Count - 1
    idx = oListOrigin.InDexOf(oArrList(lR), 0) + 1
    aDes(lR + 1, 1) = aTmpArr(idx, 1)
    aDes(lR + 1, 2) = aTmpArr(idx, 2)
  Next
  Me.ListBox1.List = aDes
End Sub
Còn thắc mắc thì ta... tiếp tục
 
Upvote 0
OK, đồng ý với bạn chỗ này!

Xem code
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc, aTmpArr, item, sTmp As String
  Dim oArrList As Object, oListOrigin As Object
  Dim lR As Long, n As Long, idx As Long
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  ReDim aTmpArr(1 To UBound(aSrc, 1), 1 To 2)
  Set oArrList = CreateObject("System.Collections.ArrayList")
  For lR = 1 To UBound(aSrc, 1)
    sTmp = CStr(aSrc(lR, 1))
    If Len(sTmp) Then
      If Not oArrList.Contains(sTmp) Then
        oArrList.Add sTmp
        n = n + 1
        aTmpArr(n, 1) = sTmp
        aTmpArr(n, 2) = aSrc(lR, 2)
      End If
    End If
  Next
  Set oListOrigin = oArrList.Clone
  oArrList.Sort
  ReDim aDes(1 To oArrList.Count, 1 To 2)
  For lR = 0 To oArrList.Count - 1
    idx = oListOrigin.InDexOf(oArrList(lR), 0) + 1
    aDes(lR + 1, 1) = aTmpArr(idx, 1)
    aDes(lR + 1, 2) = aTmpArr(idx, 2)
  Next
  Me.ListBox1.List = aDes
End Sub
Còn thắc mắc thì ta... tiếp tục
Code của anh hay qúa trời luôn
em sửa mãi mới ra được đoạn code thì đã muộn rồi.
PHP:
Sub Test2()
Dim Sarr(), Darr(), arr(), tpm(), Slist As Object, myDat As Variant, i As Long, k As Long
Dim ShortKey As String
Set Slist = CreateObject("System.Collections.ArrayList")
Darr = Sheets("Nhap").Range("C2:D" & Sheets("Nhap").Range("C65500").End(xlUp).Row).Value
    For i = 1 To UBound(Darr)
        ShortKey = Darr(i, 1) & "#" & Darr(i, 2)
        If Not Slist.Contains(ShortKey) Then
            Slist.Add ShortKey
        End If
    Next i
    Slist.Sort
    k = Slist.Count
    ReDim arr(1 To k, 1 To 2)
    tpm = Slist.toarray()
    For i = 0 To k - 1
        arr(i + 1, 1) = Left(tpm(i), InStr(tpm(i), "#") - 1)
        arr(i + 1, 2) = Right(tpm(i), Len(tpm(i)) - InStr(tpm(i), "#"))
    Next
   Sheet1.Range("K1").Resize(k, 2) = arr
End Sub
 
Upvote 0
OK, đồng ý với bạn chỗ này!

Xem code
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc, aTmpArr, item, sTmp As String
  Dim oArrList As Object, oListOrigin As Object
  Dim lR As Long, n As Long, idx As Long
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  ReDim aTmpArr(1 To UBound(aSrc, 1), 1 To 2)
  Set oArrList = CreateObject("System.Collections.ArrayList")
  For lR = 1 To UBound(aSrc, 1)
    sTmp = CStr(aSrc(lR, 1))
    If Len(sTmp) Then
      If Not oArrList.Contains(sTmp) Then
        oArrList.Add sTmp
        n = n + 1
        aTmpArr(n, 1) = sTmp
        aTmpArr(n, 2) = aSrc(lR, 2)
      End If
    End If
  Next
  Set oListOrigin = oArrList.Clone
  oArrList.Sort
  ReDim aDes(1 To oArrList.Count, 1 To 2)
  For lR = 0 To oArrList.Count - 1
    idx = oListOrigin.InDexOf(oArrList(lR), 0) + 1
    aDes(lR + 1, 1) = aTmpArr(idx, 1)
    aDes(lR + 1, 2) = aTmpArr(idx, 2)
  Next
  Me.ListBox1.List = aDes
End Sub
Còn thắc mắc thì ta... tiếp tục
cám ơn bạn, biết thêm một số lệnh hay
thêm 1 arraylist để ghi nhận 1 Item, nếu nhiều dữ liệu lưu theo Key thì dùng thứ tự dòng làm Item, bạn góp ý dùm
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc, aTmpArr, sTmp As String
  Dim sList As Object, oList1 As Object, oList2 As Object
  Dim lR As Long, idx As Long
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  Set oList1 = CreateObject("System.Collections.ArrayList")
  Set oList2 = CreateObject("System.Collections.ArrayList")
  For lR = 1 To UBound(aSrc, 1)
    sTmp = CStr(aSrc(lR, 1))
    If Len(sTmp) Then
      If Not oList1.Contains(sTmp) Then
        oList1.Add sTmp
            [COLOR=#0000cd]oList2.Add CStr(aSrc(lR, 2))
            'oList2.Add lR[/COLOR]
      End If
    End If
  Next
  Set sList = oList1.Clone
  sList.Sort
  ReDim aDes(1 To sList.Count, 1 To 2)
  For lR = 0 To sList.Count - 1
    idx = oList1.InDexOf(sList(lR), 0)
[COLOR=#0000cd]        aDes(lR + 1, 1) = oList1(idx)
        aDes(lR + 1, 2) = oList2(idx)
        'idx = oList2(idx)
        'aDes(lR + 1, 1) = aSrc(idx, 1)
        'aDes(lR + 1, 2) = aSrc(idx, 2)[/COLOR]
  Next
  Me.ListBox1.List = aDes
End Sub
 
Upvote 0
cám ơn bạn, biết thêm một số lệnh hay
thêm 1 arraylist để ghi nhận 1 Item, nếu nhiều dữ liệu lưu theo Key thì dùng thứ tự dòng làm Item, bạn góp ý dùm
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc, aTmpArr, sTmp As String
  Dim sList As Object, oList1 As Object, oList2 As Object
  Dim lR As Long, idx As Long
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  Set oList1 = CreateObject("System.Collections.ArrayList")
  Set oList2 = CreateObject("System.Collections.ArrayList")
  For lR = 1 To UBound(aSrc, 1)
    sTmp = CStr(aSrc(lR, 1))
    If Len(sTmp) Then
      If Not oList1.Contains(sTmp) Then
        oList1.Add sTmp
            [COLOR=#0000cd]oList2.Add CStr(aSrc(lR, 2))
            'oList2.Add lR[/COLOR]
      End If
    End If
  Next
  Set sList = oList1.Clone
  sList.Sort
  ReDim aDes(1 To sList.Count, 1 To 2)
  For lR = 0 To sList.Count - 1
    idx = oList1.InDexOf(sList(lR), 0)
[COLOR=#0000cd]        aDes(lR + 1, 1) = oList1(idx)
        aDes(lR + 1, 2) = oList2(idx)
        'idx = oList2(idx)
        'aDes(lR + 1, 1) = aSrc(idx, 1)
        'aDes(lR + 1, 2) = aSrc(idx, 2)[/COLOR]
  Next
  Me.ListBox1.List = aDes
End Sub

Nhìn chung là ổn!
Trong code của bạn có oList1, oList2 và sList... cả 3 đều là ArrayList. Giải quyết bài này mà dùng đến 3 object thì thôi tôi thà dùng ArrayList + Dictionary sướng hơn
Nói vui thế thôi chứ các bạn tham gia là tôi vui rồi. Mong có dịp học hỏi thêm từ các bạn
 
Upvote 0
thêm 1 arraylist để ghi nhận 1 Item, nếu nhiều dữ liệu lưu theo Key thì dùng thứ tự dòng làm Item, bạn góp ý dùm

Bạn không cần phải dùng nhiều list như thế. Chỉ cần 1
Khi ghi key, cần ghi 2 lần. Lần thứ nhất là key nguyên vẹn, dùng để kiểm tra với hàm Contains. Lần thứ nhì là key cộng số row trong mảng, túc là key & Right(Cstr(1000000+lR),6)

Đương nhiên List của bạn sẽ lớn gấp 2 lần bình thường. Nhưng khong sao cả, arraylist là class của dot net, chúng tự biết chọn cách sort rất hiệu quả.

Sau khi sort xong, bạn sẽ có list sắp theo thứ tự. Những phần tử vị trí chẵn (0, 2, 4...) là key nguyên thuỷ, bạn lướt qua. Phần tử lẻ (1, 3,...) là key và chỉ số dòng trong mảng. Bạn chỉ cần tách ra Val(Right(key,6)) và dùng nó để lấy dữ liệu trong mảng.

=== Bổ sung ===
Quên mất, chơi trò sort mà ghép chuỗi suông thì sẽ bị kết quả bất ngờ nếu gặp trường hợp key này chứa key kia
"abc def" chứa "abc"; khi nối thêm chuỗi vào, "abc def005" sẽ được sort ra trước "abc002"
Khi sort chuỗi ghép, luôn luôn chèn thêm ký tự null. Lúc ấy chuỗi con sẽ bảo đảm được sort trước chuỗi mẹ.
==> key & vbNullChar & Right(Cstr(1000000+lR),6)
(nếu dùng Unicode thì dùng 2 ký tự cho chắc ăn)
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn không cần phải dùng nhiều list như thế. Chỉ cần 1
Khi ghi key, cần ghi 2 lần. Lần thứ nhất là key nguyên vẹn, dùng để kiểm tra với hàm Contains. Lần thứ nhì là key cộng số row trong mảng, túc là key & Right(Cstr(1000000+lR),6)
Đương nhiên List của bạn sẽ lớn gấp 2 lần bình thường. Nhưng khong sao cả, arraylist là class của dot net, chúng tự biết chọn cách sort rất hiệu quả.
Sau khi sort xong, bạn sẽ có list sắp theo thứ tự. Những phần tử vị trí chẵn (0, 2, 4...) là key nguyên thuỷ, bạn lướt qua. Phần tử lẻ (1, 3,...) là key và chỉ số dòng trong mảng. Bạn chỉ cần tách ra Val(Right(key,6)) và dùng nó để lấy dữ liệu trong mảng.
cám ơn bạn, cách nầy rất tuyệt, vậy là bài toán đã giải quyết triệt để
 
Upvote 0
Ai rảnh nghiên cứu thử dùng ArrayList để sort mảng 2 chiều xem. Đương nhiên mảng 2 chiều ở đây là mảng tùy ý (không phải đã lọc duy nhất như bài toán ở topic này)
Tôi chưa làm nhưng tôi cảm giác là được. Ngoài ArrayList, có thể dùng SortedList, Stack... hay gì gì đó tương tự cũng được
(vấn đề của tôi là thời gian rảnh ít quá, chưa thể bắt tay làm một cách nghiêm túc được)
 
Upvote 0
Ai rảnh nghiên cứu thử dùng ArrayList để sort mảng 2 chiều xem. Đương nhiên mảng 2 chiều ở đây là mảng tùy ý (không phải đã lọc duy nhất như bài toán ở topic này)
Tôi chưa làm nhưng tôi cảm giác là được. Ngoài ArrayList, có thể dùng SortedList, Stack... hay gì gì đó tương tự cũng được
(vấn đề của tôi là thời gian rảnh ít quá, chưa thể bắt tay làm một cách nghiêm túc được)
dùng ArrayList sort mảng 2 chiều theo tối đa 3 điều kiện, chắc còn sơ sót các bạn góp ý thêm, chạy sub main để kiểm tra
Mã:
Sub Main()
Dim Darr()
Darr = Range("A1:E" & Range("B" & Rows.Count).End(xlUp).Row).Value
Arr = SortArray(Darr, True, 2, True, 3, True, 4, True)
Range("G1").Resize(UBound(Darr), UBound(Darr, 2)) = Arr
End Sub

Function SortArray(ByVal SourceArray, ByVal HasTitle As Boolean, ByVal ColIndex1 As Byte, _
            Optional ByVal Order1 As Boolean = True, Optional ByVal ColIndex2 As Byte = 0, _
            Optional ByVal Order2 As Boolean = True, Optional ByVal ColIndex3 As Byte = 0, _
             Optional ByVal Order3 As Boolean = True)
  Dim Darr(), Arr()
  Dim i As Long, iP As Long, ir As Long, k As Long, R As Long, LenR As Byte, Tmp
  Darr = SourceArray
  ReDim Arr(1 To UBound(Darr), 1 To UBound(Darr, 2))
  If ColIndex1 >= 1 And ColIndex1 <= UBound(Darr, 2) Then
    If ColIndex2 = 0 Then
      Arr = SortArray1Col(Darr, ColIndex1, Order1, HasTitle, True)
    Else
      Arr = SortArray1Col(Darr, ColIndex1, Order1, HasTitle)
      If ColIndex2 >= 1 And ColIndex2 <= UBound(Darr, 2) Then
        Darr = Arr
        Arr = SortArray2Col(Darr, ColIndex1, ColIndex2, Order2, HasTitle)
        If ColIndex3 >= 1 And ColIndex3 <= UBound(Darr, 2) Then
          Darr = Arr
          Arr = SortArray2Col(Darr, ColIndex2, ColIndex3, Order3, HasTitle)
        End If
      End If
    End If
    SortArray = Arr
  End If
End Function

Function SortArray1Col(ByVal SourceArray, ByVal ColIndex As Byte, Optional ByVal Order As Boolean = True, _
                Optional ByVal HasTitle As Boolean = False, Optional ByVal HideTitle As Boolean = False)
  Dim IndexList As Object, List As Object, Darr(), Arr()
  Dim i As Long, j As Byte, k As Long, R As Long, LenR As Integer, Tmp
  Set IndexList = CreateObject("System.Collections.ArrayList")
  Darr = SourceArray
  R = UBound(Darr) + HasTitle 'so dong du lieu Sort
  LenR = Len(CStr(R))         'so chu so cua thu tu dong
  If ColIndex < 1 Or ColIndex > UBound(Darr, 2) Then
    MsgBox ("ColIndex khong phu hop" & Chr(13) & "Sort Data Khong duoc thuc hien")
    SortArray1Col = Darr
    Exit Function
  End If
  For i = 1 To R
    Tmp = Darr(i - HasTitle, ColIndex)
    If IsNumeric(Tmp) Then Tmp = CStr(String(15 - Len(CStr(Tmp)), "0") & Tmp)
    Tmp = Tmp & String(LenR - Len(CStr(i)), "0") & i
    IndexList.Add Tmp
  Next
  Set List = IndexList.Clone
  List.Sort
  ReDim Arr(1 To UBound(Darr), 1 To UBound(Darr, 2))
  If HasTitle = False Then HideTitle = False
  If HideTitle Then
    ReDim Arr(1 To R, 1 To UBound(Darr, 2))
  Else
    ReDim Arr(1 To UBound(Darr), 1 To UBound(Darr, 2))
    If HasTitle Then
      For j = 1 To UBound(Darr, 2)
        Arr(1, j) = Darr(1, j)
      Next j
    End If
  End If
  For i = 0 To R - 1
    k = IndexList.InDexOf(List(i), 0) + 1
    For j = 1 To UBound(Darr, 2)
      If Order Then n = i + 1 Else n = R - i
      If HideTitle Then
        Arr(n, j) = Darr(k - HasTitle, j)
      Else
        Arr(n - HasTitle, j) = Darr(k - HasTitle, j)
      End If
    Next j
  Next i
  SortArray1Col = Arr
End Function

Function SortArray2Col(ByVal SourceArray, ByVal ColMain As Byte, ByVal ColIndex As Byte, ByVal Order As Boolean, Optional ByVal HasTitle As Boolean = False)
  Dim Darr(), Arr()
  Dim i As Long, ir As Long, k As Long, R As Long, j As Integer, Tmp
  Darr = SourceArray
  For i = 1 - HasTitle To UBound(Darr) - 1
    If Darr(i, ColMain) = Darr(i + 1, ColMain) Then
      R = i
      Tmp = Darr(i, ColMain)
      k = 0
      For ir = R To UBound(Darr)
        If Darr(ir, ColMain) = Tmp Then
          k = k + 1
        Else
          Exit For
        End If
      Next ir
      ReDim Arr(1 To k, 1 To UBound(Darr, 2))
      For ir = 1 To k
        For j = 1 To UBound(Darr, 2)
          Arr(ir, j) = Darr(ir + R - 1, j)
        Next j
      Next ir
      Arr = SortArray1Col(Arr, ColIndex, Order)
      For ir = 1 To k
        For j = 1 To UBound(Darr, 2)
          Darr(ir + R - 1, j) = Arr(ir, j)
        Next j
      Next ir
      i = i + k - 1
    End If
  Next i
  SortArray2Col = Darr
End Function
 

File đính kèm

Upvote 0
dùng ArrayList sort mảng 2 chiều theo tối đa 3 điều kiện, chắc còn sơ sót các bạn góp ý thêm, chạy sub main để kiểm tra
Mã:
[/QUOTE]

Quá dữ luôn!
Thật ra bạn có thể tự kiểm tra bằng cách so sánh với sort của Excel xem độ chinh xác đến đâu
 
Upvote 0
Quá dữ luôn!
Thật ra bạn có thể tự kiểm tra bằng cách so sánh với sort của Excel xem độ chinh xác đến đâu
sort theo 3 điều kiện bị sai, có khác vài trường hợp sort của Excel ở những cột không sort, có lẽ do thứ tự dò khác nhau
chỉnh lại code
Mã:
Function SortArray(ByVal SourceArray, ByVal HasTitle As Boolean, ByVal ColIndex1 As Byte, _
            Optional ByVal Order1 As Boolean = True, Optional ByVal ColIndex2 As Byte = 0, _
            Optional ByVal Order2 As Boolean = True, Optional ByVal ColIndex3 As Byte = 0, _
             Optional ByVal Order3 As Boolean = True)
  Dim Darr(), Arr()
  Dim i As Long, iP As Long, ir As Long, k As Long, R As Long, LenR As Byte, Tmp
  Darr = SourceArray
  ReDim Arr(1 To UBound(Darr), 1 To UBound(Darr, 2))
  If ColIndex1 >= 1 And ColIndex1 <= UBound(Darr, 2) Then
    If ColIndex2 = 0 Then
      Arr = SortArray1Col(Darr, ColIndex1, Order1, HasTitle, True)
    Else
      Arr = SortArray1Col(Darr, ColIndex1, Order1, HasTitle)
      If ColIndex2 >= 1 And ColIndex2 <= UBound(Darr, 2) Then
        Darr = Arr
        Arr = SortArray2Col(Darr, ColIndex1, ColIndex1, ColIndex2, Order2, HasTitle)
        If ColIndex3 >= 1 And ColIndex3 <= UBound(Darr, 2) Then
          Darr = Arr
          Arr = SortArray2Col(Darr, ColIndex1, ColIndex2, ColIndex3, Order3, HasTitle)
        End If
      End If
    End If
    SortArray = Arr
  End If
End Function
Mã:
Function SortArray2Col(ByVal SourceArray, ByVal ColMain1 As Byte, ByVal ColMain2 As Byte, ByVal ColIndex As Byte, ByVal Order As Boolean, Optional ByVal HasTitle As Boolean = False)
  Dim Darr(), Arr()
  Dim i As Long, ir As Long, k As Long, R As Long, j As Integer, Tmp1, Tmp2
  Darr = SourceArray
  For i = 1 - HasTitle To UBound(Darr) - 1
    If Darr(i, ColMain1) = Darr(i + 1, ColMain1) And Darr(i, ColMain2) = Darr(i + 1, ColMain2) Then
      R = i
      Tmp1 = Darr(i, ColMain1): Tmp2 = Darr(i, ColMain2)
      k = 0
      For ir = R To UBound(Darr)
        If Darr(ir, ColMain1) = Tmp1 And Darr(ir, ColMain2) = Tmp2 Then
          k = k + 1
        Else
          Exit For
        End If
      Next ir
      ReDim Arr(1 To k, 1 To UBound(Darr, 2))
      For ir = 1 To k
        For j = 1 To UBound(Darr, 2)
          Arr(ir, j) = Darr(ir + R - 1, j)
        Next j
      Next ir
      Arr = SortArray1Col(Arr, ColIndex, Order)
      For ir = 1 To k
        For j = 1 To UBound(Darr, 2)
          Darr(ir + R - 1, j) = Arr(ir, j)
        Next j
      Next ir
      i = i + k - 1
    End If
  Next i
  SortArray2Col = Darr
End Function
 

File đính kèm

Upvote 0
sort theo 3 điều kiện bị sai, có khác vài trường hợp sort của Excel ở những cột không sort, có lẽ do thứ tự dò khác nhau
chỉnh lại code

Bạn nên làm vài cuộc thí nghiệm để kiểm chứng. Ví dụ: Lấy dữ liệu từ bảng tính lên ListBox rồi sort gì gì đó. Xong lấy dữ liệu từ chính cái ListBox ấy để sort, sau đó nạp kết quả trở lại ListBox
Đầu tiên thử nghiệm sort 1 cột thôi nhé
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc
  aSrc = Range("A2:E28").Value
  Dim aDes
  [COLOR=#ff0000]aDes = SortArray(aSrc, False, 3)[/COLOR]
  Me.ListBox1.List = aDes
End Sub 
Private Sub CommandButton1_Click()
   Dim aSrc
  aSrc = Me.ListBox1.List
  Dim aDes
  [COLOR=#ff0000]aDes = SortArray(aSrc, False, 3)[/COLOR]
  Me.ListBox1.List = aDes
End Sub
(vẽ thêm 1 CommandButton)
Để ý 2 dòng code màu đỏ là y chang nhau. Nếu kết quả nhận được khác nhau thì chứng tỏ code có vấn đề
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn nên làm vài cuộc thí nghiệm để kiểm chứng. Ví dụ: Lấy dữ liệu từ bảng tính lên ListBox rồi sort gì gì đó. Xong lấy dữ liệu từ chính cái ListBox ấy để sort, sau đó nạp kết quả trở lại ListBox
Đầu tiên thử nghiệm sort 1 cột thôi nhé
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc
  aSrc = Range("A2:E28").Value
  Dim aDes
  [COLOR=#ff0000]aDes = SortArray(aSrc, False, 3)[/COLOR]
  Me.ListBox1.List = aDes
End Sub 
Private Sub CommandButton1_Click()
   Dim aSrc
  aSrc = Me.ListBox1.List
  Dim aDes
  [COLOR=#ff0000]aDes = SortArray(aSrc, False, 3)[/COLOR]
  Me.ListBox1.List = aDes
End Sub
(vẽ thêm 1 CommandButton)
Để ý 2 dòng code màu đỏ là y chang nhau. Nếu kết quả nhận được khác nhau thì chứng tỏ code có vấn đề
vận hành của listbox mình không rành lắm nên không biết kiểm tra như thế nào
 
Upvote 0
vận hành của listbox mình không rành lắm nên không biết kiểm tra như thế nào

Mấy file Bạn úp mạnh thử Toàn lỗi dòng sau ko biết thiếu cái gì !!??
Mạnh Xài Office 2016 +Windows10 x32
Mã:
Set IndexList = CreateObject("System.Collections.ArrayList")
 
Upvote 0
Lần chỉnh sửa cuối:
Upvote 0
Chính xác là Object này phải cài .net framework mơid xài được. Win 10 thì vào kích hoạt lên là được. Vì nó cài sẵn rồi...

tại ông Bill thấy nó đồ cổ rồi ông keo cất kho đó mà khi nào cần thì keo nó ...giờ nó lên 4.6.1 rồi đó
 
Upvote 0
vận hành của listbox mình không rành lắm nên không biết kiểm tra như thế nào

Thì kiểm tra như code tôi ghi ở trên đó
Thật ra thì mảng lấy từ bảng tính hay lấy từ listbox cũng như như thôi
Ví dụ tôi có:
Mã:
arr1 = Range("A1:D10").Value
tôi nạp arr1 vào listbox:
Mã:
Me.ListBox1.List = arr1
Giờ tôi lại lấy dữ liệu từ listbox nạp vào 1 mảng
Mã:
arr2 = Me.ListBox1.List
Kết quả 2 mảng arr1 và arr2 là như nhau. Vậy điều gì khiến cho code sort chạy đúng khi lấy dữ liệu từ Range nhưng lại sai khi lấy từ ListBox?
Vấn đề nằm ở chỗ: Tuy 2 mảng arr1 và arr2 giống nhau nhưng có 1 điểm khác biệt "chết người", đó là mảng lấy từ Range sẽ có chuẩn BASE 1 (LBound(arr1) = 1) trong khi mảng lấy từ listbox lại có chuẩn BASE 0 (LBound(arr2) =0)
----------------------------
Điều tôi muốn nhấn mạnh ở đây là:
- Đã gọi là MẢNG BẤT KỲ thì coi như ta không biết trước được BASE = bao nhiêu (LBound(mảng) = bao nhiêu chưa biết)
- Khi viết code cho mảng, chúng ta không thể chủ quan mà xem nó như Range
- Mảng có thể lấy từ Range nhưng trong thực tế sẽ có trường hợp lấy từ nơi khác, chẳng hạn mảng do ta tự tạo ra, mảng lấy từ các control... vân vân...
- Trong 1 số trường hợp, để chuẩn hóa mảng luôn là BASE 1, người ta cho đoạn Option Base 1 lên đầu code (dưới dòng Option Explicit). Tuy nhiên điều này cũng chỉ có tác dụng với mảng do ta tự tạo ra và hoàn toàn không ăn thua gì đối với mảng được lấy từ nơi khác (có thể thí nghiệm để chứng minh)

Nói tóm lại nếu ta viết code thế này:
Mã:
For i =[SIZE=4][COLOR=#ff0000] 1[/COLOR][/SIZE] to UBound(arr,1)
 .....
Next
Thì sự chủ quan của ta nằm ở chính con số 1 (tô đỏ ở trên). Bởi làm sao ta chắc ăn 100% rằng chỉ số index đầu tiên của arr là 1 (trừ phi bạn xác định ngay từ đầu đối số của hàm phải là Range)
Chắc ăn ta luôn viết:
Mã:
For i =[SIZE=4][COLOR=#ff0000] LBound(arr,1) [/COLOR][/SIZE] to UBound(arr,1)
 .....
Next
- Mục đích viết code sort mảng 2D chủ yếu là muốn nó hoạt động ở đâu đó khác với môi trường bảng tính chứ ngay trên bảng tính Excel ta đã có công cụ sort rồi, cần gì phải viết thêm
-----------------------------------

Ôi... dài dòng quá! Không biết có ai hiểu không nữa
 
Upvote 0
Thì kiểm tra như code tôi ghi ở trên đó
Thật ra thì mảng lấy từ bảng tính hay lấy từ listbox cũng như như thôi
Ví dụ tôi có:
Mã:
arr1 = Range("A1:D10").Value
tôi nạp arr1 vào listbox:
Mã:
Me.ListBox1.List = arr1
Giờ tôi lại lấy dữ liệu từ listbox nạp vào 1 mảng
Mã:
arr2 = Me.ListBox1.List
Kết quả 2 mảng arr1 và arr2 là như nhau. Vậy điều gì khiến cho code sort chạy đúng khi lấy dữ liệu từ Range nhưng lại sai khi lấy từ ListBox?
Vấn đề nằm ở chỗ: Tuy 2 mảng arr1 và arr2 giống nhau nhưng có 1 điểm khác biệt "chết người", đó là mảng lấy từ Range sẽ có chuẩn BASE 1 (LBound(arr1) = 1) trong khi mảng lấy từ listbox lại có chuẩn BASE 0 (LBound(arr2) =0)
----------------------------
Điều tôi muốn nhấn mạnh ở đây là:
- Đã gọi là MẢNG BẤT KỲ thì coi như ta không biết trước được BASE = bao nhiêu (LBound(mảng) = bao nhiêu chưa biết)
- Khi viết code cho mảng, chúng ta không thể chủ quan mà xem nó như Range
- Mảng có thể lấy từ Range nhưng trong thực tế sẽ có trường hợp lấy từ nơi khác, chẳng hạn mảng do ta tự tạo ra, mảng lấy từ các control... vân vân...
- Trong 1 số trường hợp, để chuẩn hóa mảng luôn là BASE 1, người ta cho đoạn Option Base 1 lên đầu code (dưới dòng Option Explicit). Tuy nhiên điều này cũng chỉ có tác dụng với mảng do ta tự tạo ra và hoàn toàn không ăn thua gì đối với mảng được lấy từ nơi khác (có thể thí nghiệm để chứng minh)

Nói tóm lại nếu ta viết code thế này:
Mã:
For i =[SIZE=4][COLOR=#ff0000] 1[/COLOR][/SIZE] to UBound(arr,1)
 .....
Next
Thì sự chủ quan của ta nằm ở chính con số 1 (tô đỏ ở trên). Bởi làm sao ta chắc ăn 100% rằng chỉ số index đầu tiên của arr là 1 (trừ phi bạn xác định ngay từ đầu đối số của hàm phải là Range)
Chắc ăn ta luôn viết:
Mã:
For i =[SIZE=4][COLOR=#ff0000] LBound(arr,1) [/COLOR][/SIZE] to UBound(arr,1)
 .....
Next
- Mục đích viết code sort mảng 2D chủ yếu là muốn nó hoạt động ở đâu đó khác với môi trường bảng tính chứ ngay trên bảng tính Excel ta đã có công cụ sort rồi, cần gì phải viết thêm
-----------------------------------

Ôi... dài dòng quá! Không biết có ai hiểu không nữa
cám ơn bạn, góp ý của bạn rất hay, mình sẽ nghiên cứu và chỉnh lại các tham số code sau
ngoài ra khi nhấn command chạy list liên tục thì bị lổi, mình đã bẩy lổi nhưng không biết tại sao không được, bạn xem giúp
 

File đính kèm

Upvote 0
cám ơn bạn, góp ý của bạn rất hay, mình sẽ nghiên cứu và chỉnh lại các tham số code sau

Gợi ý:
Khi bạn viết nhanh một sub, giải quyết vấn đề tại chỗ, thì bạn có thể tuỳ tiện dùng thông số mà mình đã biết trước for i = 1 to 10, hay for i = 0 to gì gì đó. Làm như vậy cho nhanh gọn. Những con số 0, 1, vv... trong lập trình gọi là magic numbers (số từ trên trời rớt xuống). Tức là những con số mà bạn biết trước sẽ luôn như vây (hằng).
Nhưng khi bạn giải quyết một vấn đề phức tạp hơn, có nhiều sub/function; và có thể bạn sẽ cóp các sub/function này lại để dùng lâu dài thì nên tránh dùng magic numbers. Nhũng con só này nếu tính được thì nên dùng hàm để tính (điển hình là LBound, UBound cho mảng); không tính được (điển hình là PI) thì bạn cho vào biến Const và đặt lên đầu module hoạc sub (tuỳ theo bạn muốn nó là toàn cục hay cục bộ). Nên nhớ là từ khoá Const được ngôn ngữ đưa ra để khai báo hằng. Ngừoi đọc code nhìn vào thì để ý ngay là code bạn có những thong số như thế.
(ngừoi đọc code có thể chính là bạn 1 vài năm sau. Néu bạn dùng nhiều magic numbers thì 1 vài năm sau, đọc lại code có thể chính bạn cũng khong hiểu)
 
Upvote 0
Gợi ý:
Khi bạn viết nhanh một sub, giải quyết vấn đề tại chỗ, thì bạn có thể tuỳ tiện dùng thông số mà mình đã biết trước for i = 1 to 10, hay for i = 0 to gì gì đó. Làm như vậy cho nhanh gọn. Những con số 0, 1, vv... trong lập trình gọi là magic numbers (số từ trên trời rớt xuống). Tức là những con số mà bạn biết trước sẽ luôn như vây (hằng).
Nhưng khi bạn giải quyết một vấn đề phức tạp hơn, có nhiều sub/function; và có thể bạn sẽ cóp các sub/function này lại để dùng lâu dài thì nên tránh dùng magic numbers. Nhũng con só này nếu tính được thì nên dùng hàm để tính (điển hình là LBound, UBound cho mảng); không tính được (điển hình là PI) thì bạn cho vào biến Const và đặt lên đầu module hoạc sub (tuỳ theo bạn muốn nó là toàn cục hay cục bộ). Nên nhớ là từ khoá Const được ngôn ngữ đưa ra để khai báo hằng. Ngừoi đọc code nhìn vào thì để ý ngay là code bạn có những thong số như thế.
(ngừoi đọc code có thể chính là bạn 1 vài năm sau. Néu bạn dùng nhiều magic numbers thì 1 vài năm sau, đọc lại code có thể chính bạn cũng khong hiểu)
cám ơn bạn, đúng như bạn góp ý, chỉnh lại các tham số quá rắc rối, nó chạy lung tung, đành phải viết lại code mới, và sau nầy muốn thêm các điều kiện sort 4, 5, 6 cũng dể
code kết hợp ArrayList và Dictionary và duyệt qua tất cả các dòng của các cột sort và thêm 1 for next lấy kết quả, nên có thể chạy chậm hơn code trước
 

File đính kèm

Upvote 0
cám ơn bạn, góp ý của bạn rất hay, mình sẽ nghiên cứu và chỉnh lại các tham số code sau
ngoài ra khi nhấn command chạy list liên tục thì bị lổi, mình đã bẩy lổi nhưng không biết tại sao không được, bạn xem giúp

Thì bạn cũng thấy qua thí nghiệm rồi đó:
- Đầu tiên form load thì listbox có 5 cột
- Bấm CommandButton, listbox còn 4 cột
- Bấm tiếp CommandButton, listbox còn 3 cột
Và đương nhiên bấm tiếp nữa sẽ bị lỗi, bởi code button:
Mã:
Private Sub CommandButton1_Click()
   Dim aSrc
  aSrc = Me.ListBox1.List
  Test1 = UBound(aSrc, 1)
  Test2 = UBound(aSrc, 2)
  Dim aDes
  aDes = SortArray(aSrc, False, [COLOR=#ff0000]3[/COLOR])
  Me.ListBox1.List = aDes
End Sub
Sort cột 3 nhưng hiện tại có cột 3 đâu mà sort?
-----------------------------------------------------------
Vậy vấn đề nằm ở chỗ BASE 0 và BASE 1 như tôi đã đề câp ở bài 51. Cụ thể trong code của bạn:
Mã:
Function SortArray1Col(ByVal SourceArray, ByVal ColIndex As Byte, Optional ByVal Order As Boolean = True, _
                Optional ByVal HasTitle As Boolean = False, Optional ByVal HideTitle As Boolean = False)

  R = UBound(Darr) + HasTitle 'so dong du lieu Sort
  LenR = Len(CStr(R))         'so chu so cua thu tu dong
  
  [COLOR=#ff0000]For i = 1 To R[/COLOR]

End Function
Phải xem lại chỗ màu đỏ
----------------------
Để đơn giản hóa vấn đề, khuyên bạn nên làm bài toán dễ hơn, chẳng hạn lọc duy nhất từ mảng 2 chiều theo cột chỉ định, ví dụ:
Mã:
Function Unique2DArray(ByVal Source2D, ByVal ColIndex As Long)
   ........
End Function
Trong đó Source2D là mảng bất kỳ.
Nếu bạn làm được bài toán này lấy source trên range hay trên listbox đều ổn, tự nhiên bạn sẽ có ngay kinh nghiêm để làm tiếp bài toán sort2d
 
Lần chỉnh sửa cuối:
Upvote 0
Thì bạn cũng thấy qua thí nghiệm rồi đó:
- Đầu tiên form load thì listbox có 5 cột
- Bấm CommandButton, listbox còn 4 cột
- Bấm tiếp CommandButton, listbox còn 3 cột
Và đương nhiên bấm tiếp nữa sẽ bị lỗi, bởi code button:
Mã:
Private Sub CommandButton1_Click()
   Dim aSrc
  aSrc = Me.ListBox1.List
  Test1 = UBound(aSrc, 1)
  Test2 = UBound(aSrc, 2)
  Dim aDes
  aDes = SortArray(aSrc, False, [COLOR=#ff0000]3[/COLOR])
  Me.ListBox1.List = aDes
End Sub
Sort cột 3 nhưng hiện tại có cột 3 đâu mà sort?
-----------------------------------------------------------
Vậy vấn đề nằm ở chỗ BASE 0 và BASE 1 như tôi đã đề câp ở bài 51. Cụ thể trong code của bạn:
Mã:
Function SortArray1Col(ByVal SourceArray, ByVal ColIndex As Byte, Optional ByVal Order As Boolean = True, _
                Optional ByVal HasTitle As Boolean = False, Optional ByVal HideTitle As Boolean = False)

  R = UBound(Darr) + HasTitle 'so dong du lieu Sort
  LenR = Len(CStr(R))         'so chu so cua thu tu dong
  
  [COLOR=#ff0000]For i = 1 To R[/COLOR]

End Function
Phải xem lại chỗ màu đỏ
----------------------
Để đơn giản hóa vấn đề, khuyên bạn nên làm bài toán dễ hơn, chẳng hạn lọc duy nhất từ mảng 2 chiều theo cột chỉ định, ví dụ:
Mã:
Function Unique2DArray(ByVal Source2D, ByVal ColIndex As Long)
   ........
End Function
Trong đó Source2D là mảng bất kỳ.
Nếu bạn làm được bài toán này lấy source trên range hay trên listbox đều ổn, tự nhiên bạn sẽ có ngay kinh nghiêm để làm tiếp bài toán sort2d
cám ơn bạn, mình đang chỉnh lại code
bạn góp ý thêm code ở bài #54
 
Upvote 0
Bạn nên làm vài cuộc thí nghiệm để kiểm chứng. Ví dụ: Lấy dữ liệu từ bảng tính lên ListBox rồi sort gì gì đó. Xong lấy dữ liệu từ chính cái ListBox ấy để sort, sau đó nạp kết quả trở lại ListBox
Đầu tiên thử nghiệm sort 1 cột thôi nhé
Mã:
Private Sub UserForm_Initialize()
  Dim aSrc
  aSrc = Range("A2:E28").Value
  Dim aDes
  [COLOR=#ff0000]aDes = SortArray(aSrc, False, 3)[/COLOR]
  Me.ListBox1.List = aDes
End Sub 
Private Sub CommandButton1_Click()
   Dim aSrc
  aSrc = Me.ListBox1.List
  Dim aDes
  [COLOR=#ff0000]aDes = SortArray(aSrc, False, 3)[/COLOR]
  Me.ListBox1.List = aDes
End Sub
(vẽ thêm 1 CommandButton)
Để ý 2 dòng code màu đỏ là y chang nhau. Nếu kết quả nhận được khác nhau thì chứng tỏ code có vấn đề
đã chỉnh lại code chạy theo mảng hoặc range, bạn góp ý dùm
 

File đính kèm

Upvote 0
đã chỉnh lại code chạy theo mảng hoặc range, bạn góp ý dùm

Vẫn chưa được bạn à:
- Đầu tiên show form, listbox có 5 cột
- Bấm Button, listbox còn 4 cột
- Càng bấm, số cột càng mất dần
Nói chung dữ liệu lấy từ range hay từ listbox thì chúng cũng có 5 cột, cớ sao qua quá trình xử lý lại bị mất đi? Có nghĩa là bạn vẫn chưa giải quyết được hoàn toàn vấn đề base 0 và base 1 của mảng
(kết quả chính xác trước rồi mới bàn tiếp về giải thuật)
 
Upvote 0
Tôi gửi bạn file dưới đây để tham khảo. Hàm sort 1 cột đơn giản thôi nhưng chắc ăn kết quả được bảo toàn
Mã:
Function Sort2DArray(ByVal Source2D, ByVal ColIndex As Long, Optional ByVal Order As Boolean = False)
  Dim aSrc, tmp
  Dim oArrList As Object
  Dim lR As Long, lC As Long, idx As Long, lPos As Long
  Dim lFstRow As Long, lEndRow As Long, lFstCol As Long, lEndCol As Long
  aSrc = Source2D
  lFstRow = LBound(aSrc, 1): lEndRow = UBound(aSrc, 1)
  lFstCol = LBound(aSrc, 2): lEndCol = UBound(aSrc, 2)
  Set oArrList = CreateObject("System.Collections.ArrayList")
  For lR = lFstRow To lEndRow
    tmp = aSrc(lR, ColIndex)
    oArrList.Add tmp
  Next
  oArrList.Sort
  If Order Then oArrList.Reverse
  ReDim aPos(oArrList.Count - 1)  'mảng chuẩn base 0
  ReDim aDes(lFstRow To lEndRow, lFstCol To lEndCol)
  For lR = lFstRow To lEndRow
    tmp = aSrc(lR, ColIndex)
    idx = oArrList.IndexOf(tmp, 0)
    lPos = idx [COLOR=#ff0000]+ lFstRow[/COLOR] + aPos(idx) ''vi tri tùy biến theo chuẩn base của mảng nguồn
    For lC = lFstCol To lEndCol
      aDes(lPos, lC) = aSrc(lR, lC)
    Next
    aPos(idx) = aPos(idx) + 1
  Next
  Sort2DArray = aDes
End Function
Không bàn về giải thuật, bạn cứ xem cách tôi xử lý base 0, base 1 thì sẽ rõ: hoàn toàn không có bất kỳ con số (0 hay 1) gì được gán vào chỉ số index đầu tiên của mảng cả (trừ phi tôi đã định trước mảng đó thuộc chuẩn nào)
 

File đính kèm

Upvote 0
Tôi gửi bạn file dưới đây để tham khảo. Hàm sort 1 cột đơn giản thôi nhưng chắc ăn kết quả được bảo toàn
Mã:
Function Sort2DArray(ByVal Source2D, ByVal ColIndex As Long, Optional ByVal Order As Boolean = False)
  Dim aSrc, tmp
  Dim oArrList As Object
  Dim lR As Long, lC As Long, idx As Long, lPos As Long
  Dim lFstRow As Long, lEndRow As Long, lFstCol As Long, lEndCol As Long
  aSrc = Source2D
  lFstRow = LBound(aSrc, 1): lEndRow = UBound(aSrc, 1)
  lFstCol = LBound(aSrc, 2): lEndCol = UBound(aSrc, 2)
  Set oArrList = CreateObject("System.Collections.ArrayList")
  For lR = lFstRow To lEndRow
    tmp = aSrc(lR, ColIndex)
    oArrList.Add tmp
  Next
  oArrList.Sort
  If Order Then oArrList.Reverse
  ReDim aPos(oArrList.Count - 1)  'mảng chuẩn base 0
  ReDim aDes(lFstRow To lEndRow, lFstCol To lEndCol)
  For lR = lFstRow To lEndRow
    tmp = aSrc(lR, ColIndex)
    idx = oArrList.IndexOf(tmp, 0)
    lPos = idx [COLOR=#ff0000]+ lFstRow[/COLOR] + aPos(idx) ''vi tri tùy biến theo chuẩn base của mảng nguồn
    For lC = lFstCol To lEndCol
      aDes(lPos, lC) = aSrc(lR, lC)
    Next
    aPos(idx) = aPos(idx) + 1
  Next
  Sort2DArray = aDes
End Function
Không bàn về giải thuật, bạn cứ xem cách tôi xử lý base 0, base 1 thì sẽ rõ: hoàn toàn không có bất kỳ con số (0 hay 1) gì được gán vào chỉ số index đầu tiên của mảng cả (trừ phi tôi đã định trước mảng đó thuộc chuẩn nào)
code của bạn quá chuẩn, mình sẽ viết lại theo cách nầy
code viết theo kiểu đụng đâu chỉnh đó, hơi rối, đưa lên cho vui, sẽ chỉnh lại theo cách của bạn:=\+}}}}}%#^#$
 

File đính kèm

Upvote 0
Function Sort2DArray, dùng để sort theo 1 cột duy nhất
Source2D, dữ liệu nguồn nhiều dòng nhiều cột
HasTitle: Tiêu đề bảng, có tiêu đề: True, không có: False
ColIndex: thứ tự cột điều kiện Sort, đếm thứ tự từ cột đầu tiên (1, 2, 3 ....)
Order: Kiểu Sort, tăng dần: True, giảm dần: False
ShowTitle: Hiên tiêu đề bảng, cho hiện: True, không hiện: False

Function SortArray, dùng để sort theo tối đa theo 3 cột điều kiện
Mã:
Function Sort2DArray(ByVal Source2D, ByVal HasTitle As Boolean, ByVal ColIndex As Integer, _
        Optional ByVal Order As Boolean = True, Optional ByVal ShowTitle As Boolean = True)
'Sort theo 1 dieu kien, voi ColIndex là thu tu cot Sort dem tu cot dau tien
  Dim List As Object, Darr(), Arr(), SameArr(), tmp
  Dim i As Long, j As Long, idx As Long, lPos As Long
  Dim FistR As Integer, LastR As Long, FistC As Integer, LastC As Integer
  On Error GoTo Thoat
  Const Test_Source2D = 1
  If Test_Source2D = 2 Then
Thoat:
    MsgBox ("Source2D hoac các ColIndex khong dung" & Chr(13) & "Sort Data Khong duoc thuc hien")
    Sort2DArray = Source2D
    Exit Function
  End If
  Darr = Source2D
  FistR = LBound(Darr, 1): LastR = UBound(Darr, 1)
  FistC = LBound(Darr, 2): LastC = UBound(Darr, 2)
  Col = FistC + ColIndex - 1
  If HasTitle = False Then ShowTitle = True
  Set List = CreateObject("System.Collections.ArrayList")
  For i = FistR - HasTitle To LastR
    tmp = Darr(i, Col)
    If IsNumeric(tmp) Then tmp = CStr(String(15 - Len(CStr(tmp)), "0") & tmp)
    List.Add tmp
  Next
  List.Sort
  If Not Order Then List.Reverse
  If ShowTitle Then
    ReDim Arr(FistR To LastR, FistC To LastC)
    If HasTitle Then
      For j = FistC To LastC
        Arr(FistR, j) = Darr(FistR, j)
      Next j
    End If
  Else
    ReDim Arr(FistR To LastR + HasTitle, FistC To LastC)
  End If
  ReDim SameArr(List.Count - 1)
  For i = FistR - HasTitle To LastR
    tmp = Darr(i, Col)
    If IsNumeric(tmp) Then tmp = CStr(String(15 - Len(CStr(tmp)), "0") & tmp)
    idx = List.IndexOf(tmp, 0)
    lPos = idx + FistR + SameArr(idx)
    If ShowTitle Then lPos = lPos - HasTitle
    For j = FistC To LastC
      Arr(lPos, j) = Darr(i, j)
    Next
    SameArr(idx) = SameArr(idx) + 1
  Next
  Sort2DArray = Arr
  Set List = Nothing
End Function


Function SortArray(ByVal Source2D, ByVal HasTitle As Boolean, ByVal ColIndex1 As Integer, _
            Optional ByVal Order1 As Boolean = True, Optional ByVal ColIndex2 As Integer = -1245, _
            Optional ByVal Order2 As Boolean = True, Optional ByVal ColIndex3 As Integer = -1245, _
             Optional ByVal Order3 As Boolean = True, Optional ByVal ShowTitle As Boolean = True)
'Sort theo toi da 3 cot
  Dim Darr(), Scol As Integer
  On Error GoTo Thoat
  Const Test_Source2D = 1
  If Test_Source2D = 2 Then
Thoat:
    MsgBox ("Source2D hoac các ColIndex khong dung" & Chr(13) & "Sort Data Khong duoc thuc hien")
    SortArray = Source2D
    Exit Function
  End If
  Darr = Source2D
  Scol = UBound(Darr, 2) - LBound(Darr, 2) + 1  'So cot du lieu
  If ColIndex1 >= 1 And ColIndex1 <= Scol Then
    Darr = Sort2DArray(Darr, HasTitle, ColIndex1, Order1, ShowTitle)
    If ColIndex2 >= 1 Then
      If ColIndex2 <= Scol Then
        Darr = SortArray2Col(Darr, ColIndex1, ColIndex1, ColIndex2, Order2, HasTitle, ShowTitle)
        If ColIndex3 >= 1 And ColIndex3 <= Scol Then
          Darr = SortArray2Col(Darr, ColIndex1, ColIndex2, ColIndex3, Order3, HasTitle, ShowTitle)
        ElseIf ColIndex3 <> -1245 Then
          GoTo Thoat
        End If
      ElseIf ColIndex2 <> -1245 Then
        GoTo Thoat
      End If
    End If
    SortArray = Darr
  Else
    GoTo Thoat
  End If
End Function

Function SortArray2Col(ByVal Source2D, ByVal ColMain1 As Integer, ByVal ColMain2 As Integer, _
      ByVal ColIndex As Integer, ByVal Order As Boolean, ByVal HasTitle As Boolean, ByVal ShowTitle As Boolean)
  Dim Darr(), Arr()
  Dim i As Long, ir As Long, K As Long, StarR As Long, j As Integer, Tmp1, Tmp2
  Dim FistR As Integer, LastR As Long, FistC As Integer, LastC As Integer
  Darr = Source2D
  FistR = LBound(Darr, 1): LastR = UBound(Darr, 1)
  FistC = LBound(Darr, 2): LastC = UBound(Darr, 2)
  Col1 = FistC + ColMain1 - 1
  Col2 = FistC + ColMain2 - 1
  For i = FistR - HasTitle To LastR - 1
    If Darr(i, Col1) = Darr(i + 1, Col1) And Darr(i, Col2) = Darr(i + 1, Col2) Then
      StarR = i
      Tmp1 = Darr(i, Col1): Tmp2 = Darr(i, Col2)
      K = 0
      For ir = StarR To LastR
        If Darr(ir, Col1) = Tmp1 And Darr(ir, Col2) = Tmp2 Then
          K = K + 1
        Else
          Exit For
        End If
      Next ir
      ReDim Arr(1 To K, FistC To LastC)
      For ir = 1 To K
        For j = FistC To LastC
          Arr(ir, j) = Darr(StarR + ir - 1, j)
        Next j
      Next ir
      Arr = Sort2DArray(Arr, False, ColIndex, Order, True)
      For ir = 1 To K
        For j = FistC To LastC
          Darr(StarR + ir - 1, j) = Arr(ir, j)
        Next j
      Next ir
      i = i + K - 1
    End If
  Next i
  SortArray2Col = Darr
End Function
Các bạn góp ý dùm
 

File đính kèm

Upvote 0
Code sắp xếp tiếng Việt không đúng.
Giả sử cột B có dữ liệu:
Đinh Công Tráng
Đinh Công Trẵng
Đinh Công Trấng
Đinh Công Trẳng
Đinh Công Trầng
Đinh Công Trặng
Đinh Công Trậng
Đinh Công Trâng
Đinh Công Trẫng
Đinh Công Trẩng
Đinh Công Tràng
Đinh Công Trằng
Đinh Công Trang
Đinh Công Trạng
Đinh Công Trăng
Đinh Công Trãng
Đinh Công Trảng
Đinh Công Trắng

Sau khi sắp xếp phải được: các dòng có a, à, ả, ã, á, ạ phải liền nhau.
Tức phải có
Đinh Công Trang
Đinh Công Tràng
Đinh Công Trảng
Đinh Công Trãng
Đinh Công Tráng
Đinh Công Trạng

Tương tự với các dòng có ă, ằ, ẳ, ẵ, ắ, ặ và các dòng có â, ầ, ẩ, ẫ, ấ, ậ

Code dùng ArrayList sắp xếp tiếng Việt không chuẩn.
 
Upvote 0
Function Sort2DArray, dùng để sort theo 1 cột duy nhất
Source2D, dữ liệu nguồn nhiều dòng nhiều cột
HasTitle: Tiêu đề bảng, có tiêu đề: True, không có: False
ColIndex: thứ tự cột điều kiện Sort, đếm thứ tự từ cột đầu tiên (1, 2, 3 ....)
Order: Kiểu Sort, tăng dần: True, giảm dần: False
ShowTitle: Hiên tiêu đề bảng, cho hiện: True, không hiện: False

Các bạn góp ý dùm

Tôi nghĩ bạn có thể cải tiến để chỉ còn lại 2 function: 1 function chính + 1 function phụ trợ. Tôi nghĩ như thế là đủ.
Thậm chí nếu khéo léo, muốn sort bao nhiêu cột cũng không có vấn đề chứ không chỉ giới hạn ở 3 cột đâu
 
Upvote 0
Code sắp xếp tiếng Việt không đúng.
Giả sử cột B có dữ liệu:
Đinh Công Tráng
Đinh Công Trẵng
Đinh Công Trấng
Đinh Công Trẳng
Đinh Công Trầng
Đinh Công Trặng
Đinh Công Trậng
Đinh Công Trâng
Đinh Công Trẫng
Đinh Công Trẩng
Đinh Công Tràng
Đinh Công Trằng
Đinh Công Trang
Đinh Công Trạng
Đinh Công Trăng
Đinh Công Trãng
Đinh Công Trảng
Đinh Công Trắng

Sau khi sắp xếp phải được: các dòng có a, à, ả, ã, á, ạ phải liền nhau.
Tức phải có
Đinh Công Trang
Đinh Công Tràng
Đinh Công Trảng
Đinh Công Trãng
Đinh Công Tráng
Đinh Công Trạng
Tương tự với các dòng có ă, ằ, ẳ, ẵ, ắ, ặ và các dòng có â, ầ, ẩ, ẫ, ấ, ậ
Code dùng ArrayList sắp xếp tiếng Việt không chuẩn.
ArrayLisst sort ra kết quả giống cụ sort của Excel, còn sort theo chuẩn tiếng Việt chắc phải thêm Function chuyển mã
 
Upvote 0
Tôi nghĩ bạn có thể cải tiến để chỉ còn lại 2 function: 1 function chính + 1 function phụ trợ. Tôi nghĩ như thế là đủ.
Thậm chí nếu khéo léo, muốn sort bao nhiêu cột cũng không có vấn đề chứ không chỉ giới hạn ở 3 cột đâu
mình sẽ cố thử xem được không
giới hạn số cột là do khai báo trong Function, có cách nào khai báo mà nhập bao nhiêu tham số cũng được không?như dạng
Mã:
Function SortArray(ByVal Source2D, ByVal HasTitle As Boolean, ByVal ColIndex1 As Integer, _
            Optional ByVal Order1 As Boolean = True, Optional ByVal ColIndex2 As Integer = -1245, Optional ByVal Order2 As Boolean = True, _
            ??? cái gì đó để không cần liệt kê đủ n cột bất kỳ chẳng hạn ??? _
            , Optional ByVal ShowTitle As Boolean = True)
nếu không có cách khai báo thì vẫn phải giới hạn số cột
 
Upvote 0
mình sẽ cố thử xem được không
giới hạn số cột là do khai báo trong Function, có cách nào khai báo mà nhập bao nhiêu tham số cũng được không?như dạng
Mã:
Function SortArray(ByVal Source2D, ByVal HasTitle As Boolean, ByVal ColIndex1 As Integer, _
            Optional ByVal Order1 As Boolean = True, Optional ByVal ColIndex2 As Integer = -1245, Optional ByVal Order2 As Boolean = True, _
            ??? cái gì đó để không cần liệt kê đủ n cột bất kỳ chẳng hạn ??? _
            , Optional ByVal ShowTitle As Boolean = True)
nếu không có cách khai báo thì vẫn phải giới hạn số cột

Tôi đang nghĩ đến hướng cho ColIndex và Order vào 1 Array, chẳng
Mã:
SortArray(Source2D, Array(5,2,3), Array(True, False, True))
Thì có nghĩa là sort 3 cột 5, 2, 3 theo kiểu Tăng, giảm, tăng (chẳng hạn)
 
Lần chỉnh sửa cuối:
Upvote 0
Tôi đang nghĩ đến hướng cho ColIndex và Order vào 1 Array, chẳng
Mã:
SortArray(Source2D, Array(5,2,3), Array(True, False, True))
Thì có nghĩa là sort 3 cột 5, 2, 3 theo kiểu Tăng, giảm, tăng (chẳng hạn)
theo gợi ý và code mẩu của bạn, mình tạo 3 function, trong đó TestSame có thể ghép vào còn 2 nhưng nhìn hơi rối và dài quá
Mã:
Function SortArray(ByVal Source2D, ByVal HasTitle As Boolean, ByVal ColIndex As Variant, _
             Optional ByVal Order As Variant = True, Optional ByVal ShowTitle As Boolean = True)
  Dim Darr(), Arr(), TmpArr(), Test_Source2D As Boolean
  Dim i As Long, ir As Long, k As Long, StarR As Long, j As Integer, C As Integer, Tmp
  Dim FistR As Integer, LastR As Long, FistC As Integer, LastC As Integer
  Darr = Source2D
  FistR = LBound(Darr, 1): LastR = UBound(Darr, 1)
  FistC = LBound(Darr, 2): LastC = UBound(Darr, 2)
  On Error GoTo Thoat
'Xet dieu kien chay Sort
  If Not IsArray(Darr) Then GoTo Thoat
  If LastR - FistR + 1 + HasTitle < 2 Then GoTo Thoat
  If IsArray(ColIndex) Then
      With CreateObject("scripting.dictionary")
        For k = LBound(ColIndex) To UBound(ColIndex)
          .Add ColIndex(k), ""
          If Not IsNumeric(ColIndex(k)) Then GoTo Thoat
          ColIndex(k) = ColIndex(k) + FistC - 1  'thu tu ColIndex theo column Darr
          If ColIndex(k) < LBound(Darr, 2) Or ColIndex(k) > UBound(Darr, 2) Then GoTo Thoat
        Next k
      End With
      If IsArray(Order) Then
        For k = LBound(ColIndex) To UBound(ColIndex)
          If k > UBound(Order) Or Order(k) <> False Then Order(k) = True
        Next k
      Else
        If Order = False Then Tmp = False Else Tmp = True
          ReDim Order(LBound(ColIndex) To UBound(ColIndex))
          For k = LBound(Order) To UBound(Order)
            Order(k) = Tmp
          Next k
      End If
  Else
    If Not IsNumeric(ColIndex) Or IsArray(Order) Then GoTo Thoat
    ColIndex = ColIndex + FistC - 1  'thu tu ColIndex theo column Darr
    If ColIndex < LBound(Darr) Or ColIndex > UBound(Darr) Then GoTo Thoat
    If Order <> False Then Order = True
    Darr = Sort2DArray(Darr, HasTitle, ColIndex, Order, ShowTitle)
  End If
  If IsArray(ColIndex) Then
    Darr = Sort2DArray(Darr, HasTitle, ColIndex(0), Order(0), ShowTitle)
    If UBound(ColIndex) - LBound(ColIndex) = 0 Then GoTo KetQua
    For C = LBound(ColIndex) + 1 To UBound(ColIndex)
      For i = FistR - HasTitle To LastR - 1
        If TestSame(Darr, ColIndex, i, C, 0) Then
          StarR = i
          k = 0
          For ir = StarR To LastR
            If TestSame(Darr, ColIndex, ir, C, i) Then
              k = k + 1
            Else
              Exit For
            End If
          Next ir
          ReDim Arr(1 To k, FistC To LastC)
          For ir = 1 To k
            For j = FistC To LastC
              Arr(ir, j) = Darr(StarR + ir - 1, j)
            Next j
          Next ir
          Arr = Sort2DArray(Arr, False, ColIndex(C), Order(C), True)
          For ir = 1 To k
            For j = FistC To LastC
              Darr(StarR + ir - 1, j) = Arr(ir, j)
            Next j
          Next ir
          i = i + k - 1
        End If
      Next i
    Next C
  End If
KetQua:
  SortArray = Darr
  Exit Function
  If Test_Source2D Then
Thoat:
    MsgBox ("Source2D hoac các ColIndex khong dung" & Chr(13) & "Sort Data Khong duoc thuc hien")
    SortArray = Source2D
    Exit Function
  End If
End Function
Mã:
Function Sort2DArray(ByVal Source2D, ByVal HasTitle As Boolean, ByVal ColIndex As Integer, _
        Optional ByVal Order As Boolean = True, Optional ByVal ShowTitle As Boolean = True)
'Sort theo 1 dieu kien, voi ColIndex là thu tu cot Sort dem tu cot dau tien
  Dim List As Object, Darr(), Arr(), SameArr(), Tmp
  Dim i As Long, j As Long, idx As Long, lPos As Long
  Dim FistR As Integer, LastR As Long, FistC As Integer, LastC As Integer
  'On Error GoTo Thoat
  Darr = Source2D
  FistR = LBound(Darr, 1): LastR = UBound(Darr, 1)
  FistC = LBound(Darr, 2): LastC = UBound(Darr, 2)
  If HasTitle = False Then ShowTitle = True
  Set List = CreateObject("System.Collections.ArrayList")
  For i = FistR - HasTitle To LastR
    Tmp = Darr(i, ColIndex)
    If IsNumeric(Tmp) Then Tmp = CStr(String(15 - Len(CStr(Tmp)), "0") & Tmp)
    List.Add Tmp
  Next
  List.Sort
  If Not Order Then List.Reverse
  If ShowTitle Then
    ReDim Arr(FistR To LastR, FistC To LastC)
    If HasTitle Then
      For j = FistC To LastC
        Arr(FistR, j) = Darr(FistR, j)
      Next j
    End If
  Else
    ReDim Arr(FistR To LastR + HasTitle, FistC To LastC)
  End If
  ReDim SameArr(List.Count - 1)
  For i = FistR - HasTitle To LastR
    Tmp = Darr(i, ColIndex)
    If IsNumeric(Tmp) Then Tmp = CStr(String(15 - Len(CStr(Tmp)), "0") & Tmp)
    idx = List.IndexOf(Tmp, 0)
    lPos = idx + FistR + SameArr(idx)
    If ShowTitle Then lPos = lPos - HasTitle
    For j = FistC To LastC
      Arr(lPos, j) = Darr(i, j)
    Next
    SameArr(idx) = SameArr(idx) + 1
  Next
  Sort2DArray = Arr
  Set List = Nothing
End Function
Mã:
Function TestSame(ByVal Darr, ByVal ColIndex, ByVal i, ByVal C, ByVal iC)
  Dim j As Integer, Col As Integer, Tmp
  TestSame = True
  For j = 0 To C - 1
    Col = ColIndex(j)
    If iC = 0 Then Tmp = Darr(i + 1, Col) Else Tmp = Darr(iC, Col)
    If Darr(i, Col) <> Tmp Then
      TestSame = False
      Exit Function
    End If
  Next j
End Function
 

File đính kèm

Upvote 0
Chủ đề này không thấy tiếp tục, anh HieuCD có kết quả cuối cùng chưa?
Em đang muốn tìm cách sort nhiều cột trong mảng (tương tự add sort của excel) nhưng vẫn chưa tìm thấy, có ai đi ngang qua cho em xin một link, cảm ơn.
 
Upvote 0
Cái đó người ta dùng ArrayList và bản thân nó có chức năng này. Tuy nhiên, thấy dễ ăn vậy chứ thật ra chỉ áp dụng được bài của bạn, List đã được lọc duy nhất rồi nên sort xong còn "mò mò" ra được các phần tử ở cột khác chứ nếu để nguyên cái list chưa lọc duy nhất thì đố Arraylist mần được bài này
Mà cũng thấy lạ thật! Bạn HieuCD dùng ArrayList, bản thân nó cũng có chức năng lọc duy nhất, vậy sao phải thêm Dictionary vào chẳng hóa ra.. thừa???
----------------------
Tôi giải quyết bài toán theo hướng tổng quát: Mặc kệ danh sách có duy nhất hay không vẫn cứ sort được
1> Code trong Module:
Mã:
Function Sort2DArray(ByVal SourceArray, ByVal ColIndex As Long, ByVal Order As Boolean, ByVal HasTitle As Boolean)
  Dim tmpArr, i As Long, j As Long, SortArr, Item1, Item2, firstVal As Double
  Dim Arr, iR As Long, tmp(), n As Long, Chk As Boolean
  On Error Resume Next
  tmpArr = SourceArray: Arr = tmpArr
  ColIndex = ColIndex + LBound(tmpArr, 2) - 1
  firstVal = CDbl(tmpArr(LBound(tmpArr, 1) - HasTitle, ColIndex))
  Chk = firstVal > 0
  For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
    ReDim Preserve tmp(n)
    If Chk Then
      tmp(n) = CDbl(tmpArr(i, ColIndex))
    Else
      tmp(n) = CStr(tmpArr(i, ColIndex))
    End If
    n = n + 1
  Next
  SortArr = Sort1DArray(tmp, Order)
  With CreateObject("Scripting.Dictionary")
    For i = LBound(SortArr) To UBound(SortArr)
      If Chk Then
        If Not .Exists(CDbl(SortArr(i))) Then .Add CDbl(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      Else
        If Not .Exists(CStr(SortArr(i))) Then .Add CStr(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      End If
    Next
    For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
      If Chk Then
        iR = .Item(CDbl(tmpArr(i, ColIndex)))
      Else
        iR = .Item(CStr(tmpArr(i, ColIndex)))
      End If
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(iR, j) = tmpArr(i, j)
      Next
      If Chk Then
        .Item(CDbl(tmpArr(i, ColIndex))) = iR + 1
      Else
        .Item(CStr(tmpArr(i, ColIndex))) = iR + 1
      End If
    Next
  End With
  Sort2DArray = Arr
End Function
Function Sort1DArray(ByVal srcArr, ByVal Order As Boolean)
  Dim Item, tmpArr, Arr1, Arr2, n As Long, m As Long
  tmpArr = srcArr
  With CreateObject("System.Collections.ArrayList")
    For Each Item In tmpArr
      .Add Item
    Next
    .Sort
    Arr1 = .ToArray
    If Order = False Then
      ReDim Arr2(LBound(Arr1) To UBound(Arr1))
      For n = .Count To 1 Step -1
        Arr2(m) = Arr1(n - 1)
        m = m + 1
      Next
      Sort1DArray = Arr2
    Else
      Sort1DArray = Arr1
    End If
  End With
End Function
Function Unique2DArray(ByVal SourceArray, Optional ByVal ColIndex As Variant = 1)
  Dim aSrc, aCol, tmp As String, cItem
  Dim lR As Long, lC As Long, lUB1 As Long, lUB2 As Long, lLB1 As Long, lLB2 As Long, n As Long
  Dim dic As Object
  Set dic = CreateObject("Scripting.Dictionary")
  aSrc = SourceArray: aCol = ColIndex
  If Not IsArray(aCol) Then aCol = Array(aCol)
  lLB1 = LBound(aSrc, 1): lLB2 = LBound(aSrc, 2)
  lUB1 = UBound(aSrc, 1): lUB2 = UBound(aSrc, 2)
  ReDim aDes(1 To lUB2 + 1 - lLB2, 1 To 1)
  For lR = lLB1 To lUB1
    tmp = vbNullString
    If Len(CStr(aSrc(lR, 1))) Then
      For Each cItem In aCol
        tmp = tmp & vbBack & aSrc(lR, cItem)
      Next
      If Not dic.Exists(tmp) Then
        n = n + 1
        dic.Add tmp, lR
        ReDim Preserve aDes(1 To lUB2 + 1 - lLB2, 1 To n)
        For lC = lLB2 To lUB2
          aDes(lC, n) = aSrc(lR, lC)
        Next
      End If
    End If
  Next
  If n Then Unique2DArray = Transpose2DArray(aDes)
End Function
Function Transpose2DArray(ByVal SourceArray)
  Dim aSrc
  Dim lR As Long, lC As Long
  aSrc = SourceArray
  ReDim aDes(LBound(aSrc, 2) To UBound(aSrc, 2), LBound(aSrc, 1) To UBound(aSrc, 1))
  For lR = LBound(aSrc, 1) To UBound(aSrc, 1)
    For lC = LBound(aSrc, 2) To UBound(aSrc, 2)
      aDes(lC, lR) = aSrc(lR, lC)
    Next
  Next
  Transpose2DArray = aDes
End Function
2> Code trong Form:
Mã:
[COLOR=#ff0000]Private Sub Label1_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 1, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub Label2_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 2, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub UserForm_Initialize()
  Dim aSrc, aDes
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  aDes = Unique2DArray(aSrc, 1)
  ListBox1.List = aDes
End Sub[/COLOR]
Code trong Module tuy dài nhưng bạn mặc kệ nó đi, chỉ cần biết áp dụng hàm vào form là đủ (xem và khai triển chỗ màu đỏ là được rồi)
Lưu ý: Ở đây tôi viết cho bạn chức năng sort theo cách bạn bấm vào Label TÊN HÀNG hoặc ĐVT thì nó sẽ sort
Cám ơn thầy NDU
 
Upvote 0
Cái đó người ta dùng ArrayList và bản thân nó có chức năng này. Tuy nhiên, thấy dễ ăn vậy chứ thật ra chỉ áp dụng được bài của bạn, List đã được lọc duy nhất rồi nên sort xong còn "mò mò" ra được các phần tử ở cột khác chứ nếu để nguyên cái list chưa lọc duy nhất thì đố Arraylist mần được bài này
Mà cũng thấy lạ thật! Bạn HieuCD dùng ArrayList, bản thân nó cũng có chức năng lọc duy nhất, vậy sao phải thêm Dictionary vào chẳng hóa ra.. thừa???
----------------------
Tôi giải quyết bài toán theo hướng tổng quát: Mặc kệ danh sách có duy nhất hay không vẫn cứ sort được
1> Code trong Module:
Mã:
Function Sort2DArray(ByVal SourceArray, ByVal ColIndex As Long, ByVal Order As Boolean, ByVal HasTitle As Boolean)
  Dim tmpArr, i As Long, j As Long, SortArr, Item1, Item2, firstVal As Double
  Dim Arr, iR As Long, tmp(), n As Long, Chk As Boolean
  On Error Resume Next
  tmpArr = SourceArray: Arr = tmpArr
  ColIndex = ColIndex + LBound(tmpArr, 2) - 1
  firstVal = CDbl(tmpArr(LBound(tmpArr, 1) - HasTitle, ColIndex))
  Chk = firstVal > 0
  For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
    ReDim Preserve tmp(n)
    If Chk Then
      tmp(n) = CDbl(tmpArr(i, ColIndex))
    Else
      tmp(n) = CStr(tmpArr(i, ColIndex))
    End If
    n = n + 1
  Next
  SortArr = Sort1DArray(tmp, Order)
  With CreateObject("Scripting.Dictionary")
    For i = LBound(SortArr) To UBound(SortArr)
      If Chk Then
        If Not .Exists(CDbl(SortArr(i))) Then .Add CDbl(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      Else
        If Not .Exists(CStr(SortArr(i))) Then .Add CStr(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      End If
    Next
    For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
      If Chk Then
        iR = .Item(CDbl(tmpArr(i, ColIndex)))
      Else
        iR = .Item(CStr(tmpArr(i, ColIndex)))
      End If
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(iR, j) = tmpArr(i, j)
      Next
      If Chk Then
        .Item(CDbl(tmpArr(i, ColIndex))) = iR + 1
      Else
        .Item(CStr(tmpArr(i, ColIndex))) = iR + 1
      End If
    Next
  End With
  Sort2DArray = Arr
End Function
Function Sort1DArray(ByVal srcArr, ByVal Order As Boolean)
  Dim Item, tmpArr, Arr1, Arr2, n As Long, m As Long
  tmpArr = srcArr
  With CreateObject("System.Collections.ArrayList")
    For Each Item In tmpArr
      .Add Item
    Next
    .Sort
    Arr1 = .ToArray
    If Order = False Then
      ReDim Arr2(LBound(Arr1) To UBound(Arr1))
      For n = .Count To 1 Step -1
        Arr2(m) = Arr1(n - 1)
        m = m + 1
      Next
      Sort1DArray = Arr2
    Else
      Sort1DArray = Arr1
    End If
  End With
End Function
Function Unique2DArray(ByVal SourceArray, Optional ByVal ColIndex As Variant = 1)
  Dim aSrc, aCol, tmp As String, cItem
  Dim lR As Long, lC As Long, lUB1 As Long, lUB2 As Long, lLB1 As Long, lLB2 As Long, n As Long
  Dim dic As Object
  Set dic = CreateObject("Scripting.Dictionary")
  aSrc = SourceArray: aCol = ColIndex
  If Not IsArray(aCol) Then aCol = Array(aCol)
  lLB1 = LBound(aSrc, 1): lLB2 = LBound(aSrc, 2)
  lUB1 = UBound(aSrc, 1): lUB2 = UBound(aSrc, 2)
  ReDim aDes(1 To lUB2 + 1 - lLB2, 1 To 1)
  For lR = lLB1 To lUB1
    tmp = vbNullString
    If Len(CStr(aSrc(lR, 1))) Then
      For Each cItem In aCol
        tmp = tmp & vbBack & aSrc(lR, cItem)
      Next
      If Not dic.Exists(tmp) Then
        n = n + 1
        dic.Add tmp, lR
        ReDim Preserve aDes(1 To lUB2 + 1 - lLB2, 1 To n)
        For lC = lLB2 To lUB2
          aDes(lC, n) = aSrc(lR, lC)
        Next
      End If
    End If
  Next
  If n Then Unique2DArray = Transpose2DArray(aDes)
End Function
Function Transpose2DArray(ByVal SourceArray)
  Dim aSrc
  Dim lR As Long, lC As Long
  aSrc = SourceArray
  ReDim aDes(LBound(aSrc, 2) To UBound(aSrc, 2), LBound(aSrc, 1) To UBound(aSrc, 1))
  For lR = LBound(aSrc, 1) To UBound(aSrc, 1)
    For lC = LBound(aSrc, 2) To UBound(aSrc, 2)
      aDes(lC, lR) = aSrc(lR, lC)
    Next
  Next
  Transpose2DArray = aDes
End Function
2> Code trong Form:
Mã:
[COLOR=#ff0000]Private Sub Label1_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 1, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub Label2_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 2, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub UserForm_Initialize()
  Dim aSrc, aDes
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  aDes = Unique2DArray(aSrc, 1)
  ListBox1.List = aDes
End Sub[/COLOR]
Code trong Module tuy dài nhưng bạn mặc kệ nó đi, chỉ cần biết áp dụng hàm vào form là đủ (xem và khai triển chỗ màu đỏ là được rồi)
Lưu ý: Ở đây tôi viết cho bạn chức năng sort theo cách bạn bấm vào Label TÊN HÀNG hoặc ĐVT thì nó sẽ sort
Cái đó người ta dùng ArrayList và bản thân nó có chức năng này. Tuy nhiên, thấy dễ ăn vậy chứ thật ra chỉ áp dụng được bài của bạn, List đã được lọc duy nhất rồi nên sort xong còn "mò mò" ra được các phần tử ở cột khác chứ nếu để nguyên cái list chưa lọc duy nhất thì đố Arraylist mần được bài này
Mà cũng thấy lạ thật! Bạn HieuCD dùng ArrayList, bản thân nó cũng có chức năng lọc duy nhất, vậy sao phải thêm Dictionary vào chẳng hóa ra.. thừa???
----------------------
Tôi giải quyết bài toán theo hướng tổng quát: Mặc kệ danh sách có duy nhất hay không vẫn cứ sort được
1> Code trong Module:
Mã:
Function Sort2DArray(ByVal SourceArray, ByVal ColIndex As Long, ByVal Order As Boolean, ByVal HasTitle As Boolean)
  Dim tmpArr, i As Long, j As Long, SortArr, Item1, Item2, firstVal As Double
  Dim Arr, iR As Long, tmp(), n As Long, Chk As Boolean
  On Error Resume Next
  tmpArr = SourceArray: Arr = tmpArr
  ColIndex = ColIndex + LBound(tmpArr, 2) - 1
  firstVal = CDbl(tmpArr(LBound(tmpArr, 1) - HasTitle, ColIndex))
  Chk = firstVal > 0
  For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
    ReDim Preserve tmp(n)
    If Chk Then
      tmp(n) = CDbl(tmpArr(i, ColIndex))
    Else
      tmp(n) = CStr(tmpArr(i, ColIndex))
    End If
    n = n + 1
  Next
  SortArr = Sort1DArray(tmp, Order)
  With CreateObject("Scripting.Dictionary")
    For i = LBound(SortArr) To UBound(SortArr)
      If Chk Then
        If Not .Exists(CDbl(SortArr(i))) Then .Add CDbl(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      Else
        If Not .Exists(CStr(SortArr(i))) Then .Add CStr(SortArr(i)), i + LBound(tmpArr, 1) - HasTitle
      End If
    Next
    For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
      If Chk Then
        iR = .Item(CDbl(tmpArr(i, ColIndex)))
      Else
        iR = .Item(CStr(tmpArr(i, ColIndex)))
      End If
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(iR, j) = tmpArr(i, j)
      Next
      If Chk Then
        .Item(CDbl(tmpArr(i, ColIndex))) = iR + 1
      Else
        .Item(CStr(tmpArr(i, ColIndex))) = iR + 1
      End If
    Next
  End With
  Sort2DArray = Arr
End Function
Function Sort1DArray(ByVal srcArr, ByVal Order As Boolean)
  Dim Item, tmpArr, Arr1, Arr2, n As Long, m As Long
  tmpArr = srcArr
  With CreateObject("System.Collections.ArrayList")
    For Each Item In tmpArr
      .Add Item
    Next
    .Sort
    Arr1 = .ToArray
    If Order = False Then
      ReDim Arr2(LBound(Arr1) To UBound(Arr1))
      For n = .Count To 1 Step -1
        Arr2(m) = Arr1(n - 1)
        m = m + 1
      Next
      Sort1DArray = Arr2
    Else
      Sort1DArray = Arr1
    End If
  End With
End Function
Function Unique2DArray(ByVal SourceArray, Optional ByVal ColIndex As Variant = 1)
  Dim aSrc, aCol, tmp As String, cItem
  Dim lR As Long, lC As Long, lUB1 As Long, lUB2 As Long, lLB1 As Long, lLB2 As Long, n As Long
  Dim dic As Object
  Set dic = CreateObject("Scripting.Dictionary")
  aSrc = SourceArray: aCol = ColIndex
  If Not IsArray(aCol) Then aCol = Array(aCol)
  lLB1 = LBound(aSrc, 1): lLB2 = LBound(aSrc, 2)
  lUB1 = UBound(aSrc, 1): lUB2 = UBound(aSrc, 2)
  ReDim aDes(1 To lUB2 + 1 - lLB2, 1 To 1)
  For lR = lLB1 To lUB1
    tmp = vbNullString
    If Len(CStr(aSrc(lR, 1))) Then
      For Each cItem In aCol
        tmp = tmp & vbBack & aSrc(lR, cItem)
      Next
      If Not dic.Exists(tmp) Then
        n = n + 1
        dic.Add tmp, lR
        ReDim Preserve aDes(1 To lUB2 + 1 - lLB2, 1 To n)
        For lC = lLB2 To lUB2
          aDes(lC, n) = aSrc(lR, lC)
        Next
      End If
    End If
  Next
  If n Then Unique2DArray = Transpose2DArray(aDes)
End Function
Function Transpose2DArray(ByVal SourceArray)
  Dim aSrc
  Dim lR As Long, lC As Long
  aSrc = SourceArray
  ReDim aDes(LBound(aSrc, 2) To UBound(aSrc, 2), LBound(aSrc, 1) To UBound(aSrc, 1))
  For lR = LBound(aSrc, 1) To UBound(aSrc, 1)
    For lC = LBound(aSrc, 2) To UBound(aSrc, 2)
      aDes(lC, lR) = aSrc(lR, lC)
    Next
  Next
  Transpose2DArray = aDes
End Function
2> Code trong Form:
Mã:
[COLOR=#ff0000]Private Sub Label1_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 1, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub Label2_Click()
  Dim aDes
  Static bChk As Boolean
  bChk = Not bChk
  aDes = Me.ListBox1.List
  aDes = Sort2DArray(aDes, 2, bChk, False)
  Me.ListBox1.List = aDes
End Sub
Private Sub UserForm_Initialize()
  Dim aSrc, aDes
  aSrc = Sheets("Nhap").Range("C2:D1000").Value
  aDes = Unique2DArray(aSrc, 1)
  ListBox1.List = aDes
End Sub[/COLOR]
Code trong Module tuy dài nhưng bạn mặc kệ nó đi, chỉ cần biết áp dụng hàm vào form là đủ (xem và khai triển chỗ màu đỏ là được rồi)
Lưu ý: Ở đây tôi viết cho bạn chức năng sort theo cách bạn bấm vào Label TÊN HÀNG hoặc ĐVT thì nó sẽ sort
Cám ơn anh ndu96081631, Hàm của anh đã giúp tôi rất nhiều trong công việc.
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom