Các câu hỏi về mảng trong VBA (Array)

Liên hệ QC

viehoai

Thành viên gắn bó
Tham gia
22/5/09
Bài viết
2,600
Được thích
2,907
Xin các anh chị giúp đỡ Code Gán các giá trị của một Range là các phần tử của Mãng
Ví dụ: Tôi có các giá trị của Range("A1:A10"). Tôi muốn viết code để gán giá trị của các cells từ A1:A10 là các phần tử của Mãng Arr chẳn hạn.
Xin cảm ơn các anh chị
 
Về đoạn code này
PHP:
ReDim Arr(1 To UBound(sArray, 1), 1 To 2)

Có phải là sau khi chế biến, vùng đích chỉ còn 2 cột và số dòng bị co lại mình phải redim
và Mảng mới Arr có
Số dòng mới tương đương là
1 To UBound(sArray, 1) và
Số cột mới là 1 To 2

Chính xác là như vậy đó.
 
Upvote 0
Về đoạn code này
PHP:
ReDim Arr(1 To UBound(sArray, 1), 1 To 2)

Có phải là sau khi chế biến, vùng đích chỉ còn 2 cột và số dòng bị co lại mình phải redim
và Mảng mới Arr có
Số dòng mới tương đương là
1 To UBound(sArray, 1) và
Số cột mới là 1 To 2
Nếu bạn biết trước chỉ có 2 dữ liệu cần lấy thì lúc khai báo bạn khỏi Redim cũng được
Dim Arr(1 to 2, 1 to 2) là đủ
Còn khai báo ReDim Arr(1 To UBound(sArray, 1), 1 To 2) là dư nhiều lắm tốn bộ nhớ nữa và tương đương ReDim Arr(1 To 8, 1 To 2). Mà khai báo vậy cho chắc ăn muốn khai báo đủ thì dùng hàm đếm duy nhất cũng được
 
Upvote 0
Về đoạn code này
PHP:
ReDim Arr(1 To UBound(sArray, 1), 1 To 2)

Có phải là sau khi chế biến, vùng đích chỉ còn 2 cột và số dòng bị co lại mình phải redim
và Mảng mới Arr có
Số dòng mới tương đương là
1 To UBound(sArray, 1) và
Số cột mới là 1 To 2
Không đúng vì lúc đó chưa chế biến gì cả
Khai báo lại mảng Arr có số dòng bằng với số dòng của sArray vì lúc đó mình chưa biết số dòng chính xác của mảng Arr ( với đề bài này, số dòng của Arr luôn nhỏ hơn số dòng của sArray)
Sau khi chạy xong vòng lặp, hoàn thành em "Đít to" lúc đó mới biết chính xác số dòng của Arr
Thân
 
