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ị
 
Đây là diễn đàn học hỏi. Bàn chuyện đúng sai thì nên nói rõ. Nói kiểu cộc lốc chả ai học được gì cả.

Tôi lập lại lời tôi đã nói ở bài trước: nếu nói theo quan niệm lập trình lý thuyết thì tạo thêm 1 dòng cho mảng để tránh lỗi cuối mảng là phương pháp gượng ép.

Với hầu hết các ngôn ngữ dạng "thấp" hơn, ngưởi ta có thể test cuối mảng và đọc dòng kế trong cùng một câu lô gic.
IF (cuối mảng HOẶC khác dòng kế tiếp)
Lô gic được đặt theo kiểu thông minh, nếu điều kiện cuối mảng thoả rồi thì nó không tiếp tục tính điều kiện kế. Vì vậy tránh được lỗi quá chỉ số.

Nhưng VBA không cho phép làm như vậy. Đối với VBA, tất cả các biểu thức con nằm trong biểu thức lô gic chính đều được tính. Vì vậy, cách thêm 1 dòng giả tuy hơi "nghịch đạo" 1 chút, nhưng lại là cách giản dị nhất.
 
Upvote 0
Chào mọi người ! Em có bài tập này cần tư vấn. Em đã giải được bằng đoạn CODE sau:
PHP:
Option Explicit
Sub dic2()
Dim dic, i, j, k, mang(1 To 6, 1 To 6)
Set dic = CreateObject("Scripting.Dictionary")
For i = 2 To 21
If Not dic.exists(Cells(i, 2).Value) Then
   j = j + 1
   mang(j, 1) = j
   dic.Add Cells(i, 2).Value, ""
   For k = 2 To 6
      mang(j, k) = Cells(i, k).Value
   Next k
End If
Next i
  Range("J9").Resize(dic.Count, 6) = mang
End Sub
Khi khai báo mảng , nếu thay vì viết như trên nếu em chỉ viết là : mang thì sẽ bị báo lỗi. Tuy nhiên cái khó nằm ở con số 6 kia nếu như ta không biết trước kết quả thì làm sao biết là viết số nào vào chổ ấy, với chiều cột thì mình còn đếm được là 6 chứ với chiều hàng thì mình biết đếm sao ???, hổng lẽ lại khai báo là mang( 1 to 21, 1 to 6) .

Vậy xin hỏi mọi người có cách nào để giải quyết được vấn đề trên không ạ. Em xin cảm ơn !
 

File đính kèm

  • Cauhoi4_Dic.xls
    38.5 KB · Đọc: 27
Lần chỉnh sửa cuối:
Upvote 0
Chào mọi người ! Em có bài tập này cần tư vấn. Em đã giải được bằng đoạn CODE sau:
PHP:
Option Explicit
Sub dic2()
Dim dic, i, j, k, mang(1 To 6, 1 To 6)
Set dic = CreateObject("Scripting.Dictionary")
For i = 2 To 21
If Not dic.exists(Cells(i, 2).Value) Then
   j = j + 1
   mang(j, 1) = j
   dic.Add Cells(i, 2).Value, ""
   For k = 2 To 6
      mang(j, k) = Cells(i, k).Value
   Next k
End If
Next i
  Range("J9").Resize(dic.Count, 6) = mang
End Sub
Khi khai báo mảng , nếu thay vì viết như trên nếu em chỉ viết là : mang thì sẽ bị báo lỗi. Tuy nhiên cái khó nằm ở con số 6 kia nếu như ta không biết trước kết quả thì làm sao biết là viết số nào vào chổ ấy, với chiều cột thì mình còn đếm được là 6 chứ với chiều hàng thì mình biết đếm sao ???, hổng lẽ lại khai báo là mang( 1 to 21, 1 to 6) .

Vậy xin hỏi mọi người có cách nào để giải quyết được vấn đề trên không ạ. Em xin cảm ơn !

Đi tìm bài về mảng động, sẽ thấy cách giải quyết. mangr động là loại mảng có số phần tử thay đổi được.

Đại khái là khi khai báo thì dùng hai dấu ngoặc trống, và khi cho kích thước thì dùng lệnh Redim:

Dim mang() ' dấu ngoặc trống, không có tham số, báo cho VBA biết biến này là loại mảng động
...
Dim lb1 As Long, ub1 As Long, lb2 As Long, ub2 As Long
lb1 = 1: ub1 = 21: lb2 = 1: ub2 = 6 ' có thể dùng biểu thức để gán trị cho các biến này
ReDim mang(lb1 To ub1, lb2 To ub2) ' kích thước mảng được định ở đây
 
Upvote 0
Dim lb1 As Long, ub1 As Long, lb2 As Long, ub2 As Long
lb1 = 1: ub1 = 21: lb2 = 1: ub2 = 6 ' có thể dùng biểu thức để gán trị cho các biến này
ReDim mang(lb1 To ub1, lb2 To ub2) ' kích thước mảng được định ở đây

