Function tạo tổ hợp N phần tử từ mảng dữ liệu nhiều dòng nhiều cột

Liên hệ QC

HieuCD

Chuyên gia GPE
Tham gia
14/9/10
Bài viết
9,756
Được thích
23,030
Nhiều bạn có nhu cầu tạo tổ hợp, mình tạo Function CreateToHop( mảng du lieu ), các bạn góp ý thêm cho hoàn chỉnh :)
Mã:
Function CreateToHop(ByVal sArr As Variant) As Variant
'CreateToHop: Liet ke to hop N phan tu cua mang "sArr"
'sArr: Là Array hoac Range, neu khac se tra ve "Empty"
  Dim aRow(), Res(), sCol&, sRow&, N&, i&, j&, iR&

  If TypeName(sArr) = "Variant()" Then
    sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
  ElseIf TypeName(sArr) = "Range" Then
    sRow = sArr.Rows.Count:   sCol = sArr.Columns.Count
  Else
    Exit Function
  End If
  ReDim aRow(1 To sCol + 1)
  aRow(sCol + 1) = 1
  For j = sCol To 1 Step -1
    aRow(j) = sRow * aRow(j + 1)
  Next j
  N = aRow(1)
  ReDim Res(1 To N, 1 To sCol)
  For i = 1 To N
    For j = 1 To sCol
      iR = ((i - 1) Mod aRow(j)) \ aRow(j + 1) + 1 'Thu tu dong du lieu
      Res(i, j) = sArr(iR, j)
    Next j
  Next i
  CreateToHop = Res
End Function
Sub ví dụ
Mã:
Sub Main()
  Dim Res As Variant
  Range("G2:K1000000").ClearContents
  Res = CreateToHop(Range("A2:C4").Value)
  Res = CreateToHop(Range("A2:C4"))
  If Res <> Empty Then Range("G2").Resize(UBound(Res, 1), UBound(Res, 2)) = Res
End Sub
 

File đính kèm

  • Book1 (3).xlsm
    281.7 KB · Đọc: 26
ElseIf TypeName(sArr) = "Range" Then
sRow = sArr.Rows.Count: sCol = sArr.Columns.Count
Else
Ở trên có sẵn mảng sArr thì có thể chép luôn Range vào sArr cho đẹp anh. :)
PHP:
ElseIf TypeName(sArr) = "Range" Then 
If sArr.Count=1 Then Exit Function
sArr = sArr.Value
sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
Else
Trong Sub Main() em chạy bị lỗi, chỉnh lại dòng cuối một xíu.

PHP:
If TypeName(Res) <> "Empty" Then
 
Upvote 0
Ở trên có sẵn mảng sArr thì có thể chép luôn Range vào sArr cho đẹp anh. :)
PHP:
ElseIf TypeName(sArr) = "Range" Then
If sArr.Count=1 Then Exit Function
sArr = sArr.Value
sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
Else
Trong Sub Main() em chạy bị lỗi, chỉnh lại dòng cuối một xíu.

PHP:
If TypeName(Res) <> "Empty" Then
Theo gợi ý của bạn @befaint , và thêm phần bẩy lổi mảng 1 chiều
Chúc các bạn 1 ngày vui :)
Mã:
Function CreateToHop(ByVal sArr As Variant) As Variant
'CreateToHop: Liet ke to hop N phan tu cua "Mang" 2 chieu "sArr"
'sArr: Là Array hoac Range, neu khac se tra ve "Empty"
  Dim aRow(), Res(), sCol&, sRow&, N As Double, i As Double, j&, iR&, tmp
 
  On Error Resume Next
  If TypeName(sArr) = "Range" Then
    If sArr.Count = 1 Then
      tmp = sArr.Value
      ReDim sArr(1 To 1, 1 To 1)
      sArr(1, 1) = tmp
    Else
      sArr = sArr.Value
    End If
  End If
  sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
  If Err.Number > 0 Then Exit Function
 
  ReDim aRow(1 To sCol + 1)
  aRow(sCol + 1) = 1
  For j = sCol To 1 Step -1
    aRow(j) = sRow * aRow(j + 1)
  Next j
  N = aRow(1)
  ReDim Res(1 To N, 1 To sCol)
  For i = 1 To N
    For j = 1 To sCol
      iR = ((i - 1) Mod aRow(j)) \ aRow(j + 1) + 1 'Thu tu dong du lieu
      Res(i, j) = sArr(iR, j)
    Next j
  Next i
  CreateToHop = Res
