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

  • Thread starter Thread starter viehoai
  • Ngày gửi Ngày gửi
Liên hệ QC

viehoai

Thành viên gắn bó
Tham gia
22/5/09
Bài viết
2,599
Được thích
2,908
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ị
 
Đã nói vấn đề này đã được bàn qua rồi mà.
Cách làm căn bản là đổi dòng thành cột, cột thành dòng

1. Nếu mảng nhỏ: dùng hàm transpose và/hoặc index
Vì dùng hàm Worksheet cho nên chỉ dùng được tới vài ngàn dòng.

2. Nếu mảng lớn: tự viết một hàm chuyển.
 
Upvote 0
Bỏ dòng đó đi là được.
Hi cảm ơn snow25 đã hỗ trợ.
Mục đích OT chỉ là tìm hiểu thêm về "ReDim Preserve" làm sao để thay đổi kích thước mảng vừa đủ ứng với số phần tử thỏa mãn cần xuất ra ạ. :)

Đã nói vấn đề này đã được bàn qua rồi mà.
Cách làm căn bản là đổi dòng thành cột, cột thành dòng

1. Nếu mảng nhỏ: dùng hàm transpose và/hoặc index
Vì dùng hàm Worksheet cho nên chỉ dùng được tới vài ngàn dòng.

2. Nếu mảng lớn: tự viết một hàm chuyển.
Dạ, Bác biết link nào bàn chi tiết cho những người "ngu lâu, chậm hiểu" như con có khả năng tiếp thu được chút ít thì Bác chỉ cho con với ạ :D
 
Upvote 0
Hi cảm ơn snow25 đã hỗ trợ.
Mục đích OT chỉ là tìm hiểu thêm về "ReDim Preserve" làm sao để thay đổi kích thước mảng vừa đủ ứng với số phần tử thỏa mãn cần xuất ra ạ. :)


Dạ, Bác biết link nào bàn chi tiết cho những người "ngu lâu, chậm hiểu" như con có khả năng tiếp thu được chút ít thì Bác chỉ cho con với ạ :D
Bạn thử cái này.
Mã:
Sub TongHop2()
    Dim Data(), arr(), i As Long, TenKh As String, j As Long, k As Long
    Sheet2.Range("H2").Resize(1000, 7).ClearContents
    Data = Sheet2.Range("A2:F15").Value
        For i = 1 To UBound(Data)
            If Data(i, 1) = "Tên KH" Then TenKh = Data(i, 2)
            If IsDate(Data(i, 1)) Then
                k = k + 1
                ReDim Preserve arr(1 To 7, 1 To k)
                arr(1, k) = TenKh
                For j = 1 To 6
                    arr(j + 1, k) = Data(i, j)
                Next j
            End If
        Next i
        arr = chuyenmang(arr)
    If k Then
        Sheet2.Range("H2").Resize(k, 7).Value = arr
    End If
End Sub
Function chuyenmang(mang As Variant)
        Dim arr, i As Long, j As Long
        ReDim arr(1 To UBound(mang, 2), 1 To UBound(mang, 1))
             For i = 1 To UBound(mang, 1)
                 For j = 1 To UBound(mang, 2)
                     arr(j, i) = mang(i, j)
                 Next j
             Next i
             chuyenmang = arr
End Function
 
Upvote 0
Hi cảm ơn snow25 đã hỗ trợ.
Mục đích OT chỉ là tìm hiểu thêm về "ReDim Preserve" làm sao để thay đổi kích thước mảng vừa đủ ứng với số phần tử thỏa mãn cần xuất ra ạ. :)


Dạ, Bác biết link nào bàn chi tiết cho những người "ngu lâu, chậm hiểu" như con có khả năng tiếp thu được chút ít thì Bác chỉ cho con với ạ :D
Mỗi lần Redim mảng sẽ làm chậm tốc độ thực thi, đưa vào vòng lặp để Redim Preserve lại càng thêm chậm.
Như vậy có cần thiết Redim Preserve hay không? Nếu cần thiết thì cứ tính toán hết đi rồi Redim Preserve 1 lần.
 
Upvote 0
Bạn thử cái này.
Mã:
Sub TongHop2()
    Dim Data(), arr(), i As Long, TenKh As String, j As Long, k As Long
    Sheet2.Range("H2").Resize(1000, 7).ClearContents
    Data = Sheet2.Range("A2:F15").Value
        For i = 1 To UBound(Data)
            If Data(i, 1) = "Tên KH" Then TenKh = Data(i, 2)
            If IsDate(Data(i, 1)) Then
                k = k + 1
                ReDim Preserve arr(1 To 7, 1 To k)
                arr(1, k) = TenKh
                For j = 1 To 6
                    arr(j + 1, k) = Data(i, j)
                Next j
            End If
        Next i
        arr = chuyenmang(arr)
    If k Then
        Sheet2.Range("H2").Resize(k, 7).Value = arr
    End If