Upvote 0
Code của thầy ndu hoàn toàn đúng với yêu cầu. Vấn đề ở chỗ định dạng dữ liệu ở cột C không đồng nhất. Chỉ cần chỉnh 1 tí trong code là có thể khắc phục tình trạng này:
Mã:
If [B]CStr[/B](sArray(lR, 3)) = Target.Value Then
Không được!
Chính là tôi cố tình để y nguyên thế đấy ---> Text và Number phải có sự phân biệt chứ ---> Nếu người dùng nhập sai thì đấy là lỗi của họ, không phải do code mà ra
-----------------------------------
Nếu bạn biết trước chỉ có 2 dữ liệu cần lấy thì lúc khai báo bạn khỏi Redim cũng được
Dim Arr(1 to 2, 1 to 2) là đủ
Còn khai báo ReDim Arr(1 To UBound(sArray, 1), 1 To 2) là dư nhiều lắm tốn bộ nhớ nữa và tương đương ReDim Arr(1 To 8, 1 To 2). Mà khai báo vậy cho chắc ăn muốn khai báo đủ thì dùng hàm đếm duy nhất cũng được
Chúng ta có nhiều cách để tạo ra mảng kết quả:
1> Khai báo dư như tôi đã làm
2> Sau khi chạy xong kết quả, lại thêm 1 vòng lập nữa để "tỉa" cho kích thước vừa đủ
3> Dùng hàm đếm như bạn nói
Cho dù là cách nào thì tốc độ cũng gần như nhau thôi (vì cách nào cũng phải chịu "tốn" cái gì đó)
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng ra bài này phải nên giải quyết bằng PivotTable (vì đó mới là công cụ phù hợp)
Còn nếu viết code VBA và viết ở mức tổng quát nhất e rằng sẽ rất dài dòng
Vậy tôi viết theo đúng dữ liệu trong file của bạn nhé (dùng sự kiện Change)
PHP:
Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Address = "$F$11" Then
    Dim sArray, Arr(), tmp1, tmp2
    Dim lR As Long, n As Long
    sArray = Range("A2:C10000").Value
    ReDim Arr(1 To UBound(sArray, 1), 1 To 2)
    Range("G13:H10000").ClearContents
    With CreateObject("Scripting.Dictionary")
      For lR = 1 To UBound(sArray, 1)
        If sArray(lR, 3) = Target.Value Then
          tmp1 = sArray(lR, 1): tmp2 = sArray(lR, 2)
          If Not .Exists(tmp1) Then
            n = n + 1 ' để đếm số phần tử duy nhất
            .Add tmp1, n
            Arr(n, 1) = tmp1
            Arr(n, 2) = tmp2
          Else
            Arr(.Item(tmp1), 2) = Arr(.Item(tmp1), 2) + tmp2
          End If
        End If
      Next
    End With
    If n Then Range("G13").Resize(n, 2).Value = Arr
  End If
End Sub

Trong đoạn code trên có 2 chỗ em chưa hiểu. Giải thích giúp em nhé
ý 1:
Em hiểu n dùng để đếm số phần tử duy nhất nhưng cấu trúc câu
PHP:
 .Add tmp1, n
Thì n đứng sau Tmp1 hiểu và đọc như nào?

