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,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ị
 
Các anh cho e hỏi. E có 1 mãng từ A1:A10. Em muốn tìm giá trị của cell B1 trong mãng nói trên nếu có thì xuất giá trị đó vào cell C1. Em phải code như thế nào vậy các anh
Nghĩ cũng lạ, nếu trong dãy A1:A10 có chứa giá trị của B1 thì C1 được gán giá trị đó, vậy sao không dùng công thức tại C1: =IF(COUNTIF(A1:A10,B1),B1,"") luôn cho rồi cần gì phải tính trong mảng chứ?
 
Lần chỉnh sửa cuối:
Upvote 0
Nhờ các Thầy hướng dẫn (hoặc cho link) về gộp 2 mảng 2 chiều thành 1 mảng 2 chiều khác.
Cụ thể sArr1(1 to n,1 to 2); sArr2(1 to m, 1 to 2); mảng mới sArr(1 to n+m, 1 to 2).
Gộp 2 mảng 1 chiều thì tạm thời em dùng Split sau đó Join lại. Gộp 2 mảng 2 chiều thì tìm chưa thấy.
Chân thành cảm ơn!
 
Upvote 0
Nhờ các Thầy hướng dẫn (hoặc cho link) về gộp 2 mảng 2 chiều thành 1 mảng 2 chiều khác.
Cụ thể sArr1(1 to n,1 to 2); sArr2(1 to m, 1 to 2); mảng mới sArr(1 to n+m, 1 to 2).
Gộp 2 mảng 1 chiều thì tạm thời em dùng Split sau đó Join lại. Gộp 2 mảng 2 chiều thì tìm chưa thấy.
Chân thành cảm ơn!

bạn đọc bài #469 trong topic này của Thầy Ndu là có ngay đáp án !
 
Upvote 0
Em có 2 câu hỏi?
Câu 1:
Ví dụ với i = 20 thì
Range("A1:B" & i).select sẽ có giá trị mảng là bao nhiêu vậy ah.

Để biết mảng đó trên cửa số Immediate, thì mình dùng lệnh gì vậy?

Câu 2: Khai báo Dim endR& dấu & đây mang nghĩa là gì ah
 
Upvote 0
Em có 2 câu hỏi?
Câu 1:
Ví dụ với i = 20 thì
Range("A1:B" & i).select sẽ có giá trị mảng là bao nhiêu vậy ah.

Để biết mảng đó trên cửa số Immediate, thì mình dùng lệnh gì vậy?

Câu 2: Khai báo Dim endR& dấu & đây mang nghĩa là gì ah
Tôi trả lời câu hỏi của bạn

1/ Câu 1:
Mã:
Ví dụ với i = 20 thì
Range("A1:B" & i).select sẽ có giá trị mảng là bao nhiêu vậy ah.

=> Bạn thế i vào chỗ range sẽ được: Range("A1:B20").Select => Vùng A1:B20 được bôi đen (có .select). Đây đơn thuần chỉ là lựa chọn một vùng chứ chẳng liên quan gì tới mảng cả