End Sub
Function chuyenmang(mang As Variant)
        Dim arr, i As Long, j As Long
        ReDim arr(1 To UBound(mang, 2), 1 To UBound(mang, 1))
             For i = 1 To UBound(mang, 1)
                 For j = 1 To UBound(mang, 2)
                     arr(j, i) = mang(i, j)
                 Next j
             Next i
             chuyenmang = arr
End Function
Hihi, cảm ơn snow25 đã cho một cách tham khảo.

Mỗi lần Redim mảng sẽ làm chậm tốc độ thực thi, đưa vào vòng lặp để Redim Preserve lại càng thêm chậm.
Như vậy có cần thiết Redim Preserve hay không? Nếu cần thiết thì cứ tính toán hết đi rồi Redim Preserve 1 lần.
Dạ vâng Anh, OT cũng có nghĩ tốc độ có thể sẽ chậm hơn thật.. chỉ là tham khảo thêm về cách sử dụng Redim Preserve thôi ạ.
Nhưng xem chừng OT vẫn còn ngu ngơ lắm ạ :D
 
Upvote 0
Cho em hỏi về mảng liên quan đến Dic. Em có đọc thấy Item có thể nhận kiểu dữ liệu là mảng. Vậy làm thế nào để add mảng làm item và khi muốn lấy giá trị một phần tử bất kì của mảng (item) đó thì em phải làm thế nào?
Ví dụ đây là đoạn code của em vậy add mảng tại ??? như thế nào mọi người chỉ giúp em được không?
PHP:
If Ws.Tab.Color = 10498160 Then
     BS = Ws.Range("AJ9", Ws.Range("AJ10000").End(xlUp)).Resize(, 3).Value
     For i = 1 To UBound(BS, 1)
          Tem = BS(i, 1)
          If Not Dic.exists(Tem) And Len(Tem) > 0 Then Dic.Add Tem, ???
     Next i
End If
 
Lần chỉnh sửa cuối:
Upvote 0
Cho em hỏi về mảng liên quan đến Dic. Em có đọc thấy Item có thể nhận kiểu dữ liệu là mảng. Vậy làm thế nào để add mảng làm item và khi muốn lấy giá trị một phần tử bất kì của mảng (item) đó thì em phải làm thế nào?
Ví dụ đây là đoạn code của em vậy add mảng tại ??? như thế nào mọi người chỉ giúp em được không?
PHP:
If Ws.Tab.Color = 10498160 Then
     BS = Ws.Range("AJ9", Ws.Range("AJ10000").End(xlUp)).Resize(, 3).Value
     For i = 1 To UBound(BS, 1)
          Tem = BS(i, 1)
          If Not Dic.exists(Tem) And Len(Tem) > 0 Then Dic.Add Tem, ???
     Next i
End If
Bạn gán mảng vào item như bình thường thôi.Còn lấy ra tùy theo bạn muốn lấy cả mảng ra hay từng vị trí của mảng cũng được.
Mã:
If Ws.Tab.Color = 10498160 Then
     BS = Ws.Range("AJ9", Ws.Range("AJ10000").End(xlUp)).Resize(, 3).Value
     For i = 1 To UBound(BS, 1)
          tem = BS(i, 1)
          If Not dic.exists(tem) And Len(tem) > 0 Then
             dic.Add tem, Array(1, 2, 3)
             MsgBox dic.Item(tem)(0)
             MsgBox dic.Item(tem)(1)
             MsgBox dic.Item(tem)(2)
         end if
     Next i
End If
 
Upvote 0
Bạn gán mảng vào item như bình thường thôi.Còn lấy ra tùy theo bạn muốn lấy cả mảng ra hay từng vị trí của mảng cũng được.
Mã:
If Ws.Tab.Color = 10498160 Then
     BS = Ws.Range("AJ9", Ws.Range("AJ10000").End(xlUp)).Resize(, 3).Value
     For i = 1 To UBound(BS, 1)
          tem = BS(i, 1)
          If Not dic.exists(tem) And Len(tem) > 0 Then
             dic.Add tem, Array(1, 2, 3)
             MsgBox dic.Item(tem)(0)
             MsgBox dic.Item(tem)(1)
             MsgBox dic.Item(tem)(2)
         end if
     Next i
End If
Em muốn gán mảng BS tại vị trí i thì em cần làm như thế nào hả anh? Hay em cần viết thêm là
For j =1 to 3
Arr(i,j)= BS(i,j)
Phải không anh?
 
