Chuyên đề giải đáp những thắc mắc về code VBA

Liên hệ QC

maytinhvp01

Thành viên thường trực
Tham gia
27/7/13
Bài viết
390
Được thích
179
Mình muốn nhờ giải thich câu lệnh " If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c) "
trong ví du:
Public Function LonNhat(Ran As Range)
Dim max As Double, v As Integer, d As Integer, c As Integer
max = Ran.Cells(1, 1)
For d = 1 To Ran.Rows.Count
For c = 1 To Ran.Columns.Count
If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c)
Next c
Next d
v = Tim(max, Ran)
LonNhat = max
End Function
-------------------------------------------------------
[INFO1]Thông báo:
Vì topic này:
http://www.giaiphapexcel.com/forum/...ải-thích-các-code-đề-nghị-các-bạn-gửi-vào-đây
đã quá dài nên BQT đóng lại.
Nay tôi mở topic mới với cùng chủ đề: GIẢI THÍCH NHỮNG THẮC MẮC VỀ CODE
Các bạn nếu có nhu cầu giải thích code, vui lòng post tại đây nhé
NDU96081631

[/INFO1]
 
Chỉnh sửa lần cuối bởi điều hành viên:
mọi người cho mình hỏi:
cái đoạn code TextJoin ở dưới mình xài, khi mình chọn vùng tham chiếu từ A1:E1 (ví dụ) thì code chạy được.
nhưng khi mình bấm F9 cái vùng (A1:E1) nó thành {1,2,3,4,5} thì hàm ko chạy được báo lỗi #Value!
là thế nào mọi người.
Theo mình biết thì 2 cái đó cũng là mảng như nhau mà???

Mã:
Public Function TEXTJOIN(Delimiter As String, Ignore_Empty As Boolean, ParamArray Text1() As Variant) As String
Dim RangeArea As Variant
Dim Cell As Range
For Each RangeArea In Text1
If TypeName(RangeArea) = "Range" Then
For Each Cell In RangeArea
If Len(Cell.Value) <> 0 Or Ignore_Empty = False Then
TEXTJOIN = TEXTJOIN & Delimiter & Cell.Value
End If
Next Cell
Else
If Len(RangeArea) <> 0 Or Ignore_Empty = False Then
TEXTJOIN = TEXTJOIN & Delimiter & RangeArea
End If
End If
Next
TEXTJOIN = Mid(TEXTJOIN, Len(Delimiter) + 1)
End Function
 
Upvote 0
Sao không hỏi ngừoi viết code?
A1:E1 là mảng cell
{1,2,3,4,5} là mảng số.
Cell là object có thuộc tính Value. Số là kiểu cơ bản, không có thuộc tính.
If Len(Cell.Value) <> 0 Or Ignore_Empty = False Then
TEXTJOIN = TEXTJOIN & Delimiter & Cell.Value
End If
 
Upvote 0
Nếu tôi hiểu được mô tả của bạn thì lúc đó dữ liệu truyền vào là một mảng có 5 giá trị.
Tức RangeArea nó là mảng. Là mảng nên Len(RangeArea) sẽ có lỗi.
 
Upvote 0
Sao không hỏi ngừoi viết code?
A1:E1 là mảng cell
{1,2,3,4,5} là mảng số.
Cell là object có thuộc tính Value. Số là kiểu cơ bản, không có thuộc tính.
If Len(Cell.Value) <> 0 Or Ignore_Empty = False Then
TEXTJOIN = TEXTJOIN & Delimiter & Cell.Value
End If
{1,2,3,4,5} là mình ví dụ thôi chứ ko phải mảng số @@
Lụm lặt ở trên mạng thì biết ai viết đâu mà hỏi bạn.

Nếu tôi hiểu được mô tả của bạn thì lúc đó dữ liệu truyền vào là một mảng có 5 giá trị.
Tức RangeArea nó là mảng. Là mảng nên Len(RangeArea) sẽ có lỗi.
Đúng rồi ý mình là vậy đó bạn... sửa làm sao bạn nhỉ
 
Upvote 0
Ờ nhỉ, tôi khong để ý đến cái chỗ
If TypeName(RangeArea) = "Range" Then
 
Upvote 0
Còn code này mình tìm thấy ở trên forum cũng tương tự code ở trên.
Nó thì ko bị cái lỗi của mình nói là A1:E1 hay {1,2,3,4,5,6} đều xài được.
nhưng bị 1 vấn đề với ô trống là khi nối ô với điều kiện "-" ví dụ như {1,"","",4,"",6} thì nó thành: 1---4--6 (thay vì = 1-4-6)