Ý 2
PHP:
Arr(.Item(tmp1), 2)
Câu .Item(tmp1) trả về số thứ tự trong mảng Arr phải không? (em hiểu như hàm match dùng trong INdex?/???
 
Upvote 0
Em hỏi xong thì đã tự hiểu đuợc 2 ý trên rồi. Giờ em xin hỏi một ví dụ khác về mảng
Ví dụ 3: Ghép hai mảng để tạo 1 mảng tạm


Tên-------------Loại-------------Tiền
A1---------------USD-------------10
A1---------------USD-------------10
A1---------------VND-------------10
A1---------------VND-------------10
A1---------------USD-------------10

Kết Quả
Tên-------------Loại-------------Tiền
A1---------------USD-------------30
A1---------------VND-------------20



Thay vì xét mảng 1 thỏa mãn đk1 và mảng 2 thỏa mãn đk2 --> tỉnh tổng

giờ em có thể nối mảng 1 và mảng mảng 2 thành một mảng mới (Mảng tạm). Và mảng này mình chỉ cần xét 1 điều kiện thôi. Thì làm như nào?

Để tránh phải xét 2 điều kiện, em nối Mảng Tên & Loại lại rồi xét duy nhất sau đó tính tổng
thì cách ghep mảng tạm này như nào?

XIn chỉ giúp
 
Lần chỉnh sửa cuối:
Upvote 0
Em hỏi xong thì đã tự hiểu đuợc 2 ý trên rồi. Giờ em xin hỏi một ví dụ khác về mảng
Ví dụ 3: Ghép hai mảng để tạo 1 mảng tạm


Tên-------------Loại-------------Tiền
A1---------------USD-------------10
A1---------------USD-------------10
A1---------------VND-------------10
A1---------------VND-------------10
A1---------------USD-------------10

Kết Quả
Tên-------------Loại-------------Tiền
A1---------------USD-------------30
A1---------------VND-------------20



Thay vì xét mảng 1 thỏa mãn đk1 và mảng 2 thỏa mãn đk2 --> tỉnh tổng

giờ em có thể nối mảng 1 và mảng mảng 2 thành một mảng mới (Mảng tạm). Và mảng này mình chỉ cần xét 1 điều kiện thôi. Thì làm như nào?

Để tránh phải xét 2 điều kiện, em nối Mảng Tên & Loại lại rồi xét duy nhất sau đó tính tổng
thì cách ghep mảng tạm này như nào?

XIn chỉ giúp
Thì cứ ghép bình thường bằng toán tử & thôi mà
Mình tạo một biến Ghep cho bạn dễ hình dung
Mã:
Public Sub DitTo()
    Dim Vung, I, d, K, Mg(), Ghep, kK
    Set d = CreateObject("scripting.dictionary")
    Vung = Range([A3], [A10000].End(xlUp)).Resize(, 3).Value
    ReDim Mg(1 To UBound(Vung, 1), 1 To UBound(Vung, 2))
        For I = 1 To UBound(Vung)
            Ghep = Vung(I, 1) & Vung(I, 2)
                If Not d.exists(Ghep) Then
                    K = K + 1
                    d.Add Ghep, K
                    Mg(K, 1) = Vung(I, 1): Mg(K, 2) = Vung(I, 2): Mg(K, 3) = Vung(I, 3)
                Else
                    kK = d.Item(Ghep)
                    Mg(kK, 3) = Mg(kK, 3) + Vung(I, 3)
                End If
        Next I
    [E3].Resize(1000, UBound(Vung, 2)).ClearContents
    [E3].Resize(UBound(Mg), UBound(Vung, 2)) = Mg
End Sub
 

File đính kèm

  • vidu3.xls
    25 KB · Đọc: 117
Upvote 0
Trong lúc chờ đợi em mầy mò 1 code (dựa trên code của Anh NDU) nhưng code không chạy
Chỉ giúp em code sai ở đâu nhé. Em sẽ học hỏi nhiều qua ví dụ này

PHP:
  Target.Address = "$B$1" Then
     ' cho hang nhap
             Dim sArray, Arr(), tmpTen, tmpTien, tmpLoai
             Dim lR As Long, n As Long
           ' sArray = Sheet1.Range("A2:C1000").Value

             ReDim Arr(1 To UBound(sArray, 1), 1 To 4)
             Sheet2.Range("A5:T10000").ClearContents
             With CreateObject("Scripting.Dictionary")
               For lR = 1 To UBound(sArray, 1)
                 If sArray(lR, 20) = Target.Value Then
                   tmpTen = sArray(lR, 1): tmpTien = -sArray(lR, 5): tmpLoai = sArray(lR, 6)
                   If Not .Exists(tmpTen) Then
                    
                     n = n + 1
                     .Add tmpTen, n
                     Arr(n, 1) = tmpTen
                     Arr(n, 3) = tmpLoai
                     Arr(n, 4) = tmpTien
                 
                    
                   Else
                     ' neu co nhieu hon 1 Ten
                             If Not .exits(tmpLoai) Then ' neu chi co 1 loai ngoai te
                                 m = .Item(tmpTen) + 1
                                 .Add tmpLoai, m
                                  Arr(m, 1) = tmpTen
                                  Arr(m, 3) = tmpLoai
                                  Arr(m, 4) = tmpTien
                              Else
                                
                                 Arr(m, 4) = Arr(m, 4) + tmpTien
                              End If
                   End If
                 End If
               Next
              
             End With
             If m Then Sheet2.Range("A5").Resize(n, 4).Value = Arr
 
Lần chỉnh sửa cuối:
Upvote 0
Em hỏi xong thì đã tự hiểu đuợc 2 ý trên rồi. Giờ em xin hỏi một ví dụ khác về mảng
Để tránh phải xét 2 điều kiện, em nối Mảng Tên & Loại lại rồi xét duy nhất sau đó tính tổng
thì cách ghep mảng tạm này như nào?
XIn chỉ giúp
PHP:
Sub testcong1()
Dim d As Object, Arr(), sArr, lRow As Long, lR As Long, lC As Long, item, KQ
Set d = CreateObject("Scripting.Dictionary")
With Sheets("sheet1")
.Range("K2:M100").Clear
sArr = .Range("A1:C6").Value
ReDim Arr(1 To UBound(sArr, 1), 1 To UBound(sArr, 2))
    For lRow = 1 To UBound(sArr, 1)
    item = sArr(lRow, 1) & " " & sArr(lRow, 2)
        If Not d.Exists(item) Then
            lR = lR + 1
            d.Add item, lR
            KQ = Split(item, " ", 2)
            Arr(lR, 1) = KQ(0)
            Arr(lR, 2) = KQ(1)
            Arr(lR, 3) = sArr(lRow, 3)
        Else
            Arr(d.item(item), 3) = Arr(d.item(item), 3) + sArr(lRow, 3)
        End If
    Next lRow
.Range("K2").Resize(lR, 3).Value = Arr
End With
End Sub
Ngoài cách của Chú Concongia mình có thể dùng hàm Split trực tiếp trên phép nối mảng cũng được
 

File đính kèm

  • Congdon.xls
    39 KB · Đọc: 99
Upvote 0
Cám ơn Anh Hùng
Cũng ví dụ trên, nhưng em thử để các cột không liên tục nhau thì không được
Ví dụ
Vùng dữ liệu gốc có 27 cột

Cột tên = cột 8 (cột H)
Cột Loại = Cột 13
Cột Tiền = Cột 12

Em thử ghép nhưng nó báo lỗi ở NEXT

PHP:
Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Address = "$B$1" Then
        Dim dg As Long
        Dim d As Object, Arr(), sArr, lRow As Long, lR As Long, item
        Set d = CreateObject("Scripting.Dictionary")
        dg = Sheet1.[A99000].End(xlUp).Row
        Sheet5.Range("A5:T65000").ClearContents
        With Sheet1
        sArr = .Range("A5:AA" & dg).Value
        ReDim Arr(1 To UBound(sArr, 1), 1 To UBound(sArr, 27))
           
            For lRow = 1 To UBound(sArr, 1)
            If sArr(lRow, 27) = Target.Value Then
                item = sArr(lRow, 8) & sArr(lRow, 13)
                If Not d.Exists(item) Then
                    lR = lR + 1
                    d.Add item, lR
                    Arr(lR, 1) = sArr(lRow, 8)
                    Arr(lR, 3) = sArr(lRow, 13)
                    Arr(lR, 4) = sArr(lRow, 12)
                Else
                    Arr(d.item(item), 4) = Arr(d.item(item), 4) + sArr(lRow, 12)
                End If
            Next lRow
        Sheet5.Range("A5").Resize(lR, 4).Value = Arr
        End With

  End If
 
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn Anh Hùng
Cũng ví dụ trên, nhưng em thử để các cột không liên tục nhau thì không được
Ví dụ
Vùng dữ liệu gốc có 27 cột

Cột tên = cột 8 (cột H)
Cột Loại = Cột 13
Cột Tiền = Cột 12

Em thử ghép nhưng nó báo lỗi ở NEXT
Với những bài toán có sự tính toán tương đối phức tạp, bạn nên đưa file lên để test cho dễ nhé
 
Upvote 0
Em gửi lại ví dụ nhé
 

File đính kèm

  • Hoc_Array .xls
    89 KB · Đọc: 99
Lần chỉnh sửa cuối:
Upvote 0
Em gửi lại ví dụ nhé
Code của bạn chạy được mà
Chỉ là dưới cuối code có ghi dòng Option Base 1 ---> Điều này là không được phép
Xem thông báo lỗi cũng thấy nó nói rằng "Chỉ có comment mới được ghi ở dưới các dòng End Sub, End Function.... "
Vậy, xóa đoạn Option Base 1 ở cuối code là được rồi
Ngoài ra:
- Cũng không cần Option Base 1 ở đầu code luôn, vì mảng được tạo thành từ Range luôn có Base =1, bất chấp Option Base đầu code là bao nhiêu
- Option Base này chỉ có tác dụng với mảng do bạn tự tạo ra (ví dụ biến Arr trong code của bạn)... nhưng bạn đã ReDim Arr(1 to... ) nên cũng đã khai báo nó là Base 1 rồi còn gì
 
Lần chỉnh sửa cuối:
Upvote 0
Em gửi lại ví dụ nhé
Bạn làm gì 2 đoạn dữ vậy chỉ cần duyệt qua từng sheet rồi gán kết quả vào sheet tổng hợp là được rồi mà đọc không hiểu thật đó bạn nói Bill là những ký hiêu IMP, EXP ??? hay là số Invoice vậy? Nếu sai thì mình thấy chỉ có EXP000003 còn IMP000003 thì sai số tổng thôi mà
 
Upvote 0
Code của bạn chạy được mà
Chỉ là dưới cuối code có ghi dòng Option Base 1 ---> Điều này là không được phép
Xem thông báo lỗi cũng thấy nó nói rằng "Chỉ có comment mới được ghi ở dưới các dòng End Sub, End Function.... "
Vậy, xóa đoạn Option Base 1 ở cuối code là được rồi
Ngoài ra:
- Cũng không cần Option Base 1 ở đầu code luôn, vì mảng được tạo thành từ Range luôn có Base =1, bất chấp Option Base đầu code là bao nhiêu
- Option Base này chỉ có tác dụng với mảng do bạn tự tạo ra (ví dụ biến Arr trong code của bạn)... nhưng bạn đã ReDim Arr(1 to... ) nên cũng đã khai báo nó là Base 1 rồi còn gì

To: Anh NDU
Anh kiểm tra lại file của em xem
Bill EXP0003 của sheet2 có 2 loại tiên (10 là VND; 10 là USD) nhưng qua sheet tổng hợp nó tính thành 20. (bị sai vì không đúng loại tiền)
Làm thử lồng ghép với Split (học thêm split) nhưng không được)