Vấn đề của em là làm cách nào để biết được chiều dọc của mảng có 6 hàng mà không phải dùng đến số 21 í

hổng lẽ lại khai báo là mang( 1 to 21, 1 to 6) .

!

Và cho em xin hỏi thêm , em viết lại như vầy mà cũng không đúng là sao ạ:
PHP:
Option Explicit
Sub dic2()
Dim dic, i, j, k, mang()
Set dic = CreateObject("Scripting.Dictionary")
For i = 2 To 21
If Not dic.exists(Cells(i, 2).Value) Then
   j = j + 1
   mang(j, 1) = j
   dic.Add Cells(i, 2).Value, ""
   For k = 2 To 6
      mang(j, k) = Cells(i, k).Value
   Next k
End If
Next i
  ReDim mang(1 To dic.Count, 1 To 6)
  Range("J9").Resize(dic.Count, 6) = mang()
End Sub
 
Upvote 0
Vấn đề của em là làm cách nào để biết được chiều dọc của mảng có 6 hàng mà không phải dùng đến số 21 í



Và cho em xin hỏi thêm , em viết lại như vầy mà cũng không đúng là sao ạ:

1/ Để biết được có 6 hàng thì bạn có thể làm như sau:

- B1: Add tất cả các phần tử vào Dic
- B2: ngay sau khi add Redim lại mảng theo số hàng là dic.Count


2/ Code của bạn sai vì bạn chưa khai báo ubound của mảng mà bạn đã add dữ liệu vào mảng. Bạn hãy làm như (1) để có kết quả đúng
 
Upvote 0
Bạn chưa biết số dòng dữ liệu của mảng sẽ là bao nhiêu. Như vậy bạn có 2 cách làm.

1. Như bài #696, nạp dic trước, Redim mang theo dic.count

2. Cứ làm theo code của bạn. Lúc ghi xuống thì dùng dic.count và resize để giới hạn vùng ghi.
Vì bạn không biết trước dic bao lớn trước khi xong vòng lặp cho nên dùng UBound tối đa là đúng rồi.
Để chỉnh code của bạn, chỉ cần nhét thêm dòng
Redim mang(1 to 21, 1 to 6) ngay trước khi bắt đầu sử dụng mảng, trong trường hợp này tức là trước vòng lặp.
Con số 21 có lẽ là giới hạn vùng dữ liệu input của bạn. Như vậy với trường hợp tổng quát, câu Redim bạn áp dụng vào lúc đã tính ra con số vùng dữ liệu.
vd
dim dongCuoi as Long
dongCuoi = công_thức_tính_dòng_cuối
Redim mang(1 to dongCuoi, 1 to 6)
hoặc là
Redim mang(1 to công_thức_tính_dòng_cuối, 1 to 6)
Con số 6 cũng vậy, bạn có thể thay nó bằng công thức tính cột cuối dữ liệu
 
Upvote 0
Vấn đề của em là làm cách nào để biết được chiều dọc của mảng có 6 hàng mà không phải dùng đến số 21 í
bạn có thể khai báo tới mảng tối đa có thể chứa được dữ liệu, trong bài này là 21, nó không chiếm bộ nhớ của bạn là bao.
nếu không thích cách này thì bạn lại tốn thêm thời gian nạp vào DIC và đếm, sau đó mới redim, tính ra thì cũng không lợi bao nhiêu, mà sau này muốn xem lại code phải đọc thêm phần DIC nữa, rất mất thời gian và mất công
 
Upvote 0
Em viết như vầy:
PHP:
Option Explicit
Sub dic2()
Dim dic, i, j, k, mang()
Set dic = CreateObject("Scripting.Dictionary")
For i = 2 To 21
If Not dic.exists(Cells(i, 2).Value) Then
   j = j + 1
   dic.Add Cells(i, 2).Value, ""
   ReDim mang(1 To dic.Count, 1 To 6)
   mang(j, 1) = j
   For k = 2 To 6
      mang(j, k) = Cells(i, k).Value
   Next k
End If
Next i
    Range("J9").Resize(dic.Count, 6) = mang()
End Sub

Thì nó chỉ ra kết quả hàng cuối cùng, khi em thêm từ khóa Preserve vào sau Redim thì lại bị bắt lỗi. Mọi người giúp em thêm lần nữa với ạ. :.,:.,
 
Upvote 0
Em viết như vầy:
PHP:
Option Explicit
Sub dic2()
Dim dic, i, j, k, mang()
Set dic = CreateObject("Scripting.Dictionary")
For i = 2 To 21
If Not dic.exists(Cells(i, 2).Value) Then
   j = j + 1
   dic.Add Cells(i, 2).Value, ""
   ReDim mang(1 To dic.Count, 1 To 6)
   mang(j, 1) = j
   For k = 2 To 6
      mang(j, k) = Cells(i, k).Value
   Next k