Mã:
Function JoinText(ByVal Delimiter As String, ParamArray Arrays()) As String
  Dim aTmp, arrDes(), Item, tmp As String
  Dim idx As Long, n As Long
  'On Error Resume Next
  For idx = LBound(Arrays) To UBound(Arrays)
    aTmp = Arrays(idx)
    If Not IsArray(aTmp) Then aTmp = Array(aTmp)
    For Each Item In aTmp
      If TypeName(Item) <> "Error" Then
        tmp = CStr(Item)
        n = n + 1
        ReDim Preserve arrDes(1 To n)
        arrDes(n) = tmp
      End If
    Next
  Next
  If n Then JoinText = Join(arrDes, Delimiter)
End Function
 
Upvote 0
...
Đúng rồi ý mình là vậy đó bạn... sửa làm sao bạn nhỉ
Đúng là đáng sợ các hàm viết đại trà mà không có chú thích chỉ dẫn.
Ngừoi viết hàm muốn hàm có thể nối tất cả các loại dữ liệu, bất kỳ vụn vặt như thế nào.
Trước khi sửa, bạn phải xác định lại mình muốn gì?
{1,2,3,4,5} là một mảng, mảng này được truyền vào hàm như một tham chính thức, tức là một phần tử của cái Paramarray; chứ không phải là cả cái Paramarray.
Trong trường hợp này, bạn muốn nối như thế nào? Xem nó như là một range?
 
Upvote 0
Còn code này mình tìm thấy ở trên forum cũng tương tự code ở trên.
Nó thì ko bị cái lỗi của mình nói là A1:E1 hay {1,2,3,4,5,6} đều xài được.
nhưng bị 1 vấn đề với ô trống là khi nối ô với điều kiện "-" ví dụ như {1,"","",4,"",6} thì nó thành: 1---4--6 (thay vì = 1-4-6)


Mã:
Function JoinText(ByVal Delimiter As String, ParamArray Arrays()) As String
  Dim aTmp, arrDes(), Item, tmp As String
  Dim idx As Long, n As Long
  'On Error Resume Next
  For idx = LBound(Arrays) To UBound(Arrays)
    aTmp = Arrays(idx)
    If Not IsArray(aTmp) Then aTmp = Array(aTmp)
    For Each Item In aTmp
      If TypeName(Item) <> "Error" Then
        tmp = CStr(Item)
        n = n + 1
        ReDim Preserve arrDes(1 To n)
        arrDes(n) = tmp
      End If
    Next
  Next
  If n Then JoinText = Join(arrDes, Delimiter)
End Function
Uổi. Đó là lỗi do người truyền dữ liệu chứ. Chứ bản thân hàm thì đưa dữ liệu kiiểu gì thì mần kiểu ấy thui
 
Upvote 0
Đúng là đáng sơh các hàm viết đại trà mà không có chú thích chỉ dẫn.
Ngừoi viết hàm muốn hàm có thể nối tất cả các loại dữ liệu, bất kỳ vụn vặt như thế nào.
Trước khi sửa, bạn phải xác định lại mình muốn gì?
{1,2,3,4,5} là một mảng, mảng này được truyền vào hàm như một tham chính thức, tức là một phần tử của cái Paramarray; chứ không phải là cả cái Paramarray.
Trong trường hợp này, bạn muốn nối như thế nào? Xem nó như là một range?

mình cũng còn lờ mờ chưa rành lắm nên mấy cái khái niêm cũng ko rõ ràng lắm.
nhưng đại khái là ý mình là xem nó là 1 array ý.
khi kéo 1 vùng nào đó có trên 2 ô ngang thì nó tạo ra 1 mảng {x,y} còn 2 ô dọc thì {x;y} còn có ngang có dọc thì {x,y;x,y}
như khi mình kéo 1 vùng nào đó bất kỳ rồi nhấn F9 nó ra { } ý