To Anh Hùng
Em có 02 sheet IMP & EXP tương ứng sheet1 & 2
Nhưng bill (tại cột 8) có kỳ thỏa mãn kỳ tại sheet Tổng hợp ô B1 thì lọc qua sheet Tổng hợp ạh

Bill là những ký hiêu IMP, EXP ???

Bill là vận đơn đính kèm dưới invoice Anh ạh. Em ký hiệu IMP & EXP để phân biệt là sheet IMP & EXP cho tiện thôi

Em đang học mảng nên chưa biết cách lồng duyệt qua 2 sheet

Anh giúp nhé
 
Lần chỉnh sửa cuối:
Upvote 0
Dùng split dễ hiểu quá
Em dùng split làm được rồi nhưng với chỉ là sheet1
Sheet 2 có cấu trúc tương tự sheet 1 thì code sau mình phải sửa như nào để nó duyệt qua sheet1 và sheet 2 ??


Em gửi file nhé

PHP:
Option Base 1
Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Address = "$B$1" Then
        Dim dg As Long
        Dim d As Object, Arr(), sArr, lRow As Long, lR As Long, item
        Set d = CreateObject("Scripting.Dictionary")
        dg = Sheet1.[A99000].End(xlUp).Row
        Sheet5.Range("A5:T65000").ClearContents
        With Sheet1
        sArr = .Range("A5:AA" & dg).Value
        ReDim Arr(1 To UBound(sArr, 1), 1 To 4)
        
           
            For lRow = 1 To UBound(sArr, 1)
            If sArr(lRow, 27) = Target.Value Then
                
                item = sArr(lRow, 8) & " " & sArr(lRow, 13)
                If Not d.Exists(item) Then
                lR = lR + 1
                d.Add item, lR
                KQ = Split(item, " ", 2)
                Arr(lR, 1) = KQ(0)
                Arr(lR, 3) = KQ(1)
                Arr(lR, 4) = sArr(lRow, 12)
                Else
                Arr(d.item(item), 4) = Arr(d.item(item), 4) + sArr(lRow, 12)
                End If
            End If
                MsgBox lRow
            Next lRow
    Sheet5.Range("A5").Resize(lR, 4).Value = Arr
        End With
  End If
 