End If
Next i
    Range("J9").Resize(dic.Count, 6) = mang()
End Sub

Thì nó chỉ ra kết quả hàng cuối cùng, khi em thêm từ khóa Preserve vào sau Redim thì lại bị bắt lỗi. Mọi người giúp em thêm lần nữa với ạ. :.,:.,
Bạn làm như thế này
Mã:
Option Explicit
Sub dic2()
Dim dic, dicTMP, i, j, k, mang()
Set dic = CreateObject("Scripting.Dictionary")
'===============================================
Set dicTMP = CreateObject("Scripting.Dictionary")
For i = 2 To 21
    'Add du lieu vao Dic
    If Not dicTMP.exists(Cells(i, 2).Value) Then
        dicTMP.Add Cells(i, 2).Value, ""
    End If
Next
'Khai bao mang
ReDim mang(1 To dicTMP.Count, 1 To 6)
'================================================
'Dua du lieu vao mang - Code cua ban
For i = 2 To 21
    If Not dic.exists(Cells(i, 2).Value) Then
        j = j + 1
        mang(j, 1) = j
        dic.Add Cells(i, 2).Value, ""
        For k = 2 To 6
            mang(j, k) = Cells(i, k).Value
        Next k
    End If
Next i
Range("J9").Resize(UBound(mang, 1), 6) = mang()
End Sub
 
Upvote 0
Em viết như vầy:
PHP:
Option Explicit
Sub dic2()
Dim dic, i, j, k, mang()
Set dic = CreateObject("Scripting.Dictionary")
For i = 2 To 21
If Not dic.exists(Cells(i, 2).Value) Then
   j = j + 1
   dic.Add Cells(i, 2).Value, ""
   ReDim mang(1 To dic.Count, 1 To 6)
   mang(j, 1) = j
   For k = 2 To 6
      mang(j, k) = Cells(i, k).Value
   Next k
End If
Next i
    Range("J9").Resize(dic.Count, 6) = mang()
End Sub

Thì nó chỉ ra kết quả hàng cuối cùng, khi em thêm từ khóa Preserve vào sau Redim thì lại bị bắt lỗi. Mọi người giúp em thêm lần nữa với ạ. :.,:.,

1/ Bạn khai báo mang(), nghĩa là khai báo mảng 2 chiều
2/ Mảng 2 chiều khi Redim thì nó tạo lại 1 mảng "trống trơn".
3/ Mảng 2 chiều không thể Preserve.
4/ Bạn muốn thử dùng mảng thì chơi luôn mảng, sao xài Cells() cho "nửa nạc nửa mỡ"?
5/ Mới bước vào "con đường đau khổ" thì phải tập khai báo tường minh các biến, đừng "làm biếng" thành thói quen, dù là code vẫn chạy.
6/ Trường hợp của bạn, dữ liệu có lẽ từ B2 đến F21? Xác định được mảng Data thì dựa vào chiều dọc ngang của mảng Data mà khai báo mang(), dùng mảng thì dù bạn khai bào "dư chút đỉnh" cũng chẳng "bựa" chút nào khi chạy code đâu mà sợ.
6/ Đọc code của bạn có thể xài cái này:
PHP:
Sub dic2()
Dim Dic As Object, i As Long, j As Long, k As Long, sArr(), Mang()
Set Dic = CreateObject("Scripting.Dictionary")
sArr = Range("B2:F21").Value               '<-----------Bằng cách nào đó bạn phải xác định được Range() này'
ReDim Mang(1 To UBound(sArr, 1), 1 To UBound(sArr, 2) + 1)
For i = 1 To UBound(sArr, 1)
    If Not Dic.Exists(sArr(i, 1)) Then
        k = k + 1
        Dic.Add sArr(i, 1), ""
        Mang(k, 1) = k
        For j = 1 To UBound(sArr, 2)
            Mang(k, j + 1) = sArr(i, j)
        Next j
    End If
Next i
Range("J9").Resize(k, UBound(Mang, 2)) = Mang
Set Dic=Nothing
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
...
Thì nó chỉ ra kết quả hàng cuối cùng, khi em thêm từ khóa Preserve vào sau Redim thì lại bị bắt lỗi. Mọi người giúp em thêm lần nữa với ạ

Khi Redim, dữ liệu trong mảng sẽ bị xóa. Vì vậy các dòng trước đó sẽ trống trơn.
Khi bạn biết thêm từ khóa Preserve vào thì có lẽ bạn đã biết tại sao nó chỉ cho ra dòng cuối cùng.