như cái code dưới này nó làm được á bạn
Mã:
Function JoinText(ByVal Delimiter As String, ParamArray Arrays()) As String
  Dim aTmp, arrDes(), Item, tmp As String
  Dim idx As Long, n As Long
  'On Error Resume Next
  For idx = LBound(Arrays) To UBound(Arrays)
    aTmp = Arrays(idx)
    If Not IsArray(aTmp) Then aTmp = Array(aTmp)
    For Each Item In aTmp
      If TypeName(Item) <> "Error" Then
        tmp = CStr(Item)
        n = n + 1
        ReDim Preserve arrDes(1 To n)
        arrDes(n) = tmp
      End If
    Next
  Next
  If n Then JoinText = Join(arrDes, Delimiter)
End Function
 
Upvote 0
mình cũng còn lờ mờ chưa rành lắm nên mấy cái khái niêm cũng ko rõ ràng lắm.
nhưng đại khái là ý mình là xem nó là 1 array ý.
khi kéo 1 vùng nào đó có trên 2 ô ngang thì nó tạo ra 1 mảng {x,y} còn 2 ô dọc thì {x;y} còn có ngang có dọc thì {x,y;x,y}
như khi mình kéo 1 vùng nào đó bất kỳ rồi nhấn F9 nó ra { } ý

như cái code dưới này nó làm được á bạn
Mã:
Function JoinText(ByVal Delimiter As String, ParamArray Arrays()) As String
  Dim aTmp, arrDes(), Item, tmp As String
  Dim idx As Long, n As Long
  'On Error Resume Next
  For idx = LBound(Arrays) To UBound(Arrays)
    aTmp = Arrays(idx)
    If Not IsArray(aTmp) Then aTmp = Array(aTmp)
    For Each Item In aTmp
      If TypeName(Item) <> "Error" Then
        tmp = CStr(Item)
        n = n + 1
        ReDim Preserve arrDes(1 To n)
        arrDes(n) = tmp
      End If
    Next
  Next
  If n Then JoinText = Join(arrDes, Delimiter)
End Function
Khi khai báo ParamArray thì dữ liệu là mảng tham số ... Em có đọc tại https://bettersolutions.com/vba/arrays/paramarray.htm
 
