Nhờ các thày giúp cho vài ví dụ về truyền tham số cho thủ tục Sub (hoặc hàm Function) (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

Dauthivan

Thành viên tiêu biểu
Tham gia
15/8/08
Bài viết
565
Được thích
327
Em đang nghiên cứu về truyền tham số bằng giá trị (byVal tức Byvalue), em có tìm đọc trên diễn đàn thì hầu hết nói rất nhanh vấn đề này, chính vì vậy em nhờ mọi người giúp em:

- Tại sao lại phải cần thiết truyền tham số.

- Giúp em một vài ví dụ để em hiểu về "cách truyền" của nó

Xin đa tạ các thày
 
Em đang nghiên cứu về truyền tham số bằng giá trị (byVal tức Byvalue), em có tìm đọc trên diễn đàn thì hầu hết nói rất nhanh vấn đề này, chính vì vậy em nhờ mọi người giúp em:

- Tại sao lại phải cần thiết truyền tham số.

- Giúp em một vài ví dụ để em hiểu về "cách truyền" của nó

Xin đa tạ các thày

Truyền tham số như một cách làm giúp tận dụng ưu điểm của việc chia thành nhiều sub hay function. Cụ thể
PHP:
Sub tong(a as integer)
dim tong as integer
tong=a+5
msgbox tong
End Sub

Sub thunghiem()
Call tong(5)
Call tong(6)
Call tong(7)
End Sub

Giả sử như bạn không truyền tham số, bạn sẽ phải làm thế này
PHP:
Public a as integer
Sub tong()
dim tong as integer, a as integer
tong=a+5
msgbox tong
End Sub

Sub thunghiem()
a=5
Call tong
a=6
Call tong
a=7
Call tong
End Sub

Với chương trình đơn giản như trên thì có lẽ sẽ không có nhiều khác biệt nhưng với chương trình phức tạp hơn thì có lẽ sẽ là một cách biệt vô cùng lớn đấy.

Tương tự với function, còn sự khác biệt giữa sub với function chắc bạn biết nhỉ (thấy bạn không có hỏi).
 
Lần chỉnh sửa cuối:
Upvote 0
Anh kyo ơi, anh giúp em lấy một ví dụ truyền tham số trong trường hợp tham số truyền là vùng lựa chọn trên màn hình (ví dụ ta dùng chuột lựa chọn vùng A1:A10 chẳng hạn), nó sẽ chuyển vào Sub tổng, tức là vai trò của vùng lựa chọn như là vai trò của a trong VD của anh vậy
 
Upvote 0
Anh kyo ơi, anh giúp em lấy một ví dụ truyền tham số trong trường hợp tham số truyền là vùng lựa chọn trên màn hình (ví dụ ta dùng chuột lựa chọn vùng A1:A10 chẳng hạn), nó sẽ chuyển vào Sub tổng, tức là vai trò của vùng lựa chọn như là vai trò của a trong VD của anh vậy
Làm đơn giản thì thế này
PHP:
Sub tong(diachi As Range)
MsgBox WorksheetFunction.Sum(diachi)
End Sub

PHP:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Call tong(Target)
End Sub

Tức là khi chọn một vùng range trên màn hình thì nó sẽ gọi sub Tong với tham số truyền là địa chỉ vùng đó, sau đó trong sub Tong nó sẽ lấy cái địa chỉ vùng (tức là biến diachi) vào trong hàm Sum để tính toán. Đây chỉ là một ví dụ để giúp bạn hiểu rõ hơn về tham số truyền thôi chứ thực chất cái ví dụ này gộp thành một sub cho nó lẹ.
 
Upvote 0
Tức là khi chọn một vùng range trên màn hình thì nó sẽ gọi sub Tong với tham số truyền là địa chỉ vùng đó, sau đó trong sub Tong nó sẽ lấy cái địa chỉ vùng (tức là biến diachi) vào trong hàm Sum để tính toán. Đây chỉ là một ví dụ để giúp bạn hiểu rõ hơn về tham số truyền thôi chứ thực chất cái ví dụ này gộp thành một sub cho nó lẹ.

Cũng thí dụ đó, viết thế này cho rộng hiểu:

PHP:
Sub tong(Rng As Range)
  MsgBox WorksheetFunction.Sum(Rng)
End Sub

PHP:
Sub TongSelection()
Call Tong(Selection)
End Sub

Ghi chú:
- Đặt tên biến cho 1 Range đừng đặt diachi As Range, sẽ nhầm lẫn với Range.Address (String)
- Đưa vào sự kiện Selection_Change, hễ rục rịch nhúc nhích gì đều tính tổng!
 
Upvote 0
Em nghĩ ra một cái, nhưng sao nó không chạy được
PHP:
Sub Tinhs()
    A = InputBox("Vao gia tri A", , 4)
    B = InputBox("Vao gia tri B", , 5)
    C = InputBox("Vao gia tri C", , 6)
    Giatri = A ^ 2 + B - C
    MsgBox "Ham Giatri =" & Giatri(A, B, C)
End Sub
 
Upvote 0
Em nghĩ ra một cái, nhưng sao nó không chạy được
PHP:
Sub Tinhs()
    A = InputBox("Vao gia tri A", , 4)
    B = InputBox("Vao gia tri B", , 5)
    C = InputBox("Vao gia tri C", , 6)
    Giatri = A ^ 2 + B - C
    MsgBox "Ham Giatri =" & Giatri(A, B, C)
End Sub

Thế cái Sub Giatri(byVal A, byVal B, byVal C) của bạn đâu?
Có tính toán gì cũng sẽ tính trong sub này chứ không phải trong Sub Tinhs đâu nha
Ít nhất cũng phải vầy:
PHP:
Sub Giatri(A, B, C)
  Dim tmp
  tmp = A ^ 2 + B - C
  MsgBox "Ham Giatri =" & tmp
End Sub
PHP:
Sub Tinhs()
  Dim A, B, C
  A = InputBox("Vao gia tri A", , 4)
  B = InputBox("Vao gia tri B", , 5)
  C = InputBox("Vao gia tri C", , 6)
  Call Giatri(A, B, C)
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Em nghĩ ra một cái, nhưng sao nó không chạy được
PHP:
Sub Tinhs()
    A = InputBox("Vao gia tri A", , 4)
    B = InputBox("Vao gia tri B", , 5)
    C = InputBox("Vao gia tri C", , 6)
    Giatri = A ^ 2 + B - C
    MsgBox "Ham Giatri =" & Giatri(A, B, C)
End Sub

PHP:
Sub Tinhs()
Dim i As Integer, j As Integer, k As Integer
    i = InputBox("Vao gia tri A", , 4)
    j = InputBox("Vao gia tri B", , 5)
    k = InputBox("Vao gia tri C", , 6)
    MsgBox "Ham Giatri =" & Giatri(i, j, k)
End Sub

Function Giatri(a As Integer, b As Integer, c As Integer)
Giatri = a ^ 2 + b - c
End Function

Thế này mới đúng vì bạn gọi Giatri tức là bạn phải gọi là một Sub hoặc một Function chứ sao bạn lại làm như thế kia, bạn nhìn các ví dụ trên thì bạn sẽ thấy, muốn sử dụng tham số truyền thì phải qua một Sub hoặc một Function. Mình làm lại cho bạn thế này nhưng mà sẽ có xảy ra lỗi nếu không nhập số đấy nhé, chỉ là một ví dụ cho bạn hiểu thôi.
 
Upvote 0
Vâng, em đang tù mù về cái này, đang tưởng tượng ra cái Sub này tại sao lại phải gọi từ cái Sub (hoặc Funtion) khác mà sao không tính luôn trong nó? Sao phức tạp quá vậy
 
Upvote 0
Trong tất cả thí dụ trên, đều có 2 Sub, 1 sub A có tham số và 1 sub gọi B. Sub gọi B phải truyền tham số cho Sub A, nếu không Sub A cũng không chạy.

Thế mà đọc xong mấy thí dụ vẫn đành đoạn viết mỗi 1 sub.
 
Upvote 0
Vâng, em đang tù mù về cái này, đang tưởng tượng ra cái Sub này tại sao lại phải gọi từ cái Sub (hoặc Funtion) khác mà sao không tính luôn trong nó? Sao phức tạp quá vậy

Bạn cứ hiểu là xài Sub hay Function như một cách để bạn xài dần lâu ngày, giả sử bạn muốn xài Sub Tong 10 lần với những biến khác nhau thì thay vì bạn phải copy paste cả đoạn code của bạn rồi đổi biến thì bạn đã có sẵn một cái khuôn, chỉ việc truyền tham số biến khác vào thôi. Còn vấn đề bạn thắc mắc tại sao không tính luôn thì cũng có cách cho bạn làm mà, chỉ vì bạn sai cú pháp thôi. Nhưng làm thế này, giả sử bạn lại muốn tính Giatri nhưng trong một Sub khác thì bạn làm thế nào, chẳng lẽ lại phải copy paste toàn bộ những gì trong đây ra để làm lại sao?
PHP:
Sub Tinhs() 
    A = InputBox("Vao gia tri A", , 4) 
    B = InputBox("Vao gia tri B", , 5) 
    C = InputBox("Vao gia tri C", , 6) 
    Giatri = A ^ 2 + B - C 
    MsgBox "Ham Giatri =" & Giatri
End Sub
 
Upvote 0
Vâng, em đang tù mù về cái này, đang tưởng tượng ra cái Sub này tại sao lại phải gọi từ cái Sub (hoặc Funtion) khác mà sao không tính luôn trong nó? Sao phức tạp quá vậy
Thì tùy bạn thôi... Bạn cảm thấy tính luôn là thuận tiện thì cứ làm
Có trường hợp thế này:
- Ta thực hiện tính toán gì đó trên bảng tính của ta, đương nhiên code chỉ đúng với dữ liệu của riêng ta
- Ta nghĩ đến việc "tổng quát hóa" để sang máy tính khác, dữ liệu khác cũng sẽ dùng được
- Vậy là ta buộc phải viết code thành 1 Function hoặc Sub có tham số truyền để khi người ta áp dụng trên dữ liệu khác, tự người ta truyền vào giá trị tùy ý
Ví dụ:
- Code bạn viết thực thi tính toán trên vùng A1:A100
- Sang máy khác, người ta muốn tính trên vùng C1:C100
- Vậy phải sửa lại toàn bộ, chổ nào có A1:A100 thì phải thay thế thành C1:C100 ---> Điều này không phải không làm được nhưng có khi thay thế nhiều quá khiến ta nhầm lẫn
- Với Sub có tham số truyền hoặc Function, ta chỉ cần cho nó thành 1 biến Range nào đó (SrcRng as Range chẳng hạn) ---> Ai muốn SrcRng là vùng nào, cứ việc điền vào là xong
 
Upvote 0
Vì em đang tìm hiểu cái thực chất tại sao phải tách làm hai Sub, nó có tác dụng gì hơn so với làm trực tiếp 1 Sub, em "cố tình" so sánh giữa 2 cái đó với nhau
Tại sao người ta không dùng
PHP:
Sub Tinhs()
    A = InputBox("Vao gia tri A", , 4)
    B = InputBox("Vao gia tri B", , 5)
    C = InputBox("Vao gia tri C", , 6)
    Giatri = A ^ 2 + B - C
    MsgBox "Giatri =" & Giatri
End Sub

mà cứ khoái kết hợp thế này
PHP:
Sub Tinhs()
Dim i As Integer, j As Integer, k As Integer
    i = InputBox("Vao gia tri A", , 4)
    j = InputBox("Vao gia tri B", , 5)
    k = InputBox("Vao gia tri C", , 6)
    MsgBox "Ham Giatri =" & Giatri(i, j, k)
End Sub

Function Giatri(a As Integer, b As Integer, c As Integer)
Giatri = a ^ 2 + b - c
End Function

Xin sư phụ chỉ cho
 
Upvote 0
Ví dụ:
- Code bạn viết thực thi tính toán trên vùng A1:A100
- Sang máy khác, người ta muốn tính trên vùng C1:C100
- Vậy phải sửa lại toàn bộ, chổ nào có A1:A100 thì phải thay thế thành C1:C100 ---> Điều này không phải không làm được nhưng có khi thay thế nhiều quá khiến ta nhầm lẫn

Em đã hiểu được mục đích của nó sau khi đọc xong bài của sư phụ Ndu, anh Kyo, em đang rất cần một ví dụ đơn giản cụ thể hoá những cái trên để em đỡ phải hình dung.

Mong nhận được sự quan tâm, giúp đỡ của các sư phụ
-----------------
Em chưa hiểu được
PHP:
Sub Tinhs()
Dim i As Integer, j As Integer, k As Integer
    i = InputBox("Vao gia tri i", , 4)
    j = InputBox("Vao gia tri j", , 5)
    k = InputBox("Vao gia tri k", , 6)
    MsgBox "Ham Giatri =" & Giatri(i, j, k)
End Sub

Function Giatri(a As Integer, b As Integer, c As Integer)
Giatri = a ^ 2 + b - c
End Function
thì Sub làm gì có a,b,c đâu mà Funtion vẫn tính được nhỉ.
Vì Funtion là a,b,c còn Sub là i,j,k cơ mà
 
Lần chỉnh sửa cuối:
Upvote 0
Vì em đang tìm hiểu cái thực chất tại sao phải tách làm hai Sub, nó có tác dụng gì hơn so với làm trực tiếp 1 Sub, em "cố tình" so sánh giữa 2 cái đó với nhau
Tại sao người ta không dùng
PHP:
Sub Tinhs()
    A = InputBox("Vao gia tri A", , 4)
    B = InputBox("Vao gia tri B", , 5)
    C = InputBox("Vao gia tri C", , 6)
    Giatri = A ^ 2 + B - C
    MsgBox "Giatri =" & Giatri
End Sub

mà cứ khoái kết hợp thế này
PHP:
Sub Tinhs()
Dim i As Integer, j As Integer, k As Integer
    i = InputBox("Vao gia tri A", , 4)
    j = InputBox("Vao gia tri B", , 5)
    k = InputBox("Vao gia tri C", , 6)
    MsgBox "Ham Giatri =" & Giatri(i, j, k)
End Sub

Function Giatri(a As Integer, b As Integer, c As Integer)
Giatri = a ^ 2 + b - c
End Function

Xin sư phụ chỉ cho
Mấy ví dụ trên thuộc dạng đơn giản nên cũng cảm thấy việc chia ra 2 Sub chẳng ngon lành gì so với gộp chung lại
Giờ bạn hãy xem bài này:
http://www.giaiphapexcel.com/forum/showthread.php?29665-Dò-tìm-và-tô-màu-ký-tự
và cho biết nếu không viết thành Sub có tham số truyền riêng thì bạn sẽ làm sao để ai mang về cũng xài được với dữ liệu riêng của mình?
- Nếu viết theo kiểu gộp chung lại, mang sang file khác để dùng, buộc bạn phải sửa lại toàn bộ các tham chiếu cho phù hợp (với dữ liệu mới) ---> Điều này không phải không làm được nhưng sẽ khá rắc rối với những ai mới tiếp cần VBA vì biết chổ nào đâu mà sửa
- Trong khi nếu tôi viết thành 1 Sub có tham số truyền thi mọi chuyện trở nên đơn giản hơn... Người dùng thậm chí không cần quan tâm đến code trong Sub chính viết gì, chỉ cần biết cú pháp là dùng được
- Như code trong bài trên có Sub chính là:
PHP:
Private Sub Character_Color(Rng As Range, ByVal Text As String, ByVal MCase As Boolean)
.....
....
End Sub
Bên trong nó viết gì tôi không cần quan tâm, chỉ cần biết khi muốn dùng thì tôi sẽ viết 1 sub để gọi như thế này:
PHP:
Sub Chay
  Character_Color Range("gì gì đó"), Chuổi tìm kiếm, TRUE
End Sub
Gọi đúng theo cú pháp mà Sub Character_Color đưa ra:
- Vùng cần tô màu là vùng nào ---> truyền vào biến Rng
- Chuổi tìm kiếm vào chuổi nào ---> Cho vào biến Text
- Tìm kiếm có phân biệt HOA thường không ---> Cho vao biến MCase (là TRUE hoặc FALSE)
Vậy là xong
Nói thêm: Cái sự "đơn giản hóa" ở đây là dành cho người dùng, giúp họ thuận tiên khi dùng 1 code... Ta "đóng gói" mọi thứ vào 1 Sub (có tham số truyền), khi người ta xài, chỉ cần cho biết các tham số ấy là những gì, thế là chạy mà không cần quan tâm đến Sub chính viết những gì trong đó
 
Lần chỉnh sửa cuối:
Upvote 0
Mình nghĩ việc này cũng dễ hiểu thôi:
- Nếu bạn chỉ muốn thực hiện một dãy lệnh trên một vùng cụ thể chỉ 1 lần (hoặc rất ít lần) thì bạn chẳng cần phải dùng đến Sub hay Function thứ 2 mà chỉ cần đưa dãy lệnh này vào vị trí cần thiết.
- Nếu bạn muốn thực hiện dãy lệnh này nhiều lần và áp dụng với nhiều dữ liệu đầu vào khác nhau (chẳng hạn: trên nhiều vùng khác nhau, số liệu khác nhau,...) thì bạn hãy nghĩ đến việc tách dãy lệnh này ra thành 1 Sub hoặc 1 Function riêng cho gọn. Mỗi lần cần thực hiện dãy lệnh này thì thay vì phải gõ cả dãy lệnh, bạn chỉ cần gọi tên Sub hoặc Function tương ứng là xong. Cái này có thể xem như là việc chuyên môn hóa trong phân công nhiệm vụ đó mà.
 
Upvote 0
Mình nghĩ việc này cũng dễ hiểu thôi:
- Nếu bạn chỉ muốn thực hiện một dãy lệnh trên một vùng cụ thể chỉ 1 lần (hoặc rất ít lần) thì bạn chẳng cần phải dùng đến Sub hay Function thứ 2 mà chỉ cần đưa dãy lệnh này vào vị trí cần thiết.
- Nếu bạn muốn thực hiện dãy lệnh này nhiều lần và áp dụng với nhiều dữ liệu đầu vào khác nhau (chẳng hạn: trên nhiều vùng khác nhau, số liệu khác nhau,...) thì bạn hãy nghĩ đến việc tách dãy lệnh này ra thành 1 Sub hoặc 1 Function riêng cho gọn. Mỗi lần cần thực hiện dãy lệnh này thì thay vì phải gõ cả dãy lệnh, bạn chỉ cần gọi tên Sub hoặc Function tương ứng là xong. Cái này có thể xem như là việc chuyên môn hóa trong phân công nhiệm vụ đó mà.
Thật ra rút gọn code không phải là ý nghĩa chính của việc dùng Sub có tham số truyền... Ý nghĩa lớn nhất của nó là "đơn giản hóa" giúp người dùng thuận tiện hơn khi sử dụng (chỉ cần làm theo cú pháp, không quan tâm nội dung viết gì)
Một ví dụ thực tế minh họa tốt nhất cho Sub có tham số truyền đó là con IC trong vi mạch ----> Chỉ cần mua con IC về, biết được mỗi 1 chân của nó có công dụng gì là xài được rồi (không cần quan tâm đến các mạch trong ruột con IC được tạo ra như thế nào)
 
Upvote 0
Em chưa hiểu được
PHP Code:
Sub Tinhs()
Dim i As Integer, j As Integer, k As Integer
i
= InputBox("Vao gia tri i", , 4)
j = InputBox("Vao gia tri j", , 5)
k = InputBox("Vao gia tri k", , 6)
MsgBox "Ham Giatri =" & Giatri(i, j, k)
End Sub

Function Giatri(a As Integer, b As Integer, c As Integer)
Giatri = a ^ 2 + b - c
End
Function


thì Sub làm gì có a,b,c đâu mà Funtion vẫn tính được nhỉ.
Vì Funtion là a,b,c còn Sub là i,j,k cơ mà
Em hiểu thế này thì có đúng không ah: nhờ Sub có cái thành phần Giatri(i, j, k) mà Funtion nó hiểu thứ tự gán như sau: a=i, b=j, c=k nghĩa là a, b,c là biến tổng quát, còn i,j,k là cụ thể hóa trong từng trường hợp phải không ah?
 
Lần chỉnh sửa cuối:
Upvote 0
Em hiểu thế này thì có đúng không ah: nhờ Sub có cái thành phần Giatri(i, j, k) mà Funtion nó hiểu thứ tự gán như sau: a=i, b=j, c=k nghĩa là a, b,c là biến tổng quát, còn i,j,k là cụ thể hóa trong từng trường hợp phải không ah?
Đúng là vậy!
Tuy nhiên phải phân biết Function và Sub nha
- Function: Cho kết quả là 1 giá trị nào đó sau quá trình tính toán... Vì thế code trong Function luôn có dang
Mã:
Function Gì gì đó (biến 1, biến 2,....) [B][COLOR=#ff0000]as Kiểu dữ liệu[/COLOR][/B]
  'Tính toan
  [COLOR=#ff0000][B]Gì gì đó[/B][/COLOR] = Giá trị nào đó ''<--- Kết quả của Function
End Function
Chú ý chổ màu đó
- Sub có tham số: Làm 1 công việc gì đó theo các tham số... Nó không có kiểu dữ liệu cũng như không cần kết quả.. Nó có dạng thế này
Mã:
Sub Gì gì đó (biến 1, biến 2,....)
  'Tính toán và làm việc theo các biến 1, biến 2...
End Sub
 
Upvote 0
Em hiểu thế này thì có đúng không ah: nhờ Sub có cái thành phần Giatri(i, j, k) mà Funtion nó hiểu thứ tự gán như sau: a=i, b=j, c=k nghĩa là a, b,c là biến tổng quát, còn i,j,k là cụ thể hóa trong từng trường hợp phải không ah?
Bạn hiểu đúng rồi đó.
Trong hàm Giatri ở trên thì a, b, c được gọi là tham số hình thức, và các lệnh trong hàm Giatri sẽ cho biết hàm Giatri này được tính bởi công thức nào thông qua a, b, c. Còn khi sử dụng hàm này, ta phải thay các tham số hình thức này bởi các giá trị thực sự, chính là các số i, j, k được xác định bằng Inputbox trong sub Tinhs, các số i, j, k này được gọi là tham số thực sự. Và khi gọi hàm này trong chương trình, VBA sẽ thay thế các tham số hình thức bởi các tham số thực sự và dựa vào quy tắc tính toán trong dãy lệnh để cho ra một kết quả cụ thể.
Thực ra thì để tìm hiểu về vấn đề này, bạn cũng có thể tham khảo trong SGK Tin học 11 (nghĩa là học sinh phổ thông cũng đã được trang bị kiến thức này rồi).
 
Upvote 0
Đây là nhiệm vụ cụ thể để bạn dễ hình dung thêm:

Có 1 bạn trên diễn đàn nhờ bạn tạo mã duy nhất cho cở 750 nhân viên trong cơ quan với qui tắc sau:

Mã gồm 2 ký tự & nối tếp luôn là 3 ký số, như TH009 cho người có họ tên là Hà Chính Thống
& người có tên Hoàng Thi có mã TH008,. . . .
Nhận việc này, bạn sẽ bắt đầu từ đâu? (Có nghĩa là định hướng giải quyết vấn đề này của bạn ra sao?)
Ta biết rằng trên diễn đàn thầy Phạm duy Long có hàm để tách tên trong họ tên tiếng Việt;
Vậy bạn è cổ ra viết lại từ đầu hay đi tìm trên diễn đàn thân thương của chúng ta, cái hàm mà bạn cần. Để rối tiếp theo ta cắt lấy ký tự đầu của hàm đó:

Ma=Ma & Left$(TachTen(HoTen),1) & Left$(HoTen,1) & "000"

Giống NDU đã nói, các tàu con thoi có thể cái chở người, cái chở nguyên liệu của sự sống cho con người & thiết bị lên ISS.
Nhưng & không bao giờ vừa chở người vừa chở hùm bà lằng những thứ khác nữa cả; chuyên biệt hoá & chuyên môn hoá từng Module, sẽ an toàn cho con người hơn gấp nhiều lần. Bạn cũng đã gặp từ này trong CS VBE chứ, nhỉ? . . . . Nó đó bạn!
. . . . .
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Nếu bạn muốn tìm hiểu thêm về chủ đề này, mời bạn xem bài http://www.giaiphapexcel.com/forum/showthread.php?53572-Thắc-mắc-về-hàm-UDF-UniqueList/page2 của sư phụ Ptm0412

Nhắc mới nhớ, Bài đó áp dụng tham số truyền là mảng tự gõ. Là thử nghiệm đầu tiên của lão chết tiệt với tham số là mảng tự gõ. Viết xong bài này Cò già ngẩn tò te luôn.

PHP:
Sub UniqueAndSum(sArray, ArrCols, UniqueCol, ArrSumCols)
  ...
End Sub

PHP:
Sub Test()
  Sheet2.Range("G1:K100").ClearContents
  Arr1 = Sheet1.Range("A2:G50001").Value
  UniqueAndSum Arr1, Array(2, 1, 0, 4, 0, 7, 6), 2, Array(6, 7) 

End Sub

Nghĩa là như ndu nói:
Không cần biết Sub UniqueAndSum nó phức tạp cỡ nào, viết ra làm sao, chỉ cần nhớ cú pháp là chạy từ bất kỳ nguồn dữ liệu nào ra mọi dạng kết quả mong muốn.
 
Lần chỉnh sửa cuối:
Upvote 0
Cho bạn thêm 1 ví dụ nữa về truyền tham số nè

PHP:
Sub KiemTraTenSheet()
  Dim SheetArr, Item As Variant
  SheetArr = Array("Time", "Data", "Cot", "Dam")
  For Each Item In SheetArr
    If SheetExist(Item) = False Then MsgBox "Sheet " & Item & " khong ton tai"
  Next
MsgBox "Da kiem tra xong"
End Sub

PHP:
Function SheetExist(ByVal WorkSheetName As String) As Boolean
  On Error Resume Next
  SheetExist = Not Sheets(WorkSheetName) Is Nothing
End Function

Tuy nhiên, nếu bạn muốn kiến thức của mình lên nhanh thì cần phải thực hành nhiều vào (những bài nào mà thuộc dạng mình gặp rồi thì cố gắng tự làm trước).

Cố gắng lên nhé./.
 
Upvote 0
Nhờ sửa giúp lỗi bài toán lọc duy nhất bằng truyền tham số

Hôm nay mới có thời gian, đọc các bài trên diễn đàn em thử làm bài toán lọc duy nhất bằng truyền tham số mà không được, rất mong các anh, chị các thày chỉ cho em chỗ sai

PHP:
Sub Filter(sArray As Range)
    Dim SubArr, Tmp
    Set Dic = CreateObject("Scripting.Dictionary")
    SubArr = sArray
    For Each Tmp In SubArr
        If Tmp <> "" And Not Dic.Exists(Tmp) Then
            Dic.Add Tmp, ""
        End If
    Next
End Sub

PHP:
Sub Main()
    ReDim Arr(1 To UBound(Dic.Keys, 1), 1 To 1)
    sArray = Sheet1.Range("A1:B1000")
    Call Filter
    Arr = Dic.Keys
    Sheets("sheet2").[A1].Resize(UBound(Dic.Keys, 1)).Value = Arr
End Sub
 
Upvote 0
Hôm nay mới có thời gian, đọc các bài trên diễn đàn em thử làm bài toán lọc duy nhất bằng truyền tham số mà không được, rất mong các anh, chị các thày chỉ cho em chỗ sai

PHP:
Sub Filter(sArray As Range)
    Dim SubArr, Tmp
    Set Dic = CreateObject("Scripting.Dictionary")
    SubArr = sArray
    For Each Tmp In SubArr
        If Tmp <> "" And Not Dic.Exists(Tmp) Then
            Dic.Add Tmp, ""
        End If
    Next
End Sub

PHP:
Sub Main()
    ReDim Arr(1 To UBound(Dic.Keys, 1), 1 To 1)
    sArray = Sheet1.Range("A1:B1000")
    Call Filter
    Arr = Dic.Keys
    Sheets("sheet2").[A1].Resize(UBound(Dic.Keys, 1)).Value = Arr
End Sub

Sub Filter có tham số truyền mà bạn gọi nó lại không truyền cho nó tham số? Đáng lẽ phải là Call Filter(sArray) chứ nhỉ?
 
Lần chỉnh sửa cuối:
Upvote 0
Phiền anh Kyo và mọi người sửa lại đúng giúp em để nó có thể chạy được ah
 
Upvote 0
Phiền anh Kyo và mọi người sửa lại đúng giúp em để nó có thể chạy được ah

Sửa lại cho bạn như vầy, có điều vì bạn dùng For Each nên mình chưa nghĩ ra được cách nào để bỏ cái Transpose, chứ nếu dùng For Next thì dễ hơn. Mong các cao thủ chỉ kyo dùng For Each mà vẫn không cần phải xài cái Transpose
PHP:
Public Dic
Sub Filter(sArray As String)
    Dim SubArr, Tmp
    Set Dic = CreateObject("Scripting.Dictionary")
    SubArr = Sheets("sheet1").Range(sArray).Value
    For Each Tmp In SubArr
        If Tmp <> "" And Not Dic.Exists(Tmp) Then
            Dic.Add Tmp, ""
        End If
    Next
End Sub

Sub Main()
Dim sArray As String
    sArray = "A1:B1000"
    Call Filter(sArray)
    ReDim Arr(1 To UBound(Dic.Keys, 1), 1 To 1)
    Arr = Dic.Keys
    Sheets("sheet2").[A1].Resize(UBound(Dic.Keys, 1)).Value = WorksheetFunction.Transpose(Arr)
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Sửa lại cho bạn như vầy, có điều vì bạn dùng For Each nên mình chưa nghĩ ra được cách nào để bỏ cái Transpose, chứ nếu dùng For Next thì dễ hơn. Mong các cao thủ chỉ kyo dùng For Each mà vẫn không cần phải xài cái Transpose

Em cũng rất muốn làm theo For Next mà chưa nghĩ ra, nếu có thể phiền anh giúp em thêm cách này.
 
Upvote 0
Sửa lại cho bạn như vầy, có điều vì bạn dùng For Each nên mình chưa nghĩ ra được cách nào để bỏ cái Transpose, chứ nếu dùng For Next thì dễ hơn. Mong các cao thủ chỉ kyo dùng For Each mà vẫn không cần phải xài cái Transpose
PHP:
Public Dic
Sub Filter(sArray As String)
    Dim SubArr, Tmp
    Set Dic = CreateObject("Scripting.Dictionary")
    SubArr = Sheets("sheet1").Range(sArray).Value
    For Each Tmp In SubArr
        If Tmp <> "" And Not Dic.Exists(Tmp) Then
            Dic.Add Tmp, ""
        End If
    Next
End Sub

Sub Main()
Dim sArray As String
    sArray = "A1:B1000"
    Call Filter(sArray)
    ReDim Arr(1 To UBound(Dic.Keys, 1), 1 To 1)
    Arr = Dic.Keys
    Sheets("sheet2").[A1].Resize(UBound(Dic.Keys, 1)).Value = WorksheetFunction.Transpose(Arr)
End Sub
Có thể là vầy cũng được:
PHP:
Public Arr(), lR As Long
Sub Filter(ByVal SrcRng As Range)
  Dim SubArr, tmpArr, Item, Dic As Object
  On Error Resume Next
  lR = 0
  If SrcRng.Count = 1 Then
    ReDim Arr(0)
    Arr(0) = SrcRng.Value
    lR = 1
  Else
    tmpArr = SrcRng.Value
    ReDim Arr(1 To SrcRng.Rows.Count * SrcRng.Columns.Count, 1 To 1)
    Set Dic = CreateObject("Scripting.Dictionary")
    For Each Item In tmpArr
      If CStr(Item) <> "" Then
        If Not Dic.Exists(CStr(Item)) Then
          lR = lR + 1
          Dic.Add CStr(Item), ""
          Arr(lR, 1) = Item
        End If
      End If
    Next
  End If
End Sub
PHP:
Sub Main()
  Dim SrcRng As Range
  Set SrcRng = Sheet1.Range("A1:B1000")
  Filter SrcRng
  If lR Then Sheet2.Range("A1").Resize(lR).Value = Arr
End Sub
 
Upvote 0
Thưa thày, ByVal trong câu này
PHP:
Sub Filter(ByVal SrcRng As Range)
nó có tác dụng gì so với việc không có tức chỉ ngắn gọn như thế này
PHP:
Sub Filter(SrcRng As Range)
 
Upvote 0
Tại sao mình dùng Dic.Keys lại phải có Transpose mới xong, trong khi của thày Ndu cũng hướng đi về cơ bản gần như vậy mà có cần Transpose đâu?
 
Upvote 0
Tại sao mình dùng Dic.Keys lại phải có Transpose mới xong, trong khi của thày Ndu cũng hướng đi về cơ bản gần như vậy mà có cần Transpose đâu?
Vì Dic.Keys là mảng 1 chiều, phải TRANSPOSE nó mới "lọt" đúng vào cột bảng tính (không TRANSPOSE nó sẽ "ngang" ra thành 1 dòng)
--------------------------------------
Thưa thày, ByVal trong câu này
PHP:
Sub Filter(ByVal SrcRng As Range)
nó có tác dụng gì so với việc không có tức chỉ ngắn gọn như thế này
PHP:
Sub Filter(SrcRng As Range)
Có 2 loại byVal và byRef.
Nếu không viết gì có nghĩa là byRef, trong khi byVal là thứ mà ta thường xài, vậy nên phải ghi cho rõ, tránh trường hợp sai lầm đáng tiếc trong các bài toán lớn hơn
Còn như bạn muốn biết byVal nó khác byRef chổ nào, hãy tham khảo các bài viết sau:
http://www.giaiphapexcel.com/forum/showthread.php?35509-Thảo-luận-mở-rộng-về-Useful-functions-Các-hàm-hữu-ích-của-Lê-Văn-Duyệt&p=235377#post235377
http://www.giaiphapexcel.com/forum/showthread.php?3976-Worksheet_SelectionChange(ByVal-Target-As-Range)&p=27667#post27667
http://www.giaiphapexcel.com/forum/showthread.php?1171-Hướng-dẫn-truyền-tham-số-bằng-giá-trị-(ByVal)
 
Lần chỉnh sửa cuối:
Upvote 0
Tại sao mình dùng Dic.Keys lại phải có Transpose mới xong, trong khi của thày Ndu cũng hướng đi về cơ bản gần như vậy mà có cần Transpose đâu?
Bạn có thể bỏ Transpose với code của kyo sẽ thấy lý do tại sao phải Transpose. Ở đây, Transpose có nghĩa là đảo mảng qua 90 độ, tức là từ ngang thành dọc. Do mảng Dic là mảng ngang nên xuất ra dọc thì sẽ không được nên phải dùng Transpose.
Với code của chú ndu thì chú đã dùng một mảng 2 chiều với chiều thứ nhất là chiều dọc dùng để add dữ liệu nên không cần phải Transpose

Trở về câu hỏi xài For ... Next thì kyo làm thế này, bạn xem được không nhé.
PHP:
Sub Filter(sArray As String)
    Dim SubArr, Arr
    If Sheets("Sheet1").Range(sArray).Count = 1 Then
        ReDim Arr(0)
        Arr(0) = Sheets("sheet1").Range(sArray).Value
        GoTo Finish
    End If
    Set Dic = CreateObject("Scripting.Dictionary")
    SubArr = Sheets("sheet1").Range(sArray).Value
    ReDim Arr(1 To UBound(SubArr, 1) * UBound(SubArr, 2), 1 To 1)
    For i = LBound(SubArr, 1) To UBound(SubArr, 1) * UBound(SubArr, 2)
        If i > UBound(SubArr, 1) Then
            If CStr(SubArr(i - UBound(SubArr, 1), 2)) <> "" And _
                    Not Dic.Exists(CStr(SubArr(i - UBound(SubArr, 1), 2))) Then
                Dic.Add CStr(SubArr(i - UBound(SubArr, 1), 2)), ""
                Arr(Dic.Count, 1) = SubArr(i - UBound(SubArr, 1), 2)
            End If
        Else
            If CStr(SubArr(i, 1)) <> "" And Not Dic.Exists(CStr(SubArr(i, 1))) Then
                Dic.Add CStr(SubArr(i, 1)), ""
                Arr(Dic.Count, 1) = SubArr(i, 1)
            End If
        End If
    Next
Finish:
    Sheets("sheet2").[A1].Resize(UBound(Dic.Keys, 1) + 1).Value = Arr
End Sub

Sub Main()
Dim sArray As String
    sArray = "A1:B1000"
    Call Filter(sArray)
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Nếu dùng For Each thì như ndu, nếu dùng For Next thì dùng 2 vòng For: 1 theo cột và 1 theo dòng, viết và đọc code sẽ đơn giản hơn.
Ngoài ra còn phải bẫy trường hợp sau khi thành 1 chiều, số dòng có vượt quá số dòng bảng tính hay không.
 
Upvote 0
Trở về câu hỏi xài For ... Next thì kyo làm thế này, bạn xem được không nhé.
PHP:
Sub Filter(sArray As String)
    Dim SubArr, Arr
    If Sheets("Sheet1").Range(sArray).Count = 1 Then
        ReDim Arr(0)
        Arr(0) = Sheets("sheet1").Range(sArray).Value
        GoTo Finish
    End If
    Set Dic = CreateObject("Scripting.Dictionary")
    SubArr = Sheets("sheet1").Range(sArray).Value
    ReDim Arr(1 To UBound(SubArr, 1) * UBound(SubArr, 2), 1 To 1)
    For i = LBound(SubArr, 1) To UBound(SubArr, 1) * UBound(SubArr, 2)
        If i > UBound(SubArr, 1) Then
            If CStr(SubArr(i - UBound(SubArr, 1), 2)) <> "" And Not Dic.Exists(CStr(SubArr(i - UBound(SubArr, 1), 2))) Then
                Dic.Add CStr(SubArr(i - UBound(SubArr, 1), 2)), ""
                Arr(Dic.Count, 1) = SubArr(i - UBound(SubArr, 1), 2)
            End If
        Else
            If CStr(SubArr(i, 1)) <> "" And Not Dic.Exists(CStr(SubArr(i, 1))) Then
                Dic.Add CStr(SubArr(i, 1)), ""
                Arr(Dic.Count, 1) = SubArr(i, 1)
            End If
        End If
    Next
Finish:
    Sheets("sheet2").[A1].Resize(UBound(Dic.Keys, 1) + 1).Value = Arr
End Sub

Sub Main()
Dim sArray As String
    sArray = "A1:B1000"
    Call Filter(sArray)
End Sub

Thật ra, nếu làm bài này với mục đích để xài thì nên:
- Chuyển Sub Filter thành Function ---> Kết quả của Function trả về sẽ là 1 Array (1 chiều hay 2 chiều tự ta quyết định)
- Đối số của Function sẽ là 1 Array thật sự chứ không nên là 1 chuổi chứa địa chỉ cell ---> Mục đích để có thể truyền vào nhiều kiểu dữ liệu hơn (Array hoặc Range tùy ý)
 
Upvote 0
Thật ra, nếu làm bài này với mục đích để xài thì nên:
- Chuyển Sub Filter thành Function ---> Kết quả của Function trả về sẽ là 1 Array (1 chiều hay 2 chiều tự ta quyết định)
- Đối số của Function sẽ là 1 Array thật sự chứ không nên là 1 chuổi chứa địa chỉ cell ---> Mục đích để có thể truyền vào nhiều kiểu dữ liệu hơn (Array hoặc Range tùy ý)

Vâng, cám ơn chú.
Tại vì mục đích của bạn Dauthivan muốn biết được cách truyền tham số nên kyo chỉ dựa trên sườn có sẵn của bạn ấy thôi chứ không có thay đổi cấu trúc có sẵn của bạn ấy chứ nếu mà làm luôn thì cũng không cần phải tách ra 2 Sub như vậy.

Với lại, kyo cũng muốn hỏi chú cách làm với array cũng dựa trên cấu trúc đó. Tức là như kyo là truyền biến string, chú giúp kyo truyền qua ParamArray được không?
 
Upvote 0
Với lại, kyo cũng muốn hỏi chú cách làm với array cũng dựa trên cấu trúc đó. Tức là như kyo là truyền biến string, chú giúp kyo truyền qua ParamArray được không?
Bài đó làm rồi mà kyo, chẳng hạn là bài này:
http://www.giaiphapexcel.com/forum/showthread.php?61993-Nhờ-lọc-dữ-liệu-duy-nhất-từ-2-cột-ra-1-cột-khác-bằng-VBA&p=381048#post381048
Một ứng dụng của nó để nạp List cho ComboBox như file đính kèm
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Em làm theo For...Next mà không được, xin sửa lại đúng giúp em với ah.

PHP:
Sub Filter(ByVal sArray As Range)
    Dim SubArr, Arr, i As Long, j As Long, kk As Long
    SubArr = sArray
    Set Dic = CreateObject("Scripting.Dictionary")
    ReDim Arr(1 To UBound(SubArr, 1) * UBound(SubArr, 2), 1 To 1)
    For j = 1 To UBound(SubArr, 1)
        For j = 1 To UBound(Arr, 2)
            If SubArr(i, j) <> "" Then
                Tmp = SubArr(i, j)
                If Not Dic.Exists(Tmp) Then
                    kk = kk + 1
                    Dic.Add Tmp, kk
                    Arr(kk, 1) = Tmp
                End If
            End If
        Next j
    Next i
    Sheets("sheet2").[A1].Resize(kk).Value = Arr
End Sub

PHP:
Sub Main()
    Dim sArray As String
    sArray = Range("A1:B1000")
    Filter sArray
End Sub
 
Upvote 0
Em làm theo For...Next mà không được, xin sửa lại đúng giúp em với ah.

PHP:
Sub Filter(ByVal sArray As Range)
    Dim SubArr, Arr, i As Long, j As Long, kk As Long
    SubArr = sArray
    Set Dic = CreateObject("Scripting.Dictionary")
    ReDim Arr(1 To UBound(SubArr, 1) * UBound(SubArr, 2), 1 To 1)
    For j = 1 To UBound(SubArr, 1)
        For j = 1 To UBound(Arr, 2)
            If SubArr(i, j) <> "" Then
                Tmp = SubArr(i, j)
                If Not Dic.Exists(Tmp) Then
                    kk = kk + 1
                    Dic.Add Tmp, kk
                    Arr(kk, 1) = Tmp
                End If
            End If
        Next j
    Next i
    Sheets("sheet2").[A1].Resize(kk).Value = Arr
End Sub

PHP:
Sub Main()
    Dim sArray As String
    sArray = Range("A1:B1000")
    Filter sArray
End Sub
Sai mấy chổ:
For j = 1 To UBound(SubArr, 1) --> Lý ra phải là For i = 1 To UBound(SubArr, 1)
For j = 1 To UBound(Arr, 2) ---> Lý ra phải là For j = 1 To UBound(SubArr, 2)
Dim sArray As String ---> Ở trên khai báo sArray As Range sao ở dưới lại là String?
Ngoài ra, Sub Filter là sub viết ở mức tổng quát, vậy không nên có dòng Sheets("sheet2").[A1].Resize(kk).Value = Arr ---> Sao bạn biết chắc sẽ trích lọc kết quả vào sheet2, cell A1?
Vậy, sửa lại toàn bộ sẽ như vầy:
PHP:
Sub Filter(ByVal SrcRng As Range, ByVal Target As Range)
  Dim tmpArr, Arr(), lR As Long, lC As Long, kk As Long, Dic As Object, tmp
  On Error Resume Next
  tmpArr = SrcRng.Value
  Set Dic = CreateObject("Scripting.Dictionary")
  ReDim Arr(1 To UBound(tmpArr, 1) * UBound(tmpArr, 2), 1 To 1)
  For lR = 1 To UBound(tmpArr, 1)
    For lC = 1 To UBound(tmpArr, 2)
      If tmpArr(lR, lC) <> "" Then
        tmp = tmpArr(lR, lC)
        If Not Dic.Exists(tmp) Then
          kk = kk + 1
          Dic.Add tmp, kk
          Arr(kk, 1) = tmp
        End If
      End If
    Next
  Next
  If kk Then Target.Resize(kk).Value = Arr
End Sub
PHP:
Sub Main()
  Dim SrcRng As Range, Target As Range
  Set SrcRng = Sheet1.Range("A1:B1000")
  On Error Resume Next
  Set Target = Sheet2.Range("A1")
  Filter SrcRng, Target
End Sub
Lưu ý: Nếu viết như vầy thì chỉ hoạt động với nguồn dữ liệu là Range thôi nha
 
Upvote 0
Lưu ý: Nếu viết như vầy thì chỉ hoạt động với nguồn dữ liệu là Range thôi nha

Em vẫn chưa hiểu hết ý thày ở trên, rất mong thày giúp em bẫy các lỗi, để nó được áp dụng trong trường hợp tổng quát.
 
Upvote 0
Em vẫn chưa hiểu hết ý thày ở trên, rất mong thày giúp em bẫy các lỗi, để nó được áp dụng trong trường hợp tổng quát.

Thì do khai báo SrcRng as Range, đương nhiên dữ liệu đầu vào sẽ là Range thôi
Muốn tổng quát cho Range và Array, khai báo lại biến này... Nhưng nói trước là không dễ ăn, vì đã là Array thì bạn đâu biết trước nó 1 chiều hay 2 chiều... Vậy bằng cách nào để ReDim Arr cho đúng kích thước? (ít nhất là không bị thiếu)
 
Upvote 0
Em vẫn chưa hiểu hết ý thày ở trên, rất mong thày giúp em bẫy các lỗi, để nó được áp dụng trong trường hợp tổng quát.
Thôi, làm cho bạn luôn đây!
1> Đầu tiên phải viết 1 hàm hổ trợ để tính số phần tử trong Array
PHP:
Function Elements(ByVal sArray) As Long
  Dim lEs As Long, lDim As Long, tmpArr
  On Error Resume Next
  tmpArr = sArray
  lEs = 1
  If IsArray(tmpArr) Then
    Do While Err.Number = 0
      lDim = lDim + 1
      lEs = lEs * (UBound(tmpArr, lDim) - LBound(tmpArr, lDim) + 1)
    Loop
    Elements = lEs
  End If
End Function
2> Sub Filter sửa lại:
PHP:
Sub Filter(ByVal sArray, ByVal Target As Range)
  Dim tmpArr, Arr(), lEs As Long, Item, lR As Long, Dic As Object, tmp
  On Error Resume Next
  tmpArr = sArray
  If TypeName(tmpArr) <> "Variant()" Then
    Target(1, 1).Value = tmpArr
  Else
    lEs = Elements(tmpArr)
    Set Dic = CreateObject("Scripting.Dictionary")
    ReDim Arr(1 To lEs, 1 To 1)
    For Each Item In tmpArr
      If CStr(Item) <> "" Then
        tmp = CStr(Item)
        If Not Dic.Exists(tmp) Then
          lR = lR + 1
          Dic.Add tmp, lR
          Arr(lR, 1) = Item
        End If
      End If
    Next
    If lR Then Target.Resize(lR).Value = Arr
  End If
End Sub
3> Code chính:
PHP:
Sub Main()
  Dim sArray, Target As Range
  sArray = Sheet1.Range("A1:B1000").Value
  On Error Resume Next
  Set Target = Sheet2.Range("A1")
  Filter sArray, Target
End Sub
Code trên chạy được với dữ liệu nguồn là Range, mảng 1 chiều hoặc nhiều chiều
Tự bạn nghiên cứu code trên nhé! Chỉ lưu ý điểm này:
- Tôi đặt tmp = CStr(Item) ---> Xét tmp theo dạng chuổi
- Add tmp vào Dic bằng code Dic.Add tmp, lR
- Nhưng tôi không nạp tmp mà lại nạp Item vào Arr (Arr(lR, 1) = Item)
Lý do vì có trường hợp thế này:
- Cell A1 ta có số 1 định dạng Text
- Cell A2 ta có số 1 định dạng Number
Trường hợp này thì code trên sẽ xem 2 "thằng" này là 1 và sẽ Add vào Dic 1 cái thôi
---------------
Còn vụ If TypeName(tmpArr) <> "Variant()" Then hình như có lần tôi đã nói rồi
 
Upvote 0
Em chưa hiểu tại sao lại phải tính số phần tử Array hả thày?

PHP:
lEs = Elements(tmpArr)
....
ReDim Arr(1 To lEs, 1 To 1)
Mục đích khi tính số phần tử Array là để Redim cho chính xác, nhất là tránh trường hợp Redim thiếu
 
Upvote 0
Trình độ em còn CÒI quá, chưa biết nhiều thứ, xin thày cho em hỏi

PHP:
If TypeName(tmpArr) <> "Variant()" Then
    Target(1, 1).Value = tmpArr
nó có ý nghĩa gì?

-----------------
Em thắc mắc là sArray = Sheet1.Range("A1:B1000").Value có nghĩa sArray nó là mảng rồi thì cần gì phải làm động tác thêm biến tmpArr (= sArray) làm gì ah.

Hay là đoạn bôi đỏ theo cách hiểu của em như vầy là sai?
 
Lần chỉnh sửa cuối:
Upvote 0
Trình độ em còn CÒI quá, chưa biết nhiều thứ, xin thày cho em hỏi

PHP:
If TypeName(tmpArr) <> "Variant()" Then
    Target(1, 1).Value = tmpArr
nó có ý nghĩa gì?

-----------------
Em thắc mắc là sArray = Sheet1.Range("A1:B1000").Value có nghĩa sArray nó là mảng rồi thì cần gì phải làm động tác thêm biến tmpArr (= sArray) làm gì ah.

Hay là đoạn bôi đỏ theo cách hiểu của em như vầy là sai?
- Câu 1: Lở sArray chỉ là 1 cell thì sao? Dẫn đến tmpArr không phải là Array (mà là 1 giá trị đơn)
- Câu 2: Lở như khi áp dụng người ta ghi Set sArray = Sheet1.Range("A1:B1000") thì sao? Tức người ta truyền Range vào chứ không phải Array ----> Phòng ngừa: Nếu người ta truyền vào là 1 Array thì động tác tmpArr = sArray là dư nhưng nếu người ta truyền vào 1 Range, thì động tác tmpArr = sArray nhằm mục đích chuyển sArray sang Array thật sự
-----------------------
Cái này tôi đã nói mấy lần rồi nhưng hình như bạn không để ý
 
Upvote 0
Vì bận quá, không chịu tìm trên diễn đàn nên không biết là thày đã nói về vấn đề trên rồi.

Em vẫn chưa hiểu kỹ sự khác nhau giữa cách dùng Funtion và Sub trong bài này, cụ thể: Hai Code trên dùng Function nó có ưu điểm gì so với dùng Sub ah?
 
Upvote 0
Vì bận quá, không chịu tìm trên diễn đàn nên không biết là thày đã nói về vấn đề trên rồi.

Em vẫn chưa hiểu kỹ sự khác nhau giữa cách dùng Funtion và Sub trong bài này, cụ thể: Hai Code trên dùng Function nó có ưu điểm gì so với dùng Sub ah?
- Sub dùng để thực thi công việc nào đó (ví dụ tô màu, kẻ khung, gán 1 giá trị vào 1 vùng...) ---> Ví thế Sub có tham số sẽ có dạng
Mã:
Sub TENSUB(Đối số)
'' Thực thi công việc
End Sub
- Function sẽ trả về 1 kết quả sau quá trình tính toán ---> Ví thế bạn sẽ luôn thấy Function có dạng thế này
Mã:
Function TENHAM(Đối số) [COLOR=#ff0000]As Kiểu dữ liệu[/COLOR]
'' Tính toán gì đó
[COLOR=#ff0000]TENHAM = Kết quả tính toán[/COLOR]
End Function
Chổ tô đỏ là cái khác nhau giữa Sub và Function đấy
Ngoài ra, với 1 Function thì bạn có thể áp dụng trực tiếp trên bảng tính bằng cách gõ vào 1 cell nào đó (như hàm có sẵn của Excel) còn Sub thì không thể dùng cách này
------------------------------
Không thể nói cái nào có ưu điêm hơn, tùy theo yêu cầu mà chọn lựa hợp lý thôi
 
Lần chỉnh sửa cuối:
Upvote 0
Chán quá, thực hành lại từ bài đơn giản trước, bắt chước giống thày Ndu kiểm tra lại từng dòng rồi mà không hiểu sao Code chạy lại chẳng ra cái gì

PHP:
Sub Filter(SrcRng As Range)
    Dim Arr(), TmpArr, lR As Long, lC As Long, kk As Long, Dic As Object
    TmpArr = SrcRng
    ReDim Arr(1 To UBound(TmpArr, 1) * UBound(TmpArr, 2), 1 To 1)
    Set Dic = CreateObject("Scripting.Dictionary")
    For lC = 1 To UBound(TmpArr, 2)
        For lR = 1 To UBound(TmpArr, 1)
            If TmpArr(lR, lC) <> "" Then
                Tmp = TmpArr(lR, lC)
                If Not Dic.Exists(Tmp) Then
                    kk = kk + 1
                    Dic.Add Tmp, kk
                    Arr(kk, 1) = Tmp
                End If
            End If
        Next lR
    Next lC
End Sub

PHP:
Sub Main()
    Dim SrcRng As Range
    Set SrcRng = Sheet1.Range("A1:B1000")
    Filter SrcRng
    If kk Then Sheet2.[A1].Resize(kk).Value = Arr
End Sub
 
Upvote 0
Chán quá, thực hành lại từ bài đơn giản trước, bắt chước giống thày Ndu kiểm tra lại từng dòng rồi mà không hiểu sao Code chạy lại chẳng ra cái gì

PHP:
Sub Filter(SrcRng As Range)
    Dim Arr(), TmpArr, lR As Long, lC As Long, kk As Long, Dic As Object
    TmpArr = SrcRng
    ReDim Arr(1 To UBound(TmpArr, 1) * UBound(TmpArr, 2), 1 To 1)
    Set Dic = CreateObject("Scripting.Dictionary")
    For lC = 1 To UBound(TmpArr, 2)
        For lR = 1 To UBound(TmpArr, 1)
            If TmpArr(lR, lC) <> "" Then
                Tmp = TmpArr(lR, lC)
                If Not Dic.Exists(Tmp) Then
                    kk = kk + 1
                    Dic.Add Tmp, kk
                    Arr(kk, 1) = Tmp
                End If
            End If
        Next lR
    Next lC
End Sub

PHP:
Sub Main()
    Dim SrcRng As Range
    Set SrcRng = Sheet1.Range("A1:B1000")
    Filter SrcRng
    If kk Then Sheet2.[A1].Resize(kk).Value = Arr
End Sub
Như đã nói: Sub là để thực thi công việc nào đó... Vậy bạn nhìn lại Sub Filter của bạn xem, nó có thực thi 1 hành động cụ thể nào đâu? Sau khi Add Dic, nạp Arrary.. vân vân... thì chẳng làm cái gì cả ---> Lý ra, cuối cùng phải gán mảng kết quả vào sheet chứ
Còn Sub Main lại có câu If kk Then Sheet2.[A1].Resize(kk).Value = Arr ---> Chẳng có ý nghĩa gì cả! kk là cái gì? Arr là cái gì?
--------------
Mà bài này tôi đã viết rất rõ rồi còn gì:
PHP:
Sub Filter(ByVal SrcRng As Range, ByVal Target As Range)
  Dim Arr(), tmpArr, lR As Long, lC As Long, kk As Long, Dic As Object, tmp
  tmpArr = SrcRng
  ReDim Arr(1 To UBound(tmpArr, 1) * UBound(tmpArr, 2), 1 To 1)
  Set Dic = CreateObject("Scripting.Dictionary")
  For lC = 1 To UBound(tmpArr, 2)
    For lR = 1 To UBound(tmpArr, 1)
      If tmpArr(lR, lC) <> "" Then
        tmp = tmpArr(lR, lC)
        If Not Dic.Exists(tmp) Then
          kk = kk + 1
          Dic.Add tmp, kk
          Arr(kk, 1) = tmp
        End If
       End If
     Next lR
  Next lC
  If kk Then Target.Resize(kk).Value = Arr
End Sub
PHP:
Sub Main()
  Dim SrcRng As Range, Target As Range
  Set SrcRng = Sheet1.Range("A1:B1000")
  Set Target = Sheet2.Range("A1")
  Filter SrcRng, Target
End Sub
----------------
Hoặc nếu giữ nguyên code của bạn thì phải cho biến Arr() và kk lên thành Public
PHP:
Public Arr(), kk As Long
Sub Filter(SrcRng As Range)
  Dim TmpArr, lR As Long, lC As Long, Dic As Object, tmp
  TmpArr = SrcRng
  kk = 0
  ReDim Arr(1 To UBound(TmpArr, 1) * UBound(TmpArr, 2), 1 To 1)
  Set Dic = CreateObject("Scripting.Dictionary")
  For lC = 1 To UBound(TmpArr, 2)
    For lR = 1 To UBound(TmpArr, 1)
      If TmpArr(lR, lC) <> "" Then
        tmp = TmpArr(lR, lC)
        If Not Dic.Exists(tmp) Then
          kk = kk + 1
          Dic.Add tmp, kk
          Arr(kk, 1) = tmp
        End If
      End If
    Next lR
  Next lC
End Sub
PHP:
Sub Main()
  Dim SrcRng As Range
  Set SrcRng = Sheet1.Range("A1:B1000")
  Filter SrcRng
  If kk Then Sheet2.[A1].Resize(kk).Value = Arr
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Em tưởng câu này
PHP:
Arr(kk, 1) = tmp
cũng là thực thi hành động nào đó rồi?


PHP:
Filter SrcRng
Bởi em nghĩ sau khi có câu lệnh trên, 2 Sub liên kết với nhau rồi thì thì Sub Main nó tự biết kk là cái gì từ Sub trên chứ ah?
PHP:
    If kk Then Sheet2.[A1].Resize(kk).Value = Arr
 
Upvote 0
Em tưởng câu này
PHP:
Arr(kk, 1) = tmp
cũng là thực thi hành động nào đó rồi?


PHP:
Filter SrcRng
Bởi em nghĩ sau khi có câu lệnh trên, 2 Sub liên kết với nhau rồi thì thì Sub Main nó tự biết kk là cái gì từ Sub trên chứ ah?
PHP:
    If kk Then Sheet2.[A1].Resize(kk).Value = Arr
Cấu lệnh Arr(kk, 1) = tmp gì gì đó chỉ có thể xem là "thực thi trên giấy"... Muốn kết quả cụ thể phải có hành động cụ thể (bằng động tác gán xuống sheet chẳng hạn)
Sub của bạn phải làm được 1 trong 2 việc sau đây:
- Thực thi 1 hành động cụ thể nào đó (đoạn code thứ nhất của tôi là gán xuống sheet bằng lệnh Target.Resize(kk).Value = Arr)
- Sinh ra 1 sản phẩm nào đó (đoạn code thứ 2 của tôi, sản phẩm sinh ra chính là biến Arr được khai báo dạng Public)
Nói thêm:
- Khi bạn để biến Arr bên trong Sub Filter thì sẽ không thể dùng biến này trong 1 sub khác được, tức nó sẽ chẳng biết Arr, kk là cái giống gì cả...
- Muốn lấy kết quả từ biến Arr, phải khai báo nó dạng Public (để sau khi chạy code, biến này còn lưu giữ được giá trị cuối cùng)
 
Upvote 0
Vâng, nhận thức của em ngây thơ quá, tưởng Sub trên làm công việc đến đâu thì làm, phần công việc còn lại Sub dưới "làm nốt". Tức là tựa như quyển sách vậy, 2 Sub trên như là 2 chương còn việc phân chia nội dung cho vào Chương nào cũng được, miễn sao tổng không đổi.

Nghe thày nói em đã hình dung ra được 1 chút, em xin hỏi tác dụng của Public là gì thế (em chỉ biết nghĩa Tiếng Việt của nó là công khai thôi)?
 
Upvote 0
Nghe thày nói em đã hình dung ra được 1 chút, em xin hỏi tác dụng của Public là gì thế (em chỉ biết nghĩa Tiếng Việt của nó là công khai thôi)?

Nguyên văn bởi ndu:
Muốn lấy kết quả từ biến Arr, phải khai báo nó dạng Public (để sau khi chạy code, biến này còn lưu giữ được giá trị cuối cùng)

Ngược lại:
Nếu không khai báo dạng Public, ...
 
Upvote 0
Vâng, nhận thức của em ngây thơ quá, tưởng Sub trên làm công việc đến đâu thì làm, phần công việc còn lại Sub dưới "làm nốt". Tức là tựa như quyển sách vậy, 2 Sub trên như là 2 chương còn việc phân chia nội dung cho vào Chương nào cũng được, miễn sao tổng không đổi.

Nghe thày nói em đã hình dung ra được 1 chút, em xin hỏi tác dụng của Public là gì thế (em chỉ biết nghĩa Tiếng Việt của nó là công khai thôi)?
Nhớ có lần tôi đã hình dung thế này:
- Public = công cộng ---> Vậy thì ai xài cũng được (tức "đứng" từ sub khác hoặc 1 module khác cũng có thể dùng được cái Public do thằng ở sub này hoặc module xây dựng nên)
- Private = riêng tư ---> Mạnh ai nấy xài
Vậy thôi
---------
Cũng giống như:
- Arr là cái kho gạo công cộng (Public), thằng Sub Filter sau khi chạy xong, sẽ "đổ gạo" vào kho công cộng này nên thằng Sub Main sẽ đến lấy được gạo về nấu
- Nếu Arr là biến nằm trong sub Filter, tức là gạo của riêng nó ---> Thằng Sub Main đến lấy, nó đánh bỏ xừ...
Ẹc... Ẹc...
 
Lần chỉnh sửa cuối:
Upvote 0
Nhớ có lần tôi đã hình dung thế này:
- Public = công cộng ---> Vậy thì ai xài cũng được (tức "đứng" từ sub khác cũng có thể dùng được cái Public do thằng khác xây dựng nên)
- Private = riêng tư ---> Của ai nấy xài
Vậy thôi

Nếu nhớ lại nguyên văn thì lúc đó ndu dùng thí dụ cái toilet cơ.
Hic, spam cái. Công nhận vài hôm nay ndu quá kiên nhẫn.
Chứ có một câu ngắn như vậy, 1 là không đọc, 2 là đọc không hiểu, lão chết tiệt chịu, bó tay.
 
Upvote 0
Hic, spam cái. Công nhận vài hôm nay ndu quá kiên nhẫn.
.
Em cũng.. spam tiếp
Em chỉ có thể kiên nhẫn với những ai chịu học... dù chậm tiêu cũng không sao, miễn là cố mà "nhai" cho kỹ
Và em sẽ rất mất kiên nhẫn với những câu hỏi.. tào lao
Ẹc... Ẹc...
(nhớ ngày xưa sư phụ cũng từng mắng em rằng: "Đã viết ở bài... sao chẳng chịu xem?)
 
Upvote 0
Qua đây em nhận ra thế này Sub phải tạo ra được:
- Hoặc một sản phẩm hoàn chỉnh (thể hiện ra ngoài màn hình),
- Hoặc một sản phẩm chưa phải cuối cùng nhưng phải để cho thằng khác sài chứ không được cất đi (tức phải có thêm Public)

-------------
Em học tuy có chậm hiểu, nhưng tiếp thu bài rất nghiêm túc với mong mỏi học được được kiến thức mới, kính mong các thày giúp đỡ
 
Lần chỉnh sửa cuối:
Upvote 0
- Hoặc một sản phẩm chưa phải cuối cùng nhưng phải để cho thằng khác sài chứ không được cất đi (tức phải có thêm Public)
Nói thêm về hình thức tạo sản phẩm này: Thông thường người ta sẽ viết thành 1 Function thay cho Sub ---> Vì bản thân Function sẽ tạo ra sản phẩm và ta dùng được ngay (trong khi nếu viết thành Sub, buộc phải khai báo biến kết quả thành dạng Public ---> Rắc rối)
 
Upvote 0
Em đang loay hoay thay đổi cách dùng để nhớ lại lý thuyết, em dùng Keys của Dic thì phải sửa cái này thế nào ah
PHP:
Sub Filter(ByVal SrcRng As Range, ByVal Target As Range)
  Dim Arr(), tmpArr, lR As Long, lC As Long, Dic As Object, tmp
  tmpArr = SrcRng
  ReDim Arr(1 To UBound(tmpArr, 1) * UBound(tmpArr, 2), 1 To 1)
  Set Dic = CreateObject("Scripting.Dictionary")
  For lC = 1 To UBound(tmpArr, 2)
    For lR = 1 To UBound(tmpArr, 1)
      If tmpArr(lR, lC) <> "" Then
        tmp = tmpArr(lR, lC)
        If Not Dic.Exists(tmp) Then
          Dic.Add tmp, ""
          Arr = Dic.Keys
        End If
       End If
     Next lR
  Next lC
  If kk Then Target.Resize(, kk).Value = Arr
End Sub

PHP:
Sub Main()
  Dim SrcRng As Range, Target As Range
  Set SrcRng = Sheet1.Range("A1:B1000")
  Set Target = Sheet2.Range("A1")
  Filter SrcRng, Target
End Sub

Hôm nay không nhờ các thày bảo thì làm sao biết được thế nào là Sub, thế nào là Function; thế mà trước cứ ngỡ là biết....mơ quá ...Híc
 
Lần chỉnh sửa cuối:
Upvote 0
Nói thêm về hình thức tạo sản phẩm này: Thông thường người ta sẽ viết thành 1 Function thay cho Sub ---> Vì bản thân Function sẽ tạo ra sản phẩm và ta dùng được ngay (trong khi nếu viết thành Sub, buộc phải khai báo biến kết quả thành dạng Public ---> Rắc rối)

Đúng vậy nhưng Không chỉ như vậy. Biến Public nếu khéo dùng thì còn nhiều cái hay hơn. Thí dụ tôi có 1 Sub:

PHP:
Public abc As Long
Sub ExecutiveA()
Select Case abc
    Case 1
        Code thực thi 1
    Case 2
        Code thực thi 2
    ...

    Case 10
        Code thực thi 10
End Select
End Sub

Code này gán cho sự kiện On Open của 1 form ABC. Rồi để đó.

Trên 1 form khác có 1 số Command Button, nhấn vào đâu cũng đều mở Form ABC nói trên.

Nhưng Form ABC mỗi lần mở có công dụng khác nhau, title khác nhau, dữ liệu nguồn cho listbox, combobox khác nhau... tất cả là căn cứ vào biến Public abc.

Vậy mỗi khi nhấn nút của form 1, tuy theo ý muốn chạy form ABC thế nào, ta gán những giá trị khác nhau cho biến abc.

PHP:
Sub CmbBtn1_Click()
  abc = 1
  ABC.Show
End Sub

Nghĩa là thay vì phải tạo 10 form tương tự nhau, na ná nhau, ta chỉ cần 1 form.
 
Lần chỉnh sửa cuối:
Upvote 0
Em đang loay hoay thay đổi cách dùng để nhớ lại lý thuyết, em dùng Keys của Dic thì phải sửa cái này thế nào ah
PHP:
Sub Filter(ByVal SrcRng As Range, ByVal Target As Range)
  Dim Arr(), tmpArr, lR As Long, lC As Long, Dic As Object, tmp
  tmpArr = SrcRng
  ReDim Arr(1 To UBound(tmpArr, 1) * UBound(tmpArr, 2), 1 To 1)
  Set Dic = CreateObject("Scripting.Dictionary")
  For lC = 1 To UBound(tmpArr, 2)
    For lR = 1 To UBound(tmpArr, 1)
      If tmpArr(lR, lC) <> "" Then
        tmp = tmpArr(lR, lC)
        If Not Dic.Exists(tmp) Then
          Dic.Add tmp, ""
          Arr = Dic.Keys
        End If
       End If
     Next lR
  Next lC
  If kk Then Target.Resize(, kk).Value = Arr
End Sub

PHP:
Sub Main()
  Dim SrcRng As Range, Target As Range
  Set SrcRng = Sheet1.Range("A1:B1000")
  Set Target = Sheet2.Range("A1")
  Filter SrcRng, Target
End Sub

Hôm nay không nhờ các thày bảo thì làm sao biết được thế nào là Sub, thế nào là Function; thế mà trước cứ ngỡ là biết....mơ quá ...Híc
- Arr = Dic.Keys sao đặt trong vòng lập? Hết vòng lập mới lấy chứ
- If kk Then ---> kk là cái gì?
Sửa lại:
Mã:
Sub Filter(ByVal SrcRng As Range, ByVal Target As Range)
  Dim Arr(), tmpArr, lR As Long, lC As Long, Dic As Object, tmp
  tmpArr = SrcRng
  ReDim Arr(1 To UBound(tmpArr, 1) * UBound(tmpArr, 2), 1 To 1)
  Set Dic = CreateObject("Scripting.Dictionary")
  For lC = 1 To UBound(tmpArr, 2)
    For lR = 1 To UBound(tmpArr, 1)
      If tmpArr(lR, lC) <> "" Then
        tmp = tmpArr(lR, lC)
        If Not Dic.Exists(tmp) Then
          Dic.Add tmp, ""
        End If
       End If
     Next lR
  Next lC
  [COLOR=#ff0000]If Dic.Count Then
    Arr = Dic.Keys
    Target.Resize(, Dic.Count).Value = Arr
  End If[/COLOR]
End Sub
Cái Dic.Count này là thay cho kk ở những code lần trước đấy ---> Nó là số lượng phần tử có trong Dic ---> Dic.Count > 0 thì mới gán xuống sheet
 
Lần chỉnh sửa cuối:
Upvote 0

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

Back
Top Bottom