Tuy nhiên, cái bạn quên để ý là Redim Preserve chỉ cho phép đổi chiều cuối cùng. Mảng của bạn 2 chiều cho nên nó chỉ cho đổi chiều thứ 2. Điều này đã có nhiều bài bàn cãi qua, và tôi cũng có từng giải thích tại sao VBA lại đặt ta luật như vậy.

Trong bài trước đó, tôi có đề nghị là bạn Redim trước khi bắt đầu vòng lặp. Không phải là không có lý do.

Theo lô gic bài toán, vòng lặp của bạn biết trước là nó sẽ chạy 21 lần, không có lý do gì mảng của bạn không thể redim trước là 21 dòng. Nếu bạn thay đổi số cuối vòng lặp bằng bất cứ cái gì thì cũng có thể thay redim mảng của bạn bằng cái ấy.
 
Upvote 0
quote_icon.png
Nguyên văn bởi Ba Tê

3/ Mảng 2 chiều không thể Preserve.

mình xin phép nghi ngờ điều này

Bạn kia chỉ nói vắn tắt thôi. Bắt bẻ làm gì.
Cần bổ xung thì nói thẳng ra. Úp mở chỉ mất công tranh cãi.
 
Upvote 0
Hình như vẫn Preserve được nhưng chỉ có tác dụng với chiều thứ nhất thì phải

mảng một chiều thì có thể preverse tăng thêm dòng
mảng 2 chiều chỉ có thể preverse tăng thêm cột
(tôi xài từ "dòng", "cột", không biết chính xác trong mảng người ta gọi nó là cái gi.....hihiih)
 
Upvote 0
Đầu xuân, năm mới. Tôi xin được kính chúc các anh chị biên tập viên và các thầy giáo của diễn đàn GPE lời chúc sức khỏe hạnh phúc và thành công. Tôi có một vấn đề này mong các anh chị giúp đỡ viết cho code của nút NHAP. Các yêu cầu đã ghi rõ trong file đính kèm.
Xin trân trọng cảm ơn!

Tôi không biết đăng ở đâu. Nếu có gì sai xin được sự cảm thông của Anh chị em và các thầy nhé!
 

File đính kèm

  • NHAP PHIEU Nhap.xlsx
    19.1 KB · Đọc: 18
Upvote 0
Đầu xuân, năm mới. Tôi xin được kính chúc các anh chị biên tập viên và các thầy giáo của diễn đàn GPE lời chúc sức khỏe hạnh phúc và thành công. Tôi có một vấn đề này mong các anh chị giúp đỡ viết cho code của nút NHAP. Các yêu cầu đã ghi rõ trong file đính kèm.
Xin trân trọng cảm ơn!

Tôi không biết đăng ở đâu. Nếu có gì sai xin được sự cảm thông của Anh chị em và các thầy nhé!

Thứ nhất: Bài của bạn thuộc diện xen ngang; Nó có thể bị xóa cùng với bài này của mình.

Bài này iêu cầu làm bằng macro; Nên nó fải ở trong mục "lập trình"

Điều nữa: Thiết kế trang tính CSDL của bạn sẽ có vấn đề không tốt khi vận hành sau này;
Nếu là mình thì mình tách ra làm 2 bảng dữ liệu như sau:
Trang chính có tên là "Chung" gồm các trường/cột sau:
[STT] Dùng để ghi STT các fiếu
[Mã khóa] - Mã này để liên hệ với trang tính "chi tiết"
[Số fiếu] - Là số fiếu nhập của bạn
[Người giao]
[Người nhận]
[Ghi chú]

Trang thứ hai là có tên là "Chi tiết", gồm các trường
[STT] như trên
[Mã khóa] - Như trên
[Mã hàng]
[Tên hàng]
[Đơn vị tính]
[Đơn giá]
[Số lượng]
[Thành tiền]
Ở trang chi tiết này ta ghi mỗi loại hàng của từng hóa đơn 1 dòng;
Như vậy 2 trang tính này có mối quan hệ 1 - nhiều với nhau
 
Upvote 0
Cảm ơn bác SA_DQ nhiều!. về cấu trúc dữ liệu thị đây chỉ là file ví dụ. Tôi đã làm file có cả mã hàng, tên hàng hóa...
Mong các bác viết hộ phần code để nhập vào sheet tổng hợp.
Cảm ơn!
 
Upvote 0
Tôi dự định chỉ phân quyền cho những user có tên trong danh sách và tại các máy được chỉ định (việc này sẽ được kiểm tra khi file được mở) tôi nghĩ nên dùng mảng và for để tìm trong mảng (nếu tìm không thấy thì đóng file lại), nhưng không biết phải khai báo như thế nào và tìm ra làm sao... Mong các anh chị chỉ giáo.
 

File đính kèm

  • Book1.xlsx
    8.8 KB · Đọc: 11
Upvote 0
Web KT

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

Back
Top Bottom