End Sub
 

File đính kèm

  • Hoc_Array (split) .xls
    84 KB · Đọc: 92
Lần chỉnh sửa cuối:
Upvote 0
Dùng split dễ hiểu quá
Em dùng split làm được rồi nhưng với chỉ là sheet1
Sheet 2 có cấu trúc tương tự sheet 1 thì code sau mình phải sửa như nào để nó duyệt qua sheet1 và sheet 2 ??


Em gửi file nhé

PHP:
Option Base 1
Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Address = "$B$1" Then
        Dim dg As Long
        Dim d As Object, Arr(), sArr, lRow As Long, lR As Long, item
        Set d = CreateObject("Scripting.Dictionary")
        dg = Sheet1.[A99000].End(xlUp).Row
        Sheet5.Range("A5:T65000").ClearContents
        With Sheet1
        sArr = .Range("A5:AA" & dg).Value
        ReDim Arr(1 To UBound(sArr, 1), 1 To 4)
        
           
            For lRow = 1 To UBound(sArr, 1)
            If sArr(lRow, 27) = Target.Value Then
                
                item = sArr(lRow, 8) & " " & sArr(lRow, 13)
                If Not d.Exists(item) Then
                lR = lR + 1
                d.Add item, lR
                KQ = Split(item, " ", 2)
                Arr(lR, 1) = KQ(0)
                Arr(lR, 3) = KQ(1)
                Arr(lR, 4) = sArr(lRow, 12)
                Else
                Arr(d.item(item), 4) = Arr(d.item(item), 4) + sArr(lRow, 12)
                End If
            End If
                MsgBox lRow
            Next lRow
    Sheet5.Range("A5").Resize(lR, 4).Value = Arr
        End With
  End If
 