2/ Câu 2:
Mã:
[COLOR=#000000]Khai báo Dim endR[/COLOR][B]& dấu & đây mang nghĩa là gì ah[/B]
Bạn tham khảo topic sau sẽ rõ, song đây là cách khai báo biến không tường minh nên tôi nghĩ là thói quen không tốt.

http://www.giaiphapexcel.com/forum/showthread.php?82192-Khai-báo-biến-trong-VBA
 
Upvote 0
Em có 2 câu hỏi?
Câu 1:
Ví dụ với i = 20 thì
Range("A1:B" & i).select sẽ có giá trị mảng là bao nhiêu vậy ah.

Để biết mảng đó trên cửa số Immediate, thì mình dùng lệnh gì vậy?

Câu 2: Khai báo Dim endR& dấu & đây mang nghĩa là gì ah
Câu 1: Phương thức Select là chọn vùng, không có mảng nào được tạo ra ở đây cả. Mà tôi không hiểu giá trị mảng bạn đề cập là cái gì, là số phần tử, là giá trị của các phần tử hay là độ lớn của chiều 1, chiều 2...
Câu 2: & là khai báo biến dạng Long: endR& ~ endR As Long
 
Upvote 0
Câu 1: Phương thức Select là chọn vùng, không có mảng nào được tạo ra ở đây cả. Mà tôi không hiểu giá trị mảng bạn đề cập là cái gì, là số phần tử, là giá trị của các phần tử hay là độ lớn của chiều 1, chiều 2...
Câu 2: & là khai báo biến dạng Long: endR& ~ endR As Long
Đoạn cụ thể ntn chú ah
endR = .Cells(400, 3).End(xlUp).Row
Arr = .Range("A2:C" & endR).Value

Với cháu hỏi thêm xúi. câu lênh để đưa kết quả địa chỉ ARR lên msgbox hoặc công cụ intemediate
 
Lần chỉnh sửa cuối:
Upvote 0
Đoạn cụ thể ntn chú ah
endR = .Cells(400, 3).End(xlUp).Row
Arr = .Range("A2:C" & endR).Value

Với cháu hỏi thêm xúi. câu lênh để đưa kết quả địa chỉ ARR lên msgbox hoặc công cụ intemediate
Mảng Arr thì không có địa chỉ, chỉ có phần tử trong mảng thôi.
Có thể truy vấn thế này
MsgBox Arr(1,1) sẽ trả về phần tử đầu tiên. Dùng vòng lặp để duyệt hết các phần tử trong mảng
 
Upvote 0
Nhờ các Thầy hướng dẫn (hoặc cho link) về gộp 2 mảng 2 chiều thành 1 mảng 2 chiều khác.
Cụ thể sArr1(1 to n,1 to 2); sArr2(1 to m, 1 to 2); mảng mới sArr(1 to n+m, 1 to 2).
Gộp 2 mảng 1 chiều thì tạm thời em dùng Split sau đó Join lại. Gộp 2 mảng 2 chiều thì tìm chưa thấy.
Chân thành cảm ơn!
Nếu chưa có cách nào hay thì dùng tạm cách của anh xem sao. Kiểu gì cũng mần được nhưng hơi kỳ kỳ chút
PHP:
Sub test()
Dim arr1(), Arr2(), Arr3(), i, j
arr1 = [A1:B3].Value
Arr2 = [E1:F3].Value
ReDim Arr3(1 To UBound(arr1) + UBound(Arr2), 1 To 2)
For i = 1 To UBound(arr1)
   Arr3(i, 1) = arr1(i, 1)
   Arr3(i, 2) = arr1(i, 2)
Next
For j = 1 To UBound(Arr2)
   Arr3(j +UBound(arr1), 1) = Arr2(j, 1)
   Arr3(j +UBound(arr1), 2) = Arr2(j, 2)
Next
[H1].Resize(UBound(Arr3), 2) = Arr3
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Nối 2 mảng:
Nếu đã có chỗ chép mảng ra thì tội gì phải làm phép nối.
- Chép mảng 1 vào vị trí đầu.
- Chép mảng 2 vào vị trí với Offset số dòng = số dòng (Ubound) mảng 1
- Đọc vùng vừa chép vào mảng 3; số dòng = Ubound(mảng 1) + Ubound(mảng 2)

Nối 2 mảng, không có chỗ chép xuống:

Mã:
' code này đặt trên giả thuyết là mảng lấy từ range; LBound là 1
' nếu LBound là 0 thì phải chỉnh sửa lại cách đếm.
' Tuy nhiên nguyên tắc giải thuật thì vẫn vậy
d1 = UBound(mang1,1) ' số dòng mảng 1
d2 = UBound(mang2,1) ' số dòng mảng 2
c1 = UBound(mang1,2) ' số cột mảng 1
c2 = UBound(mang2,2) ' số cột mảng 2
' định độ lớn mảng 3, số dòng là tổng số dòng, số cột là số lớn hơn giữa 2 số cột
Redim mang3(1 to d1 + d2, 1 to IIf(c1 > c2, c1, c2))
For d0 = 1 To d1 ' chép mảng 1
  For c0 = 1 To c1
    mang3(d0,c0) = mang1(d0,c0)
  Next c0
Next d0
For d0 = 1 To d2 ' chép mảng 2
  For c0 = 1 To c2
    mang3(d0+d1,c0) = mang2(d0,c0)
  Next c0
Next d0
 
Lần chỉnh sửa cuối:
Upvote 0
Hình như có thể chép thẳng mảng 1 vào mảng 3 (1 lênh mang3 = mang1). Kế đó Redim Preserve thnahf tổng số dòng và số cột lớn. Sau cùng là chép từng phần tử mảng 2 vào.
Cách này chưa thử nên chưa biết chắc.

Có cách chép nhanh hơn nhưng phải dùng hàm Memcpy của Library. Hàm này chép thẳng một khối dữ liệu trong bộ nhớ từ vùng này sang vùng kia. Hình như hàm này viết bằng C hay gì đó Assembly nên rất hiệu quả.
 
Upvote 0
Hình như có thể chép thẳng mảng 1 vào mảng 3 (1 lênh mang3 = mang1). Kế đó Redim Preserve thnahf tổng số dòng và số cột lớn. Sau cùng là chép từng phần tử mảng 2 vào.
Cách này chưa thử nên chưa biết chắc.

Có cách chép nhanh hơn nhưng phải dùng hàm Memcpy của Library. Hàm này chép thẳng một khối dữ liệu trong bộ nhớ từ vùng này sang vùng kia. Hình như hàm này viết bằng C hay gì đó Assembly nên rất hiệu quả.
Cái phần màu đỏ là chưa có chuẩn, vì Redim Preserve chỉ có tác dụng đối với cột còn dòng là không được anh VetMini nhỉ?
 
Upvote 0
Ờ xem kỹ lại thì có chuyện giới hạn đối với mảng nhiều hơn 1 chiều. Tiếc nhỉ.
 
Upvote 0
Cái phần màu đỏ là chưa có chuẩn, vì Redim Preserve chỉ có tác dụng đối với cột còn dòng là không được anh VetMini nhỉ?

Nói tổng quát hơn, ReDim Preserve chỉ cho phép thay đổi kích thước chiều cuối cùng của mảng. Nghĩa là mảng 1 chiều có thể thay đổi kích thước, mảng 2 chiều chỉ có thể thay đổi kích thước chiều thứ 2, mảng n chiều chỉ cho thay đổi kích thước chiều thứ n.
 
Upvote 0
Cái phần màu đỏ là chưa có chuẩn, vì Redim Preserve chỉ có tác dụng đối với cột còn dòng là không được anh VetMini nhỉ?

Nói tổng quát hơn, ReDim Preserve chỉ cho phép thay đổi kích thước chiều cuối cùng của mảng. Nghĩa là mảng 1 chiều có thể thay đổi kích thước, mảng 2 chiều chỉ có thể thay đổi kích thước chiều thứ 2, mảng n chiều chỉ cho thay đổi kích thước chiều thứ n.

Tôi nói đến vấn đề "hình như..." là vì nó là chuyện quá khứ đâu đó (*) mà tôi quên mất. Đến chừng xem kỹ lại thì quả là không được.
VBA dùng kỹ thuật thiết lập mảng "trọng cột" (column major) cho nên khi code ReDim chép lại vào mảng mới thì nó chỉ có thể nới rộng hoặc cắt đi phần sau; tức là phần cột (**).

(*) Chuyện quá khứ đó xảy ra ở môi trường khác. Tức là ở môi trường mà mảng được thiết lập theo kiểu "trọng cột" (row major), và code thể dùng con trỏ để copy mảng theo từng khối.

(**) Tôi không rõ đi sâu hơn về kỹ thuật dòng-cột có làm các bạn buòn ngủ hay không nên dừng ở đây. Nếu bạn nào muón tìm hiểu thêm thì cứ việc hỏi. Tôi tim chắc rằng sẽ có bạn trả lời rõ hơn.
 
Upvote 0
(*) Chuyện quá khứ đó xảy ra ở môi trường khác. Tức là ở môi trường mà mảng được thiết lập theo kiểu "trọng cột" (row major), và code thể dùng con trỏ để copy mảng theo từng khối.

(**) Tôi không rõ đi sâu hơn về kỹ thuật dòng-cột có làm các bạn buòn ngủ hay không nên dừng ở đây. Nếu bạn nào muón tìm hiểu thêm thì cứ việc hỏi. Tôi tim chắc rằng sẽ có bạn trả lời rõ hơn.

Bạn gõ nhầm. Chỗ đỏ đỏ là "trọng dòng", do trong ngoặc là row major.

Trong Delphi thì mảng được ghi trong bộ nhớ theo từng DÒNG - HÀNG (tức "trọng dòng" như bạn nói). Tức các ô của dòng là liên tiếp nhưng các cột không hẳn là các vùng liên tiếp. Có thể dùng CopyMemory để copy từng hàng.

Trong VBA cũng chắc chắn là mảng được ghi trong bộ nhớ theo từng CỘT (tức "trọng cột" như bạn nói). Tức các ô của cột là liên tiếp nhưng theo tôi (tức không dám chắc như tôi dám chắc trong Delphi) thì các cột cũng không hẳn là các vùng liên tiếp. Nhưng cũng có thể dùng CopyMemory để copy cả cột.
 
Upvote 0
Tôi nói đến vấn đề "hình như..." là vì nó là chuyện quá khứ đâu đó (*) mà tôi quên mất. Đến chừng xem kỹ lại thì quả là không được.
VBA dùng kỹ thuật thiết lập mảng "trọng cột" (column major) cho nên khi code ReDim chép lại vào mảng mới thì nó chỉ có thể nới rộng hoặc cắt đi phần sau; tức là phần cột (**).
Tôi không rành về ngôn ngữ khác, cả VBA cũng chưa nghiên cứu sâu đến mức row major hay column major, vì tôi chỉ nghiên cứu để thực hành là chính.
Trong thực hành tôi bị báo lỗi khi ReDim Preserve dòng trong mảng 2 chiều, tìm hiểu help thì biết chỉ có thể thay đổi kích thước chiều sau cùng:
If you use the Preserve keyword, you can resize only the last array dimension and you can't change the number of dimensions at all. For example, if your array has only one dimension, you can resize that dimension because it is the last and only dimension. However, if your array has two or more dimensions, you can change the size of only the last dimension and still preserve the contents of the array.

Nhân tiện cũng lạm bàn:
Tôi không biết trọng cột hay trọng dòng là như thế nào, nhưng theo thiển ý thì khi dùng ReDim Preserve để thay đổi kích thước mảng 1 chiều thì không có khái niệm dòng cột, khi dùng để thay đổi kích thước mảng 2 chiều thì chỉ thay đổi kích thước chiều thứ 2 là cột. Nhưng khi thay đổi kích thước chiều thứ 3 của mảng 3 chiều, hay chiều thứ n của mảng n chiều thì lại chẳng phải dòng, cũng chẳng phải cột. Mà VBA hỗ trợ mảng tối đa lên 60 chiều:

Dimensions of an array variable; up to 60 multiple dimensions may be declared

Nên tôi e rằng không liên quan đến trọng dòng trọng cột, hoặc liên quan ở chỗ nào mà tôi không biết?
 
Lần chỉnh sửa cuối:
Upvote 0
Trong hầu hết các ngôn ngữ lập trình, mảng được chứa theo dạng một vùng nhớ liên tục (nếu là chuỗi thì mảng thực ra chứa địa chỉ của những chuỗi, và các địa chỉ này cũng được chứa trong vùng nhớ liên tục).

Tuy nhiên, địa chỉ bộ nhớ được truy cập theo 1 chiều cho nên mảng bắt buộc phải có cách chuyển từ n chiều thành 1 chiều.

Ví dụ, tôi có mảng 1 chiều a (1 to 3) thì trong vùng nhớ, các phần tử được sắp xếp như sau:

| a(1) | a(2) | a(3) |

giả sử mỗi phần tử mảng là một đơn vị địa chỉ (một ô trong bộ nhớ), và mảng bắt đầu từ địa chỉ 1001:

a(1) ở địa chỉ 1001; a(2) ở địa chỉ 1002; a(3) ở địa chỉ 1003

Tuy nhiên, nếu tôi có mảng 2 chiều: a (1 to 3, 1 to 2) [3 dòng, 2 cột] thì sao?

Cách xếp trọng cột, tức là xếp cột 1 trước cột 2::

| a(1,1)| a(2,1) | a(3,1) | a(1,2) | a(2,2) | a(3,2) |

a((1,1) ở 1001; a(2,1) ở 1002; a(3,1) ở 1003, a(1,2) ở 1004; a(2,2) ở 1005; a(3,2) ở 1006

Cách xếp trọng dòng, tức là xếp dòng 1 trước dòng 2 trước dòng 3:

| a(1,1) | a(1,2) | a(2,1) | a(2,2) | a(3,1) | a(3,2) |

a((1,1) ở 1001; a(1,2) ở 1002; …

Lệnh ReDim:

Lệnh này lập một vùng nhớ mới; trỏ mảng vào vùng nhớ này; và báo cho hệ thống biết là vùng nhớ cũ không còn xài nữa.

Lệnh ReDim Preserve:

Tương tự như vậy, nhưng thêm phần chép dữ liệu vùng nhớ cũ vào vùng nhớ mới.

Nếu trong mảng 1 chiều trên, a được đổi thành a(1 to 5) thì lệnh sẽ lấy một khoảng bộ nhớ khác với 5 ô (ví dụ ở 2001-2005). Ta có a mới:

| a(1) | a(2) | a(3) | a(4) | a(5) |

a(1) ở 2001; a(2) ở 2002; a(3) ở 2003; a(4) ở 2004; a(5) ở 2005

Để thực hiện copy dữ liệu, Redim Preserve sẽ dùng hàm tượng tự như MemCpy (hàm này cóp py từng khối chứ không phải từng ô) cóp py 1001-1003 vào 2001-2003.

Nếu trong mảng 2 chiều trên, a được đổi thành a(1 to 4, 1 to 2) [tăng lên 1 dòng] thì sao?

Cách xếp trọng cột:

| a(1,1)| a(2,1) | a(3,1) | a(4,1) | a(1,2) | a(2,2) | a(3,2) | a(4,2) |

Đến lúc cóp py dữ liệu Preserve không thể nào cóp py 1001-1006 vào 2001-2006 được. Vì chép 1001-1003 thì xuôi, nhưng đến 1004 thì vốn là a(1,2), nếu chép vào 2004 thì bị trật phần tử (2004 là địa chỉ mới của a(4,1), trong khi địa chỉ mới của a(1,2) phải là 2005).

Mặt khác, cũng với cách xếp trọng cột, a được đổi thành a(1 to 3, 1 to 3) [tăng lên 1 cột] thì sao?

| a(1,1)| a(2,1) | a(3,1) | a(1,2) | a(2,2) | a(3,2) | a(3,3) | a(2,3) | a(3,3) |

Ta thấy 1001-1006 có thể chép thẳng vào 2001-2006.

Vì vậy ngôn ngữ trọng cột bắt buộc không cho phép thay đổi số hàng. Mà chỉ được thay đổi số cột

@siwtom: cảm ơn bạn đã nhắc, tôi gõ nhầm thật: tôi gõ "trọng cột" (row major); trong khi "trọng dòng" (row major) mới đúng.
Hầu hết các ngôn ngữ sau này đều dùng "trọng dòng". Tôi nhớ mang máng Delphi vốn gần với Pascal, mà Pascal thì vốn được đặt ra để dạy Angol 60 cho nên nó thiết kế theo ý nghĩa đại số hơn. Theo tôi nghĩ thì BASIC vốn đi đôi với FORTRAN, là ngôn ngữ cổ cho nên nó dùng trọng cột (nghĩ vậy chứ chả có gì quyết đoán).
 
Lần chỉnh sửa cuối:
Upvote 0
....

Để thực hiện copy dữ liệu, Redim Preserve sẽ dùng hàm tượng tự như MemCpy (hàm này cóp py từng khối chứ không phải từng ô) cóp py 1001-1003 vào 2001-2003.

....

Tôi thấy bài viết của Nguyễn Duy Tuân rất tuyệt trong việc cắt, ghép, delete, insert trong mảng:

http://www.giaiphapexcel.com/forum/...rong-bộ-nhớ-Tốc-độ-nhanh!&p=479565#post479565

Các bạn theo link trên mà tham khảo nhé!
 
Upvote 0
Trong hầu hết các ngôn ngữ lập trình, mảng được chứa theo dạng một vùng nhớ liên tục (nếu là chuỗi thì mảng thực ra chứa địa chỉ của những chuỗi, và các địa chỉ này cũng được chứa trong vùng nhớ liên tục).

Tuy nhiên, địa chỉ bộ nhớ được truy cập theo 1 chiều cho nên mảng bắt buộc phải có cách chuyển từ n chiều thành 1 chiều.
Như vậy, với những ví dụ của VetMini thì tôi đã hiểu nếu ReDim Preserve chiều thứ nhất (dòng) thì không chép được nguyên khối dữ liệu của mảng sang vị trí mới của bộ nhớ.
Mảng 3 chiều trở lên cũng có thể suy ra tương tự.

Như vậy chuyện "trọng cột" chắc cũng chỉ là dùng từ ngữ 1 cách tương đối thôi, chứ chiều thứ 3 có thể gọi là chiều "sâu", chiều thứ 4 trở lên chăng biết gọi là gì, chẳng phải dòng, cột, nên "trọng cột" theo nghĩa đen không phù hợp nữa. Phải là "trọng sâu" hoặc nói chung là "trọng cuối", (he he, tèn tèn, ẹc ẹc, hic hic)
 
Upvote 0
Web KT

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

Back
Top Bottom