End Function

Mã:
Sub Main()
  Dim Res As Variant
  Range("E2:G1000000").ClearContents
  Res = CreateToHop(Range("A2:C4").Value)
  Res = CreateToHop(Range("A2:C4"))
  If TypeName(Res) <> "Empty" Then Range("E2").Resize(UBound(Res, 1), UBound(Res, 2)) = Res
End Sub
File đính kèm có thêm phần nhập công thức trên bảng tính
 

File đính kèm

  • Book1 (3).xlsm
    283.4 KB · Đọc: 18
Upvote 0
Theo gợi ý của bạn @befaint , và thêm phần bẩy lổi mảng 1 chiều
Chúc các bạn 1 ngày vui :)
Mã:
Function CreateToHop(ByVal sArr As Variant) As Variant
'CreateToHop: Liet ke to hop N phan tu cua "Mang" 2 chieu "sArr"
'sArr: Là Array hoac Range, neu khac se tra ve "Empty"
  Dim aRow(), Res(), sCol&, sRow&, N As Double, i As Double, j&, iR&, tmp

  On Error Resume Next
  If TypeName(sArr) = "Range" Then
    If sArr.Count = 1 Then
      tmp = sArr.Value
      ReDim sArr(1 To 1, 1 To 1)
      sArr(1, 1) = tmp
    Else
      sArr = sArr.Value
    End If
  End If
  sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
  If Err.Number > 0 Then Exit Function

  ReDim aRow(1 To sCol + 1)
  aRow(sCol + 1) = 1
  For j = sCol To 1 Step -1
    aRow(j) = sRow * aRow(j + 1)
  Next j
  N = aRow(1)
  ReDim Res(1 To N, 1 To sCol)
  For i = 1 To N
    For j = 1 To sCol
      iR = ((i - 1) Mod aRow(j)) \ aRow(j + 1) + 1 'Thu tu dong du lieu
      Res(i, j) = sArr(iR, j)
    Next j
  Next i
  CreateToHop = Res
End Function

Mã:
Sub Main()
  Dim Res As Variant
  Range("E2:G1000000").ClearContents
  Res = CreateToHop(Range("A2:C4").Value)
  Res = CreateToHop(Range("A2:C4"))
  If TypeName(Res) <> "Empty" Then Range("E2").Resize(UBound(Res, 1), UBound(Res, 2)) = Res
End Sub
File đính kèm có thêm phần nhập công thức trên bảng tính
Còn trường hợp 1 chiều nữa anh
Mã:
If sArr.Columns.Count = 1 or sArr.Rows.Count Then
 
Upvote 0
Theo gợi ý của bạn @befaint , và thêm phần bẩy lổi mảng 1 chiều
Chúc các bạn 1 ngày vui :)
Mã:
Function CreateToHop(ByVal sArr As Variant) As Variant
'CreateToHop: Liet ke to hop N phan tu cua "Mang" 2 chieu "sArr"
'sArr: Là Array hoac Range, neu khac se tra ve "Empty"
  Dim aRow(), Res(), sCol&, sRow&, N As Double, i As Double, j&, iR&, tmp

  On Error Resume Next
  If TypeName(sArr) = "Range" Then
    If sArr.Count = 1 Then
      tmp = sArr.Value
      ReDim sArr(1 To 1, 1 To 1)
      sArr(1, 1) = tmp
    Else
      sArr = sArr.Value
    End If
  End If
  sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
  If Err.Number > 0 Then Exit Function

  ReDim aRow(1 To sCol + 1)
  aRow(sCol + 1) = 1
  For j = sCol To 1 Step -1
    aRow(j) = sRow * aRow(j + 1)
  Next j
  N = aRow(1)
  ReDim Res(1 To N, 1 To sCol)
  For i = 1 To N
    For j = 1 To sCol
      iR = ((i - 1) Mod aRow(j)) \ aRow(j + 1) + 1 'Thu tu dong du lieu
      Res(i, j) = sArr(iR, j)
    Next j
  Next i
  CreateToHop = Res
End Function

Mã:
Sub Main()
  Dim Res As Variant
  Range("E2:G1000000").ClearContents
  Res = CreateToHop(Range("A2:C4").Value)
  Res = CreateToHop(Range("A2:C4"))
  If TypeName(Res) <> "Empty" Then Range("E2").Resize(UBound(Res, 1), UBound(Res, 2)) = Res