End Sub
Nếu cấu trúc các sheet hoàn toàn giống nhau, bạn khai báo cho biến sArr thành biến động.
-Cho biến Sh chạy qua các sheet, nếu tên của sheet không phải là "TONGHOP" thì khai báo biến sArr là vùng Sh.Range("A5:AA" & dg).Value lấy tổng số dòng của các mảng trong các sheet để khai báo lại số dòng của mảng kết quả
- Cho biến Sh chạy qua các sheet, nếu tên của sheet không phải là "TONGHOP" thì khai báo biến sArr là vùng Sh.Range("A5:AA" & dg).Value, tiếp tục chạy code,các phần khác không thay đổi
Thân
 
Upvote 0
nếu em không muốn khai báo là sh.name <> "Tonghop" mà muốn
nó duyệt từ sheet1, sheet2, sheet3 thì sao
Em có thể dùng
PHP:
  For sh = 1 To 3   
With Sheet(sh)  
 dg = Sheet(I).[A99000].End(xlUp).Row   

sArr = .Range("A5:AA" & dg).Value
...........
  next sh
như vậy được không?
 
Lần chỉnh sửa cuối:
Upvote 1
nếu em không muốn khai báo là sh.name <> "Tonghop" mà muốn
nó duyệt từ sheet1, sheet2, sheet3 thì sao
Em có thể dùng
Lỡ Sheet Tổng hợp là sheets(3) làm sao bạn nó chung bạn làm sao cũng được trừ sheet bạn tổng hợp ra là được rồi, đâu bạn làm theo gợi ý của Chú Concogia coi nếu không được thì gửi file lên
 
Upvote 0
Web KT
Back
Top Bottom