Upvote 0
Mỗi lần Redim mảng sẽ làm chậm tốc độ thực thi, đưa vào vòng lặp để Redim Preserve lại càng thêm chậm.
Như vậy có cần thiết Redim Preserve hay không? Nếu cần thiết thì cứ tính toán hết đi rồi Redim Preserve 1 lần.
Việc này có lẽ chưa hoàn toàn đúng. Nếu sau mỗi lần redim preserve mà kích thước mảng giảm, khối lượng tính sẽ giảm, có lẽ code sẽ nhanh hơn ban đầu.
Còn nếu preserve mà kích thước lại tăng thì bó tay.
 
Upvote 0
Việc này có lẽ chưa hoàn toàn đúng. Nếu sau mỗi lần redim preserve mà kích thước mảng giảm, khối lượng tính sẽ giảm, có lẽ code sẽ nhanh hơn ban đầu.
Còn nếu preserve mà kích thước lại tăng thì bó tay.
.
Nói như vậy là rất mơ hồ.

Về Redim Preserve tôi đã từng nói về cách hoạt động của nó rồi. Chịu khó tìm thớt ấy.
Đại khái:
1. Mảng là cấu trúc cổ đại của lập trình. Nó gần như luôn luôn được thể hiện bằng một vùng nhớ liên tục. Mục đích là để giúp tốc độ. Vì vậy tốc độ sẽ liên quan đến chiều hướng truy cập. Và VBA dùng mảng truy theo cột. (Có một bạn đã từng chứng minh duyệt mảng theo cột nhanh hơn theo dòng)
2. Redim Preserve chủ yếu chỉ là tạo một mảng khác rồi copy mảng cũ sang. Khi VBA thực hiện lệnh này thì tôi nghĩ rằng bên trong nó dùng mấy cái macro (macro này viết bằng ASM) để copy toàn cụm vùng nhớ, không ai dại gì copy từng đoạn hay từng phần tử.
3. Mấy cái macro (ASM) dùng để Dim/Redim mảng cũng được viết rất hiệu quả. Cho nên tốc độ tuy có bị ảnh hưởng nhưng không nhiều như ta tưởng tượng.
4. Túm lại thì xào mảng đi lại cũng nên quan tâm ở điểm làm nát (fragment) bộ nhớ. Nhưng với các hệ điều hành mới (Win7 trở đi) thì VBA sử dụng code gom bộ nhớ (dọn rác/garbage collection) rất hiệu quả.
 
Lần chỉnh sửa cuối:
Upvote 0
Em muốn gán mảng BS tại vị trí i thì em cần làm như thế nào hả anh? Hay em cần viết thêm là
For j =1 to 3
Arr(i,j)= BS(i,j)
Phải không anh?
Bạn gán mảng vào item như bình thường thôi.Còn lấy ra tùy theo bạn muốn lấy cả mảng ra hay từng vị trí của mảng cũng được.
Mã:
If Ws.Tab.Color = 10498160 Then
     BS = Ws.Range("AJ9", Ws.Range("AJ10000").End(xlUp)).Resize(, 3).Value
     For i = 1 To UBound(BS, 1)
          tem = BS(i, 1)
          If Not dic.exists(tem) And Len(tem) > 0 Then
             dic.Add tem, Array(1, 2, 3)
             MsgBox dic.Item(tem)(0)
             MsgBox dic.Item(tem)(1)
             MsgBox dic.Item(tem)(2)
         end if
     Next i
End If
Em làm được rồi nhé. Cảm ơn anh!
PHP:
Private Sub BoSungCong()
Dim BS(), i As Long, Dic As Object, Tem As String
Dim Ws As Worksheet
Set Dic = CreateObject("Scripting.Dictionary")
    For Each Ws In Worksheets
        If Ws.Tab.Color = 10498160 Then
            BS = Ws.Range("AJ9", Ws.Range("AJ10000").End(xlUp)).Resize(, 3).Value
            For i = 1 To UBound(BS, 1)
                Tem = Cells(i + 8, 3)
                If Not IsEmpty(Tem) And Not Dic.exists(Tem) Then Dic.Add Tem, Array(BS(i, 1), BS(i, 2), BS(i, 3))
            Next i
        End If
    Next Ws
End Sub
 
Upvote 0
Nói như vậy là rất mơ hồ.
...
Có lẽ nếu khối lượng tính toán giảm đáng kể tài nguyên máy so với redim preserve thì việc này cũng nên thử.
Thực tế sử dụng, nếu redim preserve cho mảng 1 chiều trong vòng lặp thấy có vẻ tốt bác ạ. Cái này thấy giống như lệnh Remove trong dictionary khi dùng trong vòng lặp.
Em chỉ thực tế thôi bác ạ.
 