End Sub
File đính kèm có thêm phần nhập công thức trên bảng tính
Mạnh xem thấy thuật toán rất hay đó ... tuy nhiên chưa hình dung ra ứng dụng nó vào việc gì là hay nhất
Mở rộng thêm chút nữa đi cho code đó nó đi vào thực tế
Cảm ơn nhiều
 
Upvote 0
Còn trường hợp 1 chiều nữa anh
Mã:
If sArr.Columns.Count = 1 or sArr.Rows.Count Then
"Range" luôn là "mảng" 2 chiều, nếu là "Range" mình xét hết kể cả chỉ có 1 "ô".
Mạnh xem thấy thuật toán rất hay đó ... tuy nhiên chưa hình dung ra ứng dụng nó vào việc gì là hay nhất
Mở rộng thêm chút nữa đi cho code đó nó đi vào thực tế
Cảm ơn nhiều
Mình tạo Function từ ý tưởng bài #1334 https://www.giaiphapexcel.com/diendan/threads/các-câu-hỏi-về-mảng-trong-vba-array.46834/page-67
Áp dụng khi phải dùng thuật toán "Vét cạn"
Function thêm tùy chọn loại bỏ ô rổng (Empty) trong vùng dữ liệu, số dòng mỗi cột có thể khác nhau
Luu ý: Khi loại ô rổng, mỗi cột phải có ít nhất 1 giá trị khác rổng, nếu không Function trả về giá trị "Empty"
Mã:
Function CreateToHop(ByVal sArr As Variant, Optional ByVal bNotBlank = False) As Variant
'CreateToHop: Liet ke to hop N phan tu cua "Mang" 2 chieu "sArr"
'sArr: Là Array hoac Range, neu khac se tra ve "Empty"
'bNotBlank: Là giá tri luan ly, mac dinh = False lay ca gia tri "Empty"
'bNotBlank = True: Loai bo gia tri "Empty", neu có Cot chi co gia tri "Empty", Function tra ve "Empty"
  Dim aRow(), Res(), sCol&, sRow&, N As Double, i As Double, j&, iR&, tmp

  On Error Resume Next
  If TypeName(sArr) = "Range" Then
    If sArr.Count = 1 Then
      tmp = sArr.Value
      ReDim sArr(1 To 1, 1 To 1)
      sArr(1, 1) = tmp
    Else
      sArr = sArr.Value
    End If
  End If
  sRow = UBound(sArr, 1):   sCol = UBound(sArr, 2)
  If Err.Number > 0 Then Exit Function

  Call AddValue_aRow(sArr, aRow, sRow, sCol, bNotBlank)
  N = aRow(1)
  If N = 0 Then Exit Function
  ReDim Res(1 To N, 1 To sCol)
  For i = 1 To N
    For j = 1 To sCol
      iR = ((i - 1) Mod aRow(j)) \ aRow(j + 1) + 1 'Thu tu dong du lieu
      If sArr(iR, j) = Empty Then Res(i, j) = "" Else Res(i, j) = sArr(iR, j)
    Next j
  Next i
  CreateToHop = Res
End Function

Private Sub AddValue_aRow(sArr, aRow, sRow, sCol, bNotBlank)
  Dim i&, j&, k&, tmp
  ReDim aRow(1 To sCol + 1)
  aRow(sCol + 1) = 1
  If bNotBlank = False Then
    For j = sCol To 1 Step -1
      aRow(j) = sRow * aRow(j + 1)
    Next j
  Else
    For j = sCol To 1 Step -1
      k = 0
      For i = 1 To sRow
        tmp = sArr(i, j)
        If Len(tmp) Then
          sArr(i, j) = Empty
          k = k + 1
          sArr(k, j) = tmp
        End If
      Next i
      If k > 0 Then aRow(j) = k * aRow(j + 1)
    Next j
  End If
End Sub
 

File đính kèm

  • Book1 (3).xlsm
    286.7 KB · Đọc: 25
Lần chỉnh sửa cuối:
Upvote 0
CreateToHop(A1:A3) => sArr(1 to 3, 1 To 1) Kết quả
 
Upvote 0
Function ghép các giá trị từng cột với giá trị của các cột khác, nếu chỉ có 1 cột sẽ trả về kết quả của cột đó
Theo bạn, trường hợp CreateToHop(A1:A3) kết quả nên như thế nào hợp lý hơn?
Em nghĩ cái hoán đổi, sắp xếp gép Item vậy là hợp lý rồi hihih
 
Upvote 0
Web KT
Back
Top Bottom