Upvote 0
Về cái vụ "mảng" thì bài #1518 đã giải thích rồi.
Code TextJoin của bạn bị lỗi khi nó xét thấy {1,2,3,4,5} không phải là Range (điều kiện If, bài #1520), và tự động (mệnh đề Else) coi như là một chuỗi, hoặc một kiểu dữ liệu có thể ngầm ép thành chuỗi.
Hàm Len nhận tham là chuỗi, nếu tham là kiểu khác chuỗi thì nó sẽ cố gắng ép thành chuỗi. Thường thì chỉ có kiểu đơn giản (như Integer, Double, Boolean,...) mới ép được thành chuỗi. Mảng {1,2,3,4,5} không phải là kiểu đơn giản cho nên không ép được và hàm Len trả về lỗi

Code JoinText biến tất cả các mẫu dữ liệu trong Paramarry thành mảng VBA (mảng VBA khác với mảng Range). Sau đó nó cứ việc duyệt mảng mà khong sợ nhầm lẫn.

Khi khai báo ParamArray thì dữ liệu là mảng tham số ... Em có đọc tại https://bettersolutions.com/vba/arrays/paramarray.htm

Câu cuối của bài trên nói rằng nếu bạn nạp tham mảng vào Paramarry thì nó sẽ thành mảng 2 chiều. Điều này không hẳn đúng ý nghĩa của mảng 2 chiều. Thực tế thì Paramarray luôn luôn là mảng 1 chiều. Nếu bạn nạp tham mảng thì cả cái mảng ấy sẽ là 1 phần tử của Paramarray.
Ví dụ, bạn nạp
1,2,3,4,5
Thì Paramarray sẽ cho về 1 mảng Arr với 5 phần tử: Arr(0) = 1, Arr(1) = 2, ... Arr(4) = 5
Mặt khác, bạn nạp:
{1,2,3,4,5}
Thì Paramarray sẽ cho về 1 mảng Arr với 1 phần tử, và phần tử này là một mảng 5 phần tử: Arr(0)(1) = 1,... Arr(0)(5) = 5
Nói cách khác, đây là mảng trong mảng chứ không phải mảng 2 chiều.

Chú thích: { } là lệnh gọi hàm Evaluate để dựng mảng. Vì vậy kết quả là mảng Excel (Worksheet), Lbound là 1
Paramarray dùng mảng VBA, LBound luôn luôn là 0. Tôi dùng từ "luôn luôn" bởi vì Option Base 1 chỉ ảnh hưởng hàm Array thường, nhưng không ảnh hưởng được hàm VBA.Array
 
Lần chỉnh sửa cuối:
Upvote 0
Các anh/chị cho em hỏi chút.
Em có 1 sub sau:
Sub Xoadongtrong()
Dim i as Integer
For i = 1 to 100
If cells(i,1) = "" Then
Rows(i).Delete
End if
Next
End Sub

Nhưng khi em chạy sub thì sau lần chạy đầu tiên chỉ xóa được vài dòng, sau đó em phải ấn tiếp thì lại xóa thêm được vài dòng, và em phải chạy sub nhiều lần mới xóa được hết các dòng trống.
Như vậy là lỗi gì ạ.

Nhân tiện đây, em muốn hỏi chút là, em muốn sau khi thực hiên code rows(i).Delete thì 1s sau đó mới tiếp tục thực hiện code tiếp theo trong sub thì phải làm như thế nào ạ.
Em cảm ơn các anh/chị ạ
 
Upvote 0
Các anh/chị cho em hỏi chút.
Em có 1 sub sau:
Sub Xoadongtrong()
Dim i as Integer
For i = 1 to 100
If cells(i,1) = "" Then
Rows(i).Delete
End if
Next
End Sub

Nhưng khi em chạy sub thì sau lần chạy đầu tiên chỉ xóa được vài dòng, sau đó em phải ấn tiếp thì lại xóa thêm được vài dòng, và em phải chạy sub nhiều lần mới xóa được hết các dòng trống.
Như vậy là lỗi gì ạ.

Nhân tiện đây, em muốn hỏi chút là, em muốn sau khi thực hiên code rows(i).Delete thì 1s sau đó mới tiếp tục thực hiện code tiếp theo trong sub thì phải làm như thế nào ạ.
Em cảm ơn các anh/chị ạ
Vấn đề thứ nhất: Giả sử từ hàng 1 đến hàng 100 có 2 hàng trống là hàng 2 và hàng 3. Khi bạn cho i chạy từ 1 đến 100, khi i bằng 2 thì thỏa mãn điều kiện bạn xóa hàng 2 đi, vậy hàng 3 bây giờ đố bạn là hàng mấy? Và khi i bằng 3 đố bạn điều kiện có thỏa mãn hay không? bạn trả lời được 2 câu hỏi của tôi thì bạn hiểu được vấn đề do đâu.
Vấn đề thứ hai: Muốn chạy xong lệnh xóa hàng và chờ 1 giây mới chạy tiếp thì thêm lệnh sau vào sau lệnh xóa hàng.
Mã:
Application.Wait Now + #0:00:01#
 
Upvote 0
Vấn đề thứ nhất: Giả sử từ hàng 1 đến hàng 100 có 2 hàng trống là hàng 2 và hàng 3. Khi bạn cho i chạy từ 1 đến 100, khi i bằng 2 thì thỏa mãn điều kiện bạn xóa hàng 2 đi, vậy hàng 3 bây giờ đố bạn là hàng mấy? Và khi i bằng 3 đố bạn điều kiện có thỏa mãn hay không? bạn trả lời được 2 câu hỏi của tôi thì bạn hiểu được vấn đề do đâu.
Vấn đề thứ hai: Muốn chạy xong lệnh xóa hàng và chờ 1 giây mới chạy tiếp thì thêm lệnh sau vào sau lệnh xóa hàng.
Mã:
Application.Wait Now + #0:00:01#
Em đã hiểu vì sao em phải chạy sub nhiều lần rồi ạ.
Em cảm ơn anh đã hỗ trợ ạ
Bài đã được tự động gộp:

Vấn đề thứ nhất: Giả sử từ hàng 1 đến hàng 100 có 2 hàng trống là hàng 2 và hàng 3. Khi bạn cho i chạy từ 1 đến 100, khi i bằng 2 thì thỏa mãn điều kiện bạn xóa hàng 2 đi, vậy hàng 3 bây giờ đố bạn là hàng mấy? Và khi i bằng 3 đố bạn điều kiện có thỏa mãn hay không? bạn trả lời được 2 câu hỏi của tôi thì bạn hiểu được vấn đề do đâu.
Vấn đề thứ hai: Muốn chạy xong lệnh xóa hàng và chờ 1 giây mới chạy tiếp thì thêm lệnh sau vào sau lệnh xóa hàng.
Mã:
Application.Wait Now + #0:00:01#
Anh cho em hỏi thêm về vấn đề 2.
Em đã thêm code Application.Wait Now + #0:00:01# , nhưng sau khi xuống dòng thì lại hiện thành
Application.Wait Now + #12:00:01 AM#
Có cách nào hiện đúng 1s không ạ, và nếu em muốn chờ 0,5s sau mới chạy tiếp chẳng hạn thì sẽ để định dang như thế nào ạ.
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn biết tụi Tây hồi xưa nó có câu chuyện vui (xin lỗi rất là Tây, bởi vì nó rất kỳ thị chủng tộc):
Đem tất cả dân Tàu sắp hàng đi xuống biển thì cái hàng ấy sẽ không bao giờ dứt.

Bằng ý tưởng đó, nếu muốn xoá dòng thì phải làm ngược từ dưới lên:
For i = 100 to 1 step -1
 
Upvote 0
Bạn biết tụi Tây hồi xưa nó có câu chuyện vui (xin lỗi rất là Tây, bởi vì nó rất kỳ thị chủng tộc):
Đem tất cả dân Tàu sắp hàng đi xuống biển thì cái hàng ấy sẽ không bao giờ dứt.

Bằng ý tưởng đó, nếu muốn xoá dòng thì phải làm ngược từ dưới lên:
For i = 100 to 1 step -1
Cũng được nhưng cách làm này nếu dữ liệu nhiều đợi nó chạy xong mệt xỉu luôn. Cách để nó chạy nhanh là khi điều kiện thỏa mãn thì đưa hàng đó vào một biến vùng (Biến này để lưu hàng trống), khi xong vòng For dùng lệnh xóa một lần, Thứ hai nửa là bạn nên đưa dữ liệu vào mảng duyệt mảng sẽ nhanh hơn duyệt Cell.
 
Upvote 0
Bạn biết tụi Tây hồi xưa nó có câu chuyện vui (xin lỗi rất là Tây, bởi vì nó rất kỳ thị chủng tộc):
Đem tất cả dân Tàu sắp hàng đi xuống biển thì cái hàng ấy sẽ không bao giờ dứt.

Bằng ý tưởng đó, nếu muốn xoá dòng thì phải làm ngược từ dưới lên:
For i = 100 to 1 step -1
Em cảm ơn anh đã hướng dẫn ạ.
Nhưng em chưa hiểu câu chuyện của tụi Tây sao lại suy ra được code kia ạ
 
Upvote 0
Em đã hiểu vì sao em phải chạy sub nhiều lần rồi ạ.
Em cảm ơn anh đã hỗ trợ ạ
Bài đã được tự động gộp:


Anh cho em hỏi thêm về vấn đề 2.
Em đã thêm code Application.Wait Now + #0:00:01# , nhưng sau khi xuống dòng thì lại hiện thành
Application.Wait Now + #12:00:01 AM#
Có cách nào hiện đúng 1s không ạ, và nếu em muốn chờ 0,5s sau mới chạy tiếp chẳng hạn thì sẽ để định dang như thế nào ạ.
Dùng lại thế này xem sao?
Mã:
Application.Wait (Now + TimeValue("0:00:01"))
Có chắc là 0,5 giây không hay hướng dẫn xong lại 0,15 giây... Lúc đầu chỉ hỏi 1 giây bây giờ lại 0,5 giây, trước khi hỏi bạn nên lường trước trường hợp đặc biệt chứ. Gợi ý bạn tìm hàm API Sleep để sử dụng.
 
Upvote 0
Dùng lại thế này xem sao?
Mã:
Application.Wait (Now + TimeValue("0:00:01"))
Có chắc là 0,5 giây không hay hướng dẫn xong lại 0,15 giây... Lúc đầu chỉ hỏi 1 giây bây giờ lại 0,5 giây, trước khi hỏi bạn nên lường trước trường hợp đặc biệt chứ. Gợi ý bạn tìm hàm API Sleep để sử dụng.
Ban đầu em muốn để chờ 1s. Sau đó thử giảm xuống 0,5s xem lệnh có chạy không. Em thấy không được nên mới hỏi cho trường hợp <1s để nếu lần sau muốn dùng hoặc thậm chí dùng ngay ở lần này, coi như là tích lũy thêm kiến thức ạ.
Em sẽ tìm kiếm hàm API Sleep và tìm hiểu về nó ạ.
Em cảm ơn anh nhiều ạ!
 
Upvote 0
Web KT

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

Back
Top Bottom