Upvote 0
Tôi thường dùng từ rất đúng ngữ cảnh và ngữ pháp. Nếu tôi cho rằng "sai" thì tôi đã dùng từ "sai".
Tôi dùng từ "rất mơ hồ" bởi vì tôi cho rằng nói như vậy là "mơ hồ":
Chỉ nói suông kích thước tăng/giảm như vậy chưa đủ. Ít nhất phải cho biết tại sao thêm 1 dòng/cột lại tốn tài nguyên hơn bớt 1 dòng/cột.
Ví dụ: (giải thích rằng) thêm cột thì VBA phải tạo vùng nhớ mới; bớt cột thì vẫn giữ vùng nhớ ấy, chỉ thu ngắn lại thôi.
Lưu ý: ví dụ cụ thể thôi, thực tế tôi không tin VBA làm vậy. Tôi biết các macro tạo vùng nhớ và copy vùng nhớ của hệ thống làm việc hiệu quả lắm. Điển hình là mỗi lần bạn dùng toán tử & để gộp chuỗi là VBA phải tạo vùng nhớ mới và copy qua. (*)

(*) Đối với chuỗi, tôi vẫn khuyên là nếu tránh dùng & thì lại hiệu quả hơn. Nhưng nếu không tránh được thì cũng không đến nổi tốn kém quá.
 
Upvote 0
Tôi thường dùng từ rất đúng ngữ cảnh và ngữ pháp. Nếu tôi cho rằng "sai" thì tôi đã dùng từ "sai".
Tôi dùng từ "rất mơ hồ" bởi vì tôi cho rằng nói như vậy là "mơ hồ":
Chỉ nói suông kích thước tăng/giảm như vậy chưa đủ. Ít nhất phải cho biết tại sao thêm 1 dòng/cột lại tốn tài nguyên hơn bớt 1 dòng/cột.
Ví dụ: (giải thích rằng) thêm cột thì VBA phải tạo vùng nhớ mới; bớt cột thì vẫn giữ vùng nhớ ấy, chỉ thu ngắn lại thôi.
Lưu ý: ví dụ cụ thể thôi, thực tế tôi không tin VBA làm vậy. Tôi biết các macro tạo vùng nhớ và copy vùng nhớ của hệ thống làm việc hiệu quả lắm. Điển hình là mỗi lần bạn dùng toán tử & để gộp chuỗi là VBA phải tạo vùng nhớ mới và copy qua. (*)

(*) Đối với chuỗi, tôi vẫn khuyên là nếu tránh dùng & thì lại hiệu quả hơn. Nhưng nếu không tránh được thì cũng không đến nổi tốn kém quá.
Nếu chỉ xét riêng việc redim preserve trong vòng lặp thì chắc là code sẽ chậm hơn khi không redim, nhưng thực tế, viêc quét qua mảng thường kèm theo các phép tính liên quan nên việc redim mảng trong một số trường hợp có thể làm giảm khối lượng tính đi kèm. Trong trường hợp này, nếu khối lượng công việc đi kèm là đáng kể thì có thể code sẽ nhanh hơn nếu không redim & ngược lại, tính toán kèm theo là không lớn thì code cũng sẽ vẫn chậm hơn khi không redim.
Việc đánh giá là mơ hồ chỗ này thì cũng hoàn toàn đồng ý với bác .
 
Upvote 0
Upvote 0
Chào mọi người trong group

hiện tại mình mới bắt đầu học VBA excel (mình bên logictics) muốn có vấn đề muốn hỏi mong các pro chỉ giúp :
1. autofillter : mình có 1 bang data , mình muốn tự lọc theo dự liệu paste 1 cột
+> phần điều kiện Criteria : cột dữ lieu muốn filter thành 1 chuỗi arr(....) =Criteria?
+> có cần xác định dòng cuối dự lieu cần lọc không? có thì xin chỉ giáo giúp
2> khi lọc được những dữ lieu mình muốn lọc :
+> Tìm được date >2/3 ( date = ((HSD -now())/HSD - NXS) và số lượng của những dữ lieu (mã hang) mà mình muốn lọc
 
Upvote 0
Vấn đề của em khá nhỏ nên em không lập topic mới ạ (nếu em đăng sai, nhờ BQT nhắc nhở để em đăng bài mới)

Em có biến LastRow để xác định dòng cuối
Em cần chọn nhiều vùng không liền nhau kết hợp LastRow để xác định dữ liệu mảng động để format trước khi thêm dữ liệu vào sheetform nhưng em chưa viết được code (như hình em cần chọn ô bắt đầu là B2:C & LastRow, E2 & LastRow, G2 & LastRow, I2:K & LastRow,....)

Mong anh/chị GPE giúp em ạ.
Em cám ơn nhiều!
 

File đính kèm

  • 1560182911693.png
    1560182911693.png
    25.6 KB · Đọc: 6
Upvote 0
Web KT

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

Back
Top Bottom