Các câu đố, bài tập nhằm ôn tập & bổ sung kiến thức căn bản VBA (1 người xem)

Liên hệ QC

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

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,662
Được thích
16,725
Giới tính
Nam
Với tinh thần chơi mà học, học mà chơi, nên tôi đã mở ra topic này, hy vọng các thành viên tham gia, nhất là các thành viên mới biết về VBA.

Sau đây là câu hỏi đầu tiên:

Câu hỏi 1: Bằng phương pháp nào nhanh nhất để tìm ra ô nào trong một cột chứa một điều kiện.

Tôi có 1 file Excel 2007, với cột A, từ A1 đến A1048576 đều có giá trị.

Bằng phương pháp nào nhanh nhất (dùng mảng, dùng For Each v.v...) để tìm ra ô nào trong cột A chứa chữ "Nghia", đồng thời với ô ở cột B tương ứng nhập giá trị "OK" vào đó?

Ví dụ tìm thấy trong ô A2 có giá trị là "Nghia" thì ô B2 nhập vào "OK".

Hiện tại, đáp án nhanh nhất mà tôi có được đã gửi mail riêng (nhằm ghi lại thời gian gửi, để tránh nói ăn gian).

Để tiện việc theo dõi các câu đố, các bài tập tôi đã tạo ra topic này các bạn click vào đây:

Các link của topic "Các câu đố, bài tập nhằm ôn tập & bổ sung kiến thức căn bản VBA"
 

File đính kèm

Lần chỉnh sửa cuối:
Các học viên vừa mới tham gia lớp học VBA tại TPHCM đâu rồi nhỉ? Thử test bài xem nào!
 
Upvote 0
Em xin được gửi bài
Mã:
Sub dotimten()
Dim i As Long
    For i = 1 To 1048576
        If Cells(i, 1) = "Nghia" Then
            Cells(i, 2) = "OK"
        End If
    Next
End Sub


Wow, lần đầu tiên cô Mộng Tiền tham gia viết code! Có lẽ lớp học vừa rồi sẽ có nhiều học viên xuất sắc đây!
 
Upvote 0
Nhóc Tiền viết y sách căn bản của lão chết tiệt!
Mà vì là căn bản nên chưa có xài tới mảng.
 
Upvote 0
Nhóc Tiền viết y sách căn bản của lão chết tiệt!
Mà vì là căn bản nên chưa có xài tới mảng.
Vậy Sư phụ gửi một bài về Array (mảng) mà Sư phụ cho là nhanh nhất lên để các "nhóc" của Sư phụ tham khảo và học hỏi đi ạ!
 
Upvote 0
Em xin được gửi bài
Mã:
Sub dotimten()
Dim i As Long
    For i = 1 To 1048576
        If Cells(i, 1) = "Nghia" Then
            Cells(i, 2) = "OK"
        End If
    Next
End Sub
Thử so cái đó với cái này xem có khác chút gì đó không bé Tiền?
PHP:
Public Sub moneymong()
Dim Arr(), I As Long
Arr = [A:A].Value
For I = 1 To UBound(Arr)
    If Arr(I, 1) = "Nghia" Then
        Arr(I, 1) = "OK"
    Else
        Arr(I, 1) = vbNullString
    End If
Next I
[B:B].Value = Arr
End Sub
 
Upvote 0
e cũng xin thử sức với mảng !
Mã:
sub gpe()
dim i as long, arr(), sarr()
sarr = sheet1.range("a1:a1048576").value
redim arr(1 to ubound(sarr,1),1 to 1)
for i =1 to ubound(sarr,1)
     if sarr(i,1) = "Nghia" then
        arr(i,1) = "Ok"
     end if
next i
sheet1.range("a1").resize(ubound(sarr,1),1).value = arr
end sub
 
Upvote 0
Thử so cái đó với cái này xem có khác chút gì đó không bé Tiền?
PHP:
Public Sub moneymong()
Dim Arr(), I As Long
Arr = [A:A].Value
For I = 1 To UBound(Arr)
    If Arr(I, 1) = "Nghia" Then
        Arr(I, 1) = "OK"
    Else
        Arr(I, 1) = vbNullString
    End If
Next I
[B:B].Value = Arr
End Sub
Công thức mảng con chưa biết qua nó...hehe...
Con sẽ tìm hiểu thử...cám ơn bác..
 
Upvote 0
e cũng xin thử sức với mảng !
Mã:
sub gpe()
dim i as long, arr(), sarr()
sarr = sheet1.range("a1:a1048576").value
redim arr(1 to ubound(sarr,1),1 to 1)
for i =1 to ubound(sarr,1)
     if sarr(i,1) = "Nghia" then
        arr(i,1) = "Ok"
     end if
next i
sheet1.range("a1").resize(ubound(sarr,1),1).value = arr
end sub


Code này có hướng đi nhanh, nhưng khi xử lý thì ... hic ... hic ... mất luôn cột gốc (dữ liệu cột A) rồi!
 
Upvote 0
dạ, e lộn mất tiêu , phải là range("b1") mới đúng !
Cho đến giờ, có thể nói code của bạn là nhanh nhất đấy! Mời các bạn tiếp tục tham gia cho vui, vì càng tham gia các bạn mới có thể học hỏi thêm được nhiều cách hay khác!
 
Upvote 0
Vậy Sư phụ gửi một bài về Array (mảng) mà Sư phụ cho là nhanh nhất lên để các "nhóc" của Sư phụ tham khảo và học hỏi đi ạ!

Thấy mọi người rôm rả quá em cũng xin góp vui 1 code (mặc dù hơi muộn :D).

Mã:
Sub test()  Dim SrcArr, ResArr(1 To 1048576, 1 To 1)
  Dim i As Long
  SrcArr = [A1:A1048576].Value
  [B1:B1048576].ClearContents
  For i = 1 To UBound(SrcArr)
    If SrcArr(i, 1) = "Nghia" Then
      ResArr(i, 1) = "OK"
    End If
  Next i
  [B1].Resize(1048576) = ResArr
End Sub
 
Upvote 0
Upvote 0
Thử so cái đó với cái này xem có khác chút gì đó không bé Tiền?
PHP:
Public Sub moneymong()
Dim Arr(), I As Long
Arr = [A:A].Value
For I = 1 To UBound(Arr)
    If Arr(I, 1) = "Nghia" Then
        Arr(I, 1) = "OK"
    Else
        Arr(I, 1) = vbNullString
    End If
Next I
[B:B].Value = Arr
End Sub

Thầy ơi,

Mấy bài vỡ lòng này thầy để bọn em giải lúc nào bọn em bí quá thì thầy hẵng "RA TAY".

Nhưng qua bài của thầy em lại học thêm được cái vbNullString và thêm cách gán giá trị vào mảng rất hay.

Cảm ơn thầy.
 
Upvote 0
Thầy ơi,

Mấy bài vỡ lòng này thầy để bọn em giải lúc nào bọn em bí quá thì thầy hẵng "RA TAY".

Nhưng qua bài của thầy em lại học thêm được cái vbNullString và thêm cách gán giá trị vào mảng rất hay.

Cảm ơn thầy.
E nãy giờ cũng ngồi ngẫm cách của Thầy Ba Tê, nãy nhìn vào thấy nó ngồ ngộ, giờ mới hiểu ra !
Hay quá !
 
Upvote 0
Để mở rộng về mảng, chúng ta trao đổi một số kinh nghiệm một chút hen!

Ngoài việc xử lý mảng trực tiếp như bác Ba Tê làm thì tôi nói thêm về việc cải thiện tốc độ:

Mã:
Private Sub ForArray()
    Dim i As Long, sArray(), sArr(1 To 1048576, 1 To 1)
[COLOR=#ff0000]    sArray = Sheet1.Range("A:A")[/COLOR]
    For i = 1 To 1048576
        If sArray(i, 1) = "Nghia" Then
            sArr(i, 1) = "OK"
        End If
    Next
    Sheet1.Range("B:B") = sArr
End Sub

Thông thường với dòng màu đỏ ở trên, các bạn có thói quen:

sArray = Sheet1.Range("A:A").Value

Đó không sai, nhưng lại làm chậm tốc độ gán dữ liệu lên mảng. Để tăng tốc, ta loại bỏ .Value thì sẽ nhanh hơn, các bạn thử nghiệm đi, đó là một trong những tiểu xảo tăng tốc đấy.

Dĩ nhiên với trường hợp Array, với bài toán đố này vẫn chưa phải là nhanh, các bạn tiếp tục tìm phương hướng khác xem sao!
 
Lần chỉnh sửa cuối:
Upvote 0
E nãy giờ cũng ngồi ngẫm cách của Thầy Ba Tê, nãy nhìn vào thấy nó ngồ ngộ, giờ mới hiểu ra !
Hay quá !

Hay chỗ nào hè? Chỗ bắt uống ly không bia 1 triệu lần á?

Đùa thôi, gán giá trị cho mảng thì gán gì chẳng được, thêm, sửa, xóa, kết quả tính toán, ... thậm chi gán giá trị mảng.
 
Upvote 0
Để mở rộng về mảng, chúng ta trao đổi một số kinh nghiệm một chút hen!

Ngoài việc xử lý mảng trực tiếp như bác Ba Tê làm thì tôi nói thêm về việc cải thiện tốc độ:

Mã:
Private Sub ForArray()
    Dim i As Long, sArray(), sArr(1 To 1048576, 1 To 1)
[COLOR=#ff0000]    sArray = Sheet1.Range("A:A")[/COLOR]
    For i = 1 To 1048576
        If sArray(i, 1) = "Nghia" Then
            sArr(i, 1) = "OK"
        End If
    Next
    Sheet1.Range("B:B") = sArr
End Sub

Thông thường với dòng màu đỏ ở trên, các bạn có thói quen:

sArray = Sheet1.Range("A:A").Value

Đó không sai, nhưng lại làm chậm tốc độ gán dữ liệu lên mảng. Để tăng tốc, ta loại bỏ .Value thì sẽ nhanh hơn, các bạn thử nghiệm đi, đó là một trong những tiểu xảo tăng tốc đấy.

Dĩ nhiên với trường hợp Array, với bài toán đố này vẫn chưa phải là nhanh, các bạn tiếp tục tìm phương hướng khác xem sao!
Theo như a nói còn cách khác nhanh hơn thì e suy nghĩ là lúc tìm được chữ "Nghĩa" mình gán chữ "OK" vào mảng, rồi sau khi xong thì gán kết quả xuống, vậy giờ mình tìm dc rồi gán xuống sheet luôn( đỡ được 1 bước gán vào mảng ). Theo lối suy nghĩ này e sữa câu lệnh trong hàm if là cells(i,2)="OK"
Không biết e suy nghĩ thế được không ? Có gì anh chỉ bảo thêm !
 
Upvote 0
Theo như a nói còn cách khác nhanh hơn thì e suy nghĩ là lúc tìm được chữ "Nghĩa" mình gán chữ "OK" vào mảng, rồi sau khi xong thì gán kết quả xuống, vậy giờ mình tìm dc rồi gán xuống sheet luôn( đỡ được 1 bước gán vào mảng ). Theo lối suy nghĩ này e sữa câu lệnh trong hàm if là cells(i,2)="OK"
Không biết e suy nghĩ thế được không ? Có gì anh chỉ bảo thêm !
Bạn cứ thử đi, sai chỗ nào ta sửa chỗ đó, đây là chỗ để học, đừng ngần ngại đưa ra ý tưởng của mình! Chẳng hạn với For Each ... Next:

Mã:
Private Sub ForEachRange()
[COLOR=#ff0000]    Dim t As Double[/COLOR]
[COLOR=#ff0000]    t = Timer[/COLOR]
    Dim Rng As Range
    
    For Each Rng In Sheet1.Range("A1:A1048576")
        If Rng = "Nghia" Then Rng.Offset(, 1) = "OK"
    Next
    
[COLOR=#ff0000]    Debug.Print Timer - t[/COLOR]
End Sub

Với các dòng màu đỏ là thủ tục để đo thời gian. Để biết nhanh chậm thế nào các bạn mở VBA (Alt+F11) và mở cửa sổ Immediate (Ctrl+G), sau khi chạy code các bạn sẽ thấy thời gian được đo tại cửa sổ đó.
 
Upvote 0
Bạn cứ thử đi, sai chỗ nào ta sửa chỗ đó, đây là chỗ để học, đừng ngần ngại đưa ra ý tưởng của mình! Chẳng hạn với For Each ... Next:

Mã:
Private Sub ForEachRange()
[COLOR=#ff0000]    Dim t As Double[/COLOR]
[COLOR=#ff0000]    t = Timer[/COLOR]
    Dim Rng As Range
    
    For Each Rng In Sheet1.Range("A1:A1048576")
        If Rng = "Nghia" Then Rng.Offset(, 1) = "OK"
    Next
    
[COLOR=#ff0000]    Debug.Print Timer - t[/COLOR]
End Sub

Với các dòng màu đỏ là thủ tục để đo thời gian. Để biết nhanh chậm thế nào các bạn mở VBA (Alt+F11) và mở cửa sổ Immediate (Ctrl+G), sau khi chạy code các bạn sẽ thấy thời gian được đo tại cửa sổ đó.
dạ, sau khi e thử chạy cả 3 code thì thời gian là
1. Dùng for each của a như trên là : 2,4765625 giây
2. Dùng mảng lúc đầu là : 1,6640625 giây
3. Dùng mảng theo lối suy nghĩ lúc sau : 0,78125 giây
 
Upvote 0
dạ, sau khi e thử chạy cả 3 code thì thời gian là
1. Dùng for each của a như trên là : 2,4765625 giây
2. Dùng mảng lúc đầu là : 1,6640625 giây
3. Dùng mảng theo lối suy nghĩ lúc sau : 0,78125 giây
Bạn gửi cái thủ tục ở mục 3 lên xem nào!
 
Upvote 0
Upvote 0
Dạ đây a !
Mã:
Sub gpe2()
Dim i As Long, sarr(), t As Double
t = Timer
sarr = Sheet1.Range("a1:a1048576").Value
For i = 1 To UBound(sarr, 1)
     If sarr(i, 1) = "Nghia" Then
        Cells(i, 2) = "ok"
     End If
Next i
Debug.Print Timer - t
End Sub

Đúng thế! Đây là thủ tục nhanh nhất mà tôi biết đến! Vì ta kết hợp với mảng, đồng thời làm trực tiếp lên Cell sẽ cho thời gian nhanh hơn rất nhiều so với các thủ tục khác! Cám ơn bạn!
 
Upvote 0
Private Sub ForEachRange()
Dim t As Double
t = Timer
Dim Arr(), I As Long
Arr = Range("A:A").Value




For I = 1 To UBound(Arr)
If Arr(I, 1) = "Nghia" Then
Cells(I, 2) = "OK"
End If


Next I
Debug.Print Timer - t
End Sub



cái này máy tôi chạy 0.78 s
 
Lần chỉnh sửa cuối:
Upvote 0
Theo tôi thì có thể dùng 2 mảng 1 chiều hữu hiệu hơn 1 mảng 2 chiều.

Lúc cóp từ cột ra mảng thì cóp cột dò ra mảng A, và cột kết quả ra mảng B
Lúc dò thấy đạt thì cứ chiếu theo chỉ số mà ghi mảng B
Xong hết thì chép mảng B trở vào cột.

Nếu bạn nào thực sự muốn thử tốc độ code thì cũng cần biết rằng đây là một nhóm dữ liệu hơn triệu đơn vị. Test chưa hẳn đã đơn giản như thế.
Khi biện minh phép dò và ghi kết quả người ta có công thức riêng. Cách "rừng rú" thì cũng phải đặt ra nhiều trường hợp:
- Chả có ô nào đạt -> 0%
- Số ô đạt khoảng 5%
- Số ô đạt khoảng 10%
...
- Số ô đạt 95%, 100%
Sau đó vẽ biểu đồ thời gian theo số dữ liệu.
Và phải gồm một biện chứng trường hợp điển hình nhất là bao nhiêu phần trăm. Tìm ra điểm tối ưu (sweet spot) của code.

Ghi chú thêm: Nhìn sơ thì ta cũng có thể đoán là cách dùng mảng để đọc và ghi thẳng lên cells sẽ hữu hiệu hơn nếu con số ô cần ghi (số ô đạt) ở tỷ lệ thấp. Khi con số này lên càng cao thì công việc truy cập ô sẽ làm nó chậm đi.
 
Lần chỉnh sửa cuối:
Upvote 0
Theo tôi thì có thể dùng 2 mảng 1 chiều hữu hiệu hơn 1 mảng 2 chiều.

Lúc cóp từ cột ra mảng thì cóp cột dò ra mảng A, và cột kết quả ra mảng B
Lúc dò thấy đạt thì cứ chiếu theo chỉ số mà ghi mảng B
Xong hết thì chép mảng B trở vào cột.

Nếu bạn nào thực sự muốn thử tốc độ code thì cũng cần biết rằng đây là một nhóm dữ liệu hơn triệu đơn vị. Test chưa hẳn đã đơn giản như thế.
Khi biện minh phép dò và ghi kết quả người ta có công thức riêng. Cách "rừng rú" thì cũng phải đặt ra nhiều trường hợp:
- Chả có ô nào đạt -> 0%
- Số ô đạt khoảng 5%
- Số ô đạt khoảng 10%
...
- Số ô đạt 95%, 100%
Sau đó vẽ biểu đồ thời gian theo số dữ liệu.
Và phải gồm một biện chứng trường hợp điển hình nhất là bao nhiêu phần trăm. Tìm ra điểm tối ưu (sweet spot) của code.
Vậy bạn thử làm một thủ tục liên quan đến dòng màu đỏ cho bài toán này xem sao! Tôi rất muốn được học hỏi từ bạn.

Ghi chú thêm: Nhìn sơ thì ta cũng có thể đoán là cách dùng mảng để đọc và ghi thẳng lên cells sẽ hữu hiệu hơn nếu con số ô cần ghi ở tỷ lệ thấp. Khi con số này lên càng cao thì công việc truy cập ô sẽ làm nó chậm đi.

Đúng thế, tôi đang băn khoăn về cường độ lặp lại của giá trị đây! Đó là một điều khó khăn cho việc viết code hiệu quả!

Với giá trị 100% thì thủ tục dùng 2 mảng 2 chiều là hiệu quả:

Mã:
Private Sub ForArray()
    Dim t As Double
    t = Timer
    Dim i As Long, sArray(), sArr(1 To 1048576, 1 To 1)
    sArray = Sheet1.Range("A:A")
    For i = 1 To 1048576
        If sArray(i, 1) = "Nghia" Then
            sArr(i, 1) = "OK"
        End If
    Next
    Sheet1.Range("B:B") = sArr
    Debug.Print Timer - t
End Sub

Nhưng tôi chưa biết phải dùng làm sao cho 2 mảng 1 chiều như bạn nói lại chạy nhanh hơn được!
 
Lần chỉnh sửa cuối:
Upvote 0
Private Sub ForEachRange()
Dim t As Double
t = Timer
Dim Arr(), I As Long
Arr = Range("A:A").Value
Dim Arr1(1048576)
For I = 1 To UBound(Arr)
Arr1(I) = Arr(I, 1)
Next

For I = 1 To UBound(Arr1)
If Arr1(I) = "Nghia" Then
Cells(I, 2) = "OK"
End If
Next
Debug.Print Timer - t
End Sub
tốc độ của đoạn code này cũng nhanh chừng 0.8s mà thôi
 
Upvote 0
Câu hỏi 2: Hai mảng một chiều Arr1 và Arr2 này có giống nhau không?

Mã:
Public i As Long, Arr1(0 To 4) As Long, Arr2()


Sub CreatArr1()
    For i = 0 To 4
        [COLOR=#ff0000]Arr1[/COLOR](i) = i + 1
    Next
End Sub


Sub CreatArr2()
    [COLOR=#ff0000]Arr2 [/COLOR]= Array(1, 2, 3, 4, 5)
End Sub

Nếu không, tại sao sau khi chạy 2 code trên và kiểm tra tại code dưới đây tất cả các phần tử tương ứng nhau đều là TRUE?

Mã:
Sub Checked()
    For i = 0 To 4
        MsgBox ([COLOR=#ff0000]Arr1[/COLOR](i) = [COLOR=#ff0000]Arr2[/COLOR](i)) & "/ " & IsNumeric([COLOR=#ff0000]Arr1[/COLOR](i)) & "/ " & IsNumeric([COLOR=#ff0000]Arr2[/COLOR](i))
    Next
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Private Sub ForEachRange()
Dim t As Double
t = Timer
Dim Arr(), I As Long
Arr = Range("A:A").Value
Dim Arr1(1048576)
For I = 1 To UBound(Arr)
Arr1(I) = Arr(I, 1)
Next

For I = 1 To UBound(Arr1)
If Arr1(I) = "Nghia" Then
Cells(I, 2) = "OK"
End If
Next
Debug.Print Timer - t
End Sub
tốc độ của đoạn code này cũng nhanh chừng 0.8s mà thôi

Bạn vẫn chưa hiểu vấn đề, bạn thử gõ chữ "Nghia" ở ô A1 rồi bạn copy nó, sau đó bạn Paste cho cả cột A, rồi bạn chạy code sẽ cho thời gian là bao nhiêu?
 
Upvote 0
Mã:
Private i As Long, Arr1(0 To 4) As Long, Arr2()


Sub CreatArr1()
    For i = 0 To 4
        [COLOR=#ff0000]Arr1[/COLOR](i) = i + 1
    Next
End Sub


Sub CreatArr2()
    [COLOR=#ff0000]Arr2 [/COLOR]= Array(1, 2, 3, 4, 5)
End Sub

Nếu không, tại sao sau khi chạy 2 code trên và kiểm tra tại code dưới đây tất cả các phần tử tương ứng nhau đều là TRUE?

Mã:
Sub Checked()
    For i = 0 To 4
        MsgBox [COLOR=#ff0000]Arr1[/COLOR](i) = [COLOR=#ff0000]Arr2[/COLOR](i)
    Next
End Sub

Khi gán xuống Range 2 mảng sẽ có kết quả:
1. Cùng có 5 phần tử: {1,2,3,4,5}
2. Cùng là mảng 1 chiều.

==> Xin trả lời là 2 mảng này giống nhau.

Không biết tôi hiểu vậy có đúng không nữa. Mong mọi người góp ý.

Xin cảm ơn.
 
Upvote 0
Bạn vẫn chưa hiểu vấn đề, bạn thử gõ chữ "Nghia" ở ô A1 rồi bạn copy nó, sau đó bạn Paste cho cả cột A, rồi bạn chạy code sẽ cho thời gian là bao nhiêu?

Code này mà chạy với tất cả dòng là "Nghia" thì --> tốn thuốc lá --> treo máy luôn.

Sư phụ NDU nói đúng, xử lý ngay trên mảng và gán xuống Range 1 lần vẫn cho tốc độ nhanh hơn rất nhiều.
 
Upvote 0
Code này mà chạy với tất cả dòng là "Nghia" thì --> tốn thuốc lá --> treo máy luôn.

Sư phụ NDU nói đúng, xử lý ngay trên mảng và gán xuống Range 1 lần vẫn cho tốc độ nhanh hơn rất nhiều.


Cuối cùng, bài học rút ra ở đây là:

1) Xử lý mảng rồi gán xuống sheet 1 lần vẫn là tối ưu (cho dữ liệu thỏa điều kiện từ 20% trở lên)

Mã:
Sub gpe()
    Dim i As Long, Arr(), sArr()
    sArr = Sheet1.Range("a1:a1048576").Value
    ReDim Arr(1 To UBound(sArr, 1), 1 To 1)
    For i = 1 To UBound(sArr, 1)
         If sArr(i, 1) = "Nghia" Then
            Arr(i, 1) = "Ok"
         End If
    Next i
    Sheet1.Range("b1").Resize(UBound(sArr, 1), 1).Value = Arr
End Sub

2) Dữ liệu thỏa điều kiện từ dưới 20% trở xuống ta kết hợp vừa mảng nguồn rồi gán xuống từng cell là thượng sách.

Mã:
Private Sub gpe2()
    Dim i As Long, sArray(), sArr(1 To 1048576, 1 To 1)
    sArray = Sheet1.Range("A:A")
    For i = 1 To 1048576
        If sArray(i, 1) = "Nghia" Then
            Cells(i, 2) = "OK"
        End If
    Next
End Sub

3) Xử lý trực tiếp trên sheet luôn luôn cho thời gian rất chậm, nhưng nếu số hàng (row) ít thì OK.

Mã:
Sub dotimten()
    Dim i As Long
    For i = 1 To 1048576
        If Cells(i, 1) = "Nghia" Then
            Cells(i, 2) = "OK"
        End If
    Next
End Sub


hoặc:


Private Sub ForEachRange()
    Dim Rng As Range
    
    For Each Rng In Sheet1.Range("A1:A1048576")
        If Rng = "Nghia" Then Rng.Offset(, 1) = "OK"
    Next
    
End Sub

Các bạn thành viên mới học VBA cần rút kinh nghiệm về các vấn đề này, để sau này định hướng mình sẽ làm gì trên dữ liệu của mình trước khi viết code nhé các bạn.
 
Lần chỉnh sửa cuối:
Upvote 0
Mời các bạn tiếp tục tham gia:

Câu hỏi 2: Hai mảng một chiều Arr1 và Arr2 này có giống nhau không?

Mã:
Public i As Long, Arr1(0 To 4) As Long, Arr2()


Sub CreatArr1()
    For i = 0 To 4
        [COLOR=#ff0000]Arr1[/COLOR](i) = i + 1
    Next
End Sub


Sub CreatArr2()
    [COLOR=#ff0000]Arr2 [/COLOR]= Array(1, 2, 3, 4, 5)
End Sub

Nếu không, tại sao sau khi chạy 2 code trên và kiểm tra tại code dưới đây tất cả các phần tử tương ứng nhau đều là TRUE?

Mã:
Sub Checked()
    For i = 0 To 4
        MsgBox ([COLOR=#ff0000]Arr1[/COLOR](i) = [COLOR=#ff0000]Arr2[/COLOR](i)) & "/ " & IsNumeric([COLOR=#ff0000]Arr1[/COLOR](i)) & "/ " & IsNumeric([COLOR=#ff0000]Arr2[/COLOR](i))
    Next
End Sub

Khi gán xuống Range 2 mảng sẽ có kết quả:
1. Cùng có 5 phần tử: {1,2,3,4,5}
2. Cùng là mảng 1 chiều.

==> Xin trả lời là 2 mảng này giống nhau.

Không biết tôi hiểu vậy có đúng không nữa. Mong mọi người góp ý.

Xin cảm ơn.
 
Lần chỉnh sửa cuối:
Upvote 0
2 mảng 1 chiều:

Lúc đọc cột bình thường thì được mảng 2 chiều. Nhưng nếu lúc đọc cộng thêm hàm Application.Transpose thì có thể ép nó thành mảng 1 chiều.

mngA = Application.Transpose(Range("A1:A1000000").Value)
mngB = Application.Transpose(Range("B1:B1000000").Value)

Đương nhiên làm những cái này rất tốn nhiên liệu nên chỉ hữu dụng khi mảng rất lớn.

Chú thích thêm về chiều của mảng:

Lệnh Arr2chieu(i,j) sử dụng một con toán tính ra vị trí thứ "i*n+j" của mảng, cho nên theo lý thuyết nó sẽ chậm hơn Arr1chieu(i) chỉ sử dụng con toán tính vị trí thứ "i" thôi.
 
Lần chỉnh sửa cuối:
Upvote 0
2 mảng 1 chiều:

Lúc đọc cột bình thường thì được mảng 2 chiều. Nhưng nếu lúc đọc cộng thêm hàm Application.Transpose thì có thể ép nó thành mảng 1 chiều.

mngA = Application.Transpose(Range("A1:A1000000").Value)
mngB = Application.Transpose(Range("B1:B1000000").Value)

Đương nhiên làm những cái này rất tốn nhiên liệu nên chỉ hữu dụng khi mảng rất lớn.

Chú thích thêm về chiều của mảng:

Lệnh Arr2chieu(i,j) sử dụng một con toán tính ra vị trí thứ "i*n+j" của mảng, cho nên theo lý thuyết nó sẽ chậm hơn Arr1chieu(i) chỉ sử dụng con toán tính vị trí thứ "i" thôi.

Cái Application.Transpose này Thầy ndu96081631 đã nói đi nói lại rất rất nhiều lần rồi, nó xử lý rất tệ với dữ liệu lớn, nó còn chậm chạp hơn nhiều so với ta dùng 2 vòng lặp để chuyển chúng thành mảng 1 chiều từ mảng 2 chiều. Vậy sao bạn cứ dường như là "lạc hậu" so với vấn đề này nhỉ?
 
Lần chỉnh sửa cuối:
Upvote 0
Muốn nhanh hơn nữa có lẽ phải dùng Data Object. Cái này chờ cụ Hai Lúa thử xem. Hình như với số lượng này, DAO hữu hiệu hơn ADO một bậc.
 
Upvote 0
Muốn nhanh hơn nữa có lẽ phải dùng Data Object. Cái này chờ cụ Hai Lúa thử xem. Hình như với số lượng này, DAO hữu hiệu hơn ADO một bậc.
Vâng, mặc dù là những câu đố "căn bản", nhưng tôi tin là các thành viên đã, đang, sẽ học VBA trong đó có tôi, sẽ chờ một đáp án bằng DAO hoặc ADO để giải quyết vấn đề ở Câu đố 1 nhằm mục đích để học hỏi, để mở rộng thêm tầm mắt của mình.

Dù là tốc độ đã xét rồi, nhưng sáng nay tôi cũng ngẫm nghĩ và có một giải pháp cho bài đó cũng khá nhanh (có lẽ không bằng kiểu xử lý mảng rồi gán trực tiếp xuống chuỗi dùng cho trường hợp dữ liệu như trong file đính kèm), nhưng đây cũng là một phương pháp hữu hiệu nhưng ít thành viên nhắc đến, đó là dùng FIND METHOD.

Dùng Find Method tôi đã thấy người ta dùng rất nhiều, nhưng tôi chưa đọc được bài nào viết trên diễn đàn nói về dùng Find Next. Vậy nhân đây tôi xin giới thiệu cách viết này cho các bạn cùng tham khảo, góp ý.

Mã:
Private Sub FindNext()
    Dim c As Long, i As Long, j As Long, r As Long, _
        fRng As Range, lRng As Range
    With Sheet1.Range("A1:A1048576")
        Set lRng = .Cells(1, 1)
        For i = 1 To .Rows.Count
            Set fRng = .Find(What:="Nghia", After:=lRng, LookIn:=xlFormulas, LookAt:=xlWhole)
            If fRng Is Nothing Then Exit For
            If fRng.Row = 1 Then Exit For
            .FindNext(After:=lRng).Offset(, 1) = "OK"
            Set lRng = fRng
        Next
    End With
End Sub

Các bạn lưu ý: Với cách tìm kiếm giá trị thì LookIn:=xlFormulas luôn luôn nhanh hơn LookIn:=xlValues.

--------------------------------------------

Do code trên tôi làm một cách vội vã, chưa kiểm tra kỹ nên sẽ phát sinh vòng lặp chạy liên tục nếu ô đầu tiên không thỏa điều kiện, cho nên tôi đã sửa lại ở bài này:

http://www.giaiphapexcel.com/forum/showthread.php?86400-Đố-vui-căn-bản-về-VBA&p=538878#post538878

Chân thành Xin lỗi các bạn!
 
Lần chỉnh sửa cuối:
Upvote 0
Theo trường phái của tôi thì thích nói chuyện lý thuyết.

Theo lý thuyết 2 mảng Arr1 và Arr2 không tương đương với nhau.
Arr1 là mảng có xác định kiểu và kích thước hẳn hoi. Nó là mảng tĩnh.
Arr2 không được xác định, tức là mảng Variant. Và là mảng động.

Variant là biến có kết nối trễ. Mảng động là mảng không được trình dịch cấp phát bộ nhớ trước khi chạy. Nếu tính theo đường lối "chạy nhanh" của bạn thì Arr2 chạy chậm hơn. Tuy nhiên, vì Arr2 là Variant nên nó có thể lợi dụng hàm Array() của VBA để khở trị cho nhanh - hàm có sẵn của hệ thống được viết với dạng tối ưu.

Vì VBA có ngầm chứa phép ép kiểu cho nên cách so sánh bình thường có thể đưa đến kết quả chủ quan. Muốn so sánh thực tiễn phải so cả kiểu dữ liệu.
 
Upvote 0
2 mảng 1 chiều:

Lúc đọc cột bình thường thì được mảng 2 chiều. Nhưng nếu lúc đọc cộng thêm hàm Application.Transpose thì có thể ép nó thành mảng 1 chiều.

mngA = Application.Transpose(Range("A1:A1000000").Value)
mngB = Application.Transpose(Range("B1:B1000000").Value)

Đương nhiên làm những cái này rất tốn nhiên liệu nên chỉ hữu dụng khi mảng rất lớn.

Chú thích thêm về chiều của mảng:

Lệnh Arr2chieu(i,j) sử dụng một con toán tính ra vị trí thứ "i*n+j" của mảng, cho nên theo lý thuyết nó sẽ chậm hơn Arr1chieu(i) chỉ sử dụng con toán tính vị trí thứ "i" thôi.
Cái thèn transpose này nó có 1 đặc điểm là không chỉ hoán đổi dữ liệu mà nó còn can thiệp vào dữ liệu của mình luôn trong quá trình chuyển đổi

e xin thử đưa ra câu trả lời cho câu 2:
Nếu xét từ lúc đặt biến thì e cho rằng 2 mảng này không giống nhau, vì 1 thằng ( arr1) nó là mảng có 5 phần tử, còn thằng ( arr2 ) nó là mảng động , còn chuyện gán xuống sheet nó bằng nhau lý do mình đã cho cái arr2 có số phần tử giống với thằng arr1
Không biết e suy nghĩ đúng chưa, mong a chỉ bảo thêm !
 
Lần chỉnh sửa cuối:
Upvote 0
Theo trường phái của tôi thì thích nói chuyện lý thuyết.

Theo lý thuyết 2 mảng Arr1 và Arr2 không tương đương với nhau.
Arr1 là mảng có xác định kiểu và kích thước hẳn hoi. Nó là mảng tĩnh.
Arr2 không được xác định, tức là mảng Variant. Và là mảng động.

Variant là biến có kết nối trễ. Mảng động là mảng không được trình dịch cấp phát bộ nhớ trước khi chạy. Nếu tính theo đường lối "chạy nhanh" của bạn thì Arr2 chạy chậm hơn. Tuy nhiên, vì Arr2 là Variant nên nó có thể lợi dụng hàm Array() của VBA để khở trị cho nhanh - hàm có sẵn của hệ thống được viết với dạng tối ưu.

Vì VBA có ngầm chứa phép ép kiểu cho nên cách so sánh bình thường có thể đưa đến kết quả chủ quan. Muốn so sánh thực tiễn phải so cả kiểu dữ liệu.
Đây là một câu trả lời cho Câu đố 2 chính xác, tôi cũng xin mở rộng ra một tí là, tôi đã "gài bẫy" khi khai báo biến Arr1(0 To 4) As Long, vì thế cho dù bạn dùng phương thức nào (dù nhanh hay chậm) để tạo ra mảng một chiều, mặc dù là kết quả là như nhau, nhưng chúng khác nhau ở 1 điểm "gài" đó là Data Type!

Kiểm tra:

Mã:
Sub check2()
    MsgBox TypeName(Arr1)
    MsgBox TypeName(Arr2)
End Sub

Bài học rút ra cho các thành viên mới học VBA: Khai báo biến một cách cẩn thận, nếu không sẽ có những lỗi mà mình không biết tại sao!
 
Upvote 0
Câu hỏi 3: [A1] và Range("A1") có khác nhau không?

Mặc dù code để viết ngắn gọn khi dùng Range("A1") nhiều người thường dùng [A1], vậy chúng có gì khác nhau không?

Câu đố rất căn bản phải không các bạn?
 
Lần chỉnh sửa cuối:
Upvote 0
Cái thèn transpose này nó có 1 đặc điểm là không chỉ hoán đổi dữ liệu mà nó còn can thiệp vào dữ liệu của mình luôn trong quá trình chuyển đổi

Có lẽ đúng. Xin lỗi, tôi quên mất mục đích đề tài này là "tốc độ". Tôi chỉ theo thói quen lấy 2 mảng để xử lý 2 mảng song song không nhất thiết cùng một cụm. Vấn đề thường xuyên gặp khi cần cập nhật một cột dữ liệu dựa trên một cột dữ liệu khác.
 
Upvote 0
Lần chỉnh sửa cuối:
Upvote 0
Dùng Find Method tôi đã thấy người ta dùng rất nhiều, nhưng tôi chưa đọc được bài nào viết trên diễn đàn nói về dùng Find Next. Vậy nhân đây tôi xin giới thiệu cách viết này cho các bạn cùng tham khảo, góp ý.

Mã:
        For i = 1 To .Rows.Count
            Set fRng = .Find(What:="Nghia", After:=lRng, LookIn:=xlFormulas, LookAt:=xlWhole)
            If fRng Is Nothing Then Exit For
            If fRng.Row = 1 Then Exit For
            .FindNext(After:=lRng).Offset(, 1) = "OK"
            Set lRng = fRng
        Next

Nghĩa nói sai: GPE có hẳn 1 chuyên đề về Find của bác Chanh và Thu Nghi thảo luận rất sôi nổi. Thậm chí đã tổng hợp thành ebook. Và đương nhiên trong đó có Find Next.

Ngoài ra xét theo code Nghĩa đưa lên, đã dùng find và find next thì dùng vòng lặp Do Loop đánh 1 số lần (đến khi find hết), chứ không dùng for next đánh 1 triệu lần.
 
Upvote 0
Ngoài ra xét theo code Nghĩa đưa lên, đã dùng find và find next thì dùng vòng lặp Do Loop đánh 1 số lần (đến khi find hết), chứ không dùng for next đánh 1 triệu lần.
Vậy là Sư phụ chưa test thử rồi, với code đó For có dùng có 1 tỷ lần lặp đi chăng nữa thì nó cũng chạy đúng số điều kiện trong Range mà thôi. Chẳng hạn trong dữ liệu có 5 hàng chứa "Nghia" thì nó cũng chỉ chạy đúng 6 vòng lặp!

Theo nguyên tắc của Find Next, nếu nó đã chạy từ đầu hàng đến cuối hàng thì nó sẽ quay lại từ đầu, vì thế ta đã chặn nó tại hàng mà chúng duyệt qua rồi thì sẽ thoát For.

Code của bài đó em sửa lại như sau, bởi vì nó bị thiếu duyệt qua ô đầu tiên:

Mã:
Private Sub FindNext2()
    Dim i As Long, j As Long, m As Long, r As Long, _
        fRng As Range, lRng As Range
        
            
    With Sheet1.Range("A1:A1048576")
        
        r = .Rows.Count
        Set lRng = .Cells(r, 1)
        
        For i = 1 To [SIZE=4][COLOR=#ff0000][B]1000000000 [/B][/COLOR][/SIZE][COLOR=#0000ff]'r[/COLOR]


[COLOR=#0000ff]          ''Đếm số lần lặp tại đây:[/COLOR]
[B][COLOR=#ff0000]            m = m + 1[/COLOR][/B]
[B][COLOR=#ff0000]            MsgBox m[/COLOR][/B]
            
            Set fRng = .Find(What:="Nghia", After:=lRng, LookIn:=xlFormulas, LookAt:=xlWhole)
            
            If fRng Is Nothing Then Exit For
            If fRng.Row <= j Then Exit For
            
            .FindNext(After:=lRng).Offset(, 1) = "OK"
            
            j = fRng.Row
            Set lRng = fRng


        Next
    End With
End Sub

Có thể dùng Do ... Loop, thì số vòng lặp cũng như nhau mà thôi.
 
Lần chỉnh sửa cuối:
Upvote 0
Vậy là Sư phụ chưa test thử rồi, với code đó For có dùng có 1 tỷ lần lặp đi chăng nữa thì nó cũng chạy đúng số điều kiện trong Range mà thôi. Chẳng hạn trong dữ liệu có 5 hàng chứa "Nghia" thì nó cũng chỉ chạy đúng 6 vòng lặp!

Theo nguyên tắc của Find Next, nếu nó đã chạy từ đầu hàng đến cuối hàng thì nó sẽ quay lại từ đầu, vì thế ta đã chặn nó tại hàng mà chúng duyệt qua rồi thì sẽ thoát For.

Có thể dùng Do ... Loop, thì số vòng lặp cũng như nhau mà thôi.

Thứ nhất: người đang học căn bản nhìn vào thấy 1 triệu lần
Thứ hai: Nếu ô đâu tiên (A1) không phải "Nghia" thì fRng.Row không bao giờ bằng 1, không Exit For được, và phải đánh đủ 1 triệu lần.
Thứ ba: Biến i không hề được sử dụng, nhưng là vòng lặp For i

Code sau khi sửa ở bài 49:

Dĩ nhiên sau khi sửa thì giảm số lần lặp. Nhưng biến i cũng vẫn không được sử dụng!
 
Upvote 0
Thứ nhất: người đang học căn bản nhìn vào thấy 1 triệu lần
Thứ hai: Nếu ô đâu tiên (A1) không phải "Nghia" thì fRng.Row không bao giờ bằng 1, không Exit For được, và phải đánh đủ 1 triệu lần.
Thứ ba: Biến i không hề được sử dụng, nhưng là vòng lặp For i

Code sau khi sửa ở bài 49:

Dĩ nhiên sau khi sửa thì giảm số lần lặp. Nhưng biến i cũng vẫn không được sử dụng!

Xin thưa với Sư phụ, dòng màu đỏ là không chính xác! Code đầu do nó không tính tới hàng 1 nên nếu số hàng có "Nghia" là 5 thì nó lặp đúng 5 lần chứ không phải là 6 lần như đã sửa ở code sau đâu Sư phụ!

Nhưng biến i cũng vẫn không được sử dụng!

Ta đâu cần sử dụng biến i làm gì! Miễn số vòng lặp tối thiểu bằng tổng số hàng mà chúng duyệt qua là được, đừng nhỏ hơn sẽ bị thiếu nếu dữ liệu 100% có từ "Nghia".

Thứ hai: Nếu ô đâu tiên (A1) không phải "Nghia" thì fRng.Row không bao giờ bằng 1, không Exit For được, và phải đánh đủ 1 triệu lần.

Chính vì thế mà em mới sửa code để tối ưu nó (sáng làm vội nên chưa check nên sau đó check lại và sửa lại), nhưng đó cũng là một phương pháp để giải toán.
 
Lần chỉnh sửa cuối:
Upvote 0
Upvote 0
E nghĩ là nó khác nhau, vì hình như cái dấu "[]" là hàm evaluate thì phải( tức là tính toán giá trị tại a1, còn range("a1") là chỉ đến cái vùng trong a1
E chỉ hiểu như thế, mong mọi người góp ý !
Mục đích của topic này không phải chỉ là đố vui đâu, tôi đặt topic này còn là nơi chia sẽ, trao đổi kinh nghiệm và học hỏi lẫn nhau.

Bạn tham gia thật nhiệt tình. Đúng như bạn nói nó chính là một Evaluate!

Thay vì ta viết Evaluate("A1") thì ta viết [A1] cho ngắn gọn.

Nếu như Range() ta có thể làm được như thế này:

Range("A" & lRow)

Nhưng với [A & lRow] thì hoàn toàn không! Vì thế ta phải viết như thế này: Evaluate("A" & lRow)

Bởi Evaluate là một hàm, cho nên chúng sẽ tính toán chậm hơn khi ta dùng Range().

Nhưng chúng ta sẽ rất tiện dụng khi làm công thức với Evalute. Thay vì ghi macro cho các ô ta gán công thức sẽ được như thế này:

Mã:
Sub Macro2()
    Range("E1").FormulaR1C1 = "=VLOOKUP(""Nghia"",RC[-4]:R[6]C[-3],2,0)"
    Range("E2").FormulaR1C1 = "=SUMIF(R[-1]C[-4]:R[5]C[-4],""Nghia"",R[-1]C[-3]:R[5]C[-3])"
End Sub

Chuyển sang Evaluate, chúng có dạng như thế này:

Mã:
Sub Macro3()
    Range("E1") = Evaluate("VLOOKUP(""Nghia"",A1:B7,2,0)")
    Range("E2") = Evaluate("SUMIF(A1:A7,""Nghia"",B1:B7)")
End Sub

Thì ta viết ngắn gọn hơn, đỡ phải lằng ngoằng bởi các dấu ngoặc kép (""):

Mã:
Sub Macro4()
    [E1] = [VLOOKUP("Nghia",A1:B7,2,0)]
    [E2] = [SUMIF(A1:A7,"Nghia",B1:B7)]
End Sub



Lưu ý: Phải nhớ rằng vì nó là một hàm, cho nên nó tính toán sẽ chậm hơn!
 
Lần chỉnh sửa cuối:
Upvote 0
Chủ đề này khá hay đấy chứ mình cũng tham gia chút gọi là ủng hộ a.Nghĩa bài 1 mình xin góp code dùng ADO nhưng sao mà có lẽ thấy cũng chậm đấy chứ lâu quá không đóng góp gì cũng ngại với dữ liệu của a.Nghĩa ta có thể dùng ADO

[GPECODE=vb]
Sub DovuiADO()


Dim sSQL As String, cnn As Object, rst As Object
Set cnn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.Recordset")
With cnn
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & _
";Extended Properties=""Excel 12.0;HDR=No;IMEX=1"";"
.Open
End With
With rst
.ActiveConnection = cnn
sSQL = "SELECT IIf([F1]='Nghia','OK',[F1]) FROM [Dovui$];"
.Open sSQL
End With
With Sheets("Dovui")
.[B1:B1048576].ClearContents
.[B1].CopyFromRecordset rst
End With
rst.Close: Set rst = Nothing
cnn.Close: Set cnn = Nothing

End Sub
[/GPECODE]
 
Upvote 0
Cũng vì [] là một Evaluate, nên tôi nói rộng ra một chút cho các bạn dùng mảng hiểu được tại sao ta bị lỗi!

Với Range() ta sử dụng thế nào cũng được:

Mã:
Sub Macro7()
    Dim Arr()
    [COLOR=#0000ff]Arr = Range("B1:B6")[/COLOR][B][COLOR=#ff0000].Value[/COLOR][/B][COLOR=#0000ff][/COLOR]
''Hoặc:
[COLOR=#0000ff]    ''Arr = Range("B1:B6")[/COLOR]
End Sub

Nhưng với [] thì không được như vậy, sẽ bị lỗi trong trường hợp này:

Mã:
Sub Macro5()
    Dim Arr()
[COLOR=#0000ff]    Arr = [B1:B6][/COLOR]
End Sub

Tức là bị lỗi khi thiếu .Value. Cho nên ta chỉ được sử dụng chúng như thế này:

Mã:
Sub Macro6()
    Dim Arr()
[COLOR=#0000ff]    Arr = [B1:B6][/COLOR][B][COLOR=#FF0000].Value[/COLOR][/B]
End Sub



VBA cũng thật là rối rắm phải không các bạn?!
 
Upvote 0
Chủ đề này khá hay đấy chứ mình cũng tham gia chút gọi là ủng hộ a.Nghĩa bài 1 mình xin góp code dùng ADO nhưng sao mà có lẽ thấy cũng chậm đấy chứ lâu quá không đóng góp gì cũng ngại với dữ liệu của a.Nghĩa ta có thể dùng ADO

Trước tiên nó chậm vì có một thời gian nhất định khi kết nối ngầm.

Thứ 2 nó chậm là do dòng này: .[B1:B1048576].ClearContents, ta có thể bỏ qua nó mà!
 
Upvote 0
Đề nghị các cao thủ kiềm chế, không bàn quá sâu, topic này mang tên câu đố cơ bản, hãy động viện các người mới nào.
Các bác bàn kiểu này đố có ma mới nào dám bén mảng tới. :D

OverAC
 
Upvote 0
Đề nghị các cao thủ kiềm chế, không bàn quá sâu, topic này mang tên câu đố cơ bản, hãy động viện các người mới nào.
Các bác bàn kiểu này đố có ma mới nào dám bén mảng tới. :D

OverAC
Chính xác là thế đó thầy!
Em không hiểu cái mô tê gì hết.
Em nghĩ anh Nghĩa nên mở thêm 1 topic nữa, cùng 1 ví dụ, 1 topic dành cho người mới bắt đầu học, 1 topic dành cho các anh chị đã biết và muốn mở rộng.
Sau này các anh chị hiểu biết nâng cao hơn, sẽ vào topic nâng cao bàn luận.
Như vậy cùng 1 lúc mà giải quyết 2 vấn đề.
 
Upvote 0
Chính xác là thế đó thầy!
Em không hiểu cái mô tê gì hết.
Em nghĩ anh Nghĩa nên mở thêm 1 topic nữa, cùng 1 ví dụ, 1 topic dành cho người mới bắt đầu học, 1 topic dành cho các anh chị đã biết và muốn mở rộng.
Sau này các anh chị hiểu biết nâng cao hơn, sẽ vào topic nâng cao bàn luận.
Như vậy cùng 1 lúc mà giải quyết 2 vấn đề.

Trời ơi, các thành viên thông cảm cho, làm sao tôi biết được đâu là nhóm màu xanh và đâu là nhóm màu đỏ đây?

Thôi thì để topic này dành cho nhóm màu đỏ, tôi sẽ đố hoặc đặt ra các câu hỏi dựa vào những bài trong giáo trình của lớp VBA vừa rồi được không nhỉ?

Như thế thì các cao thủ xin vui lòng không trả lời cho đến khi các thành viên mới học VBA tham gia (sao biết mới học đây ta?), đồng thời khi trả lời các cao thủ vui lòng dẫn chứng vài trường hợp áp dụng cụ thể nhé!

 
Lần chỉnh sửa cuối:
Upvote 0
Đề nghị các cao thủ kiềm chế, không bàn quá sâu, topic này mang tên câu đố cơ bản, hãy động viện các người mới nào.
Các bác bàn kiểu này đố có ma mới nào dám bén mảng tới. :D

OverAC

Không phải tôi bắt bẻ bạn. Cái khó là tuỳ theo cách học mà vấn đề X đối với A là cơ bản trong khi đối với B là sâu.
Tốt hơn hết là định nghĩa cái giới hạn của cơ bản trước đã.

Đối với tôi chẳng hạn, chỉ riêng việc cóp range ra array để xử lý đã thuộc về cao cấp rồi.
 
Upvote 0
Câu hỏi 4: Sau khi chạy vòng lặp, i bằng bao nhiêu?

Đây là câu hỏi dành cho các thành viên mới học VBA.

Tôi có thủ tục sau:

Mã:
Sub ForNext()
[COLOR=#0000cd][B]    Dim i As Long[/B][/COLOR]
    For [COLOR=#ff0000][B]i = 1 To 1000[/B][/COLOR]
        ''Your code here
    Next
End Sub

Các bạn nhìn vào code, khoan thử vội, đố các bạn biết sau khi chạy code, i sẽ có giá trị là bao nhiêu?
 
Lần chỉnh sửa cuối:
Upvote 0
Đây là câu hỏi dành cho các thành viên mới học VBA.

Tôi có thủ tục sau:

Mã:
Sub ForNext()
[COLOR=#0000cd][B]    Dim i As Long[/B][/COLOR]
    For [COLOR=#ff0000][B]i = 1 To 1000[/B][/COLOR]
        ''Your code here
    Next
End Sub

Các bạn nhìn vào code, khoan thử vội, đố các bạn biết sau khi chạy code, i sẽ có giá trị là bao nhiêu?
Sau vòng lặp cuối cùng, i=1000, tuy nhiên sau khi chạy "Your code here" có lệnh next nên sau khi chạy thì i sẽ tăng lên 1 tức là i=1001.
 
Upvote 0
Đây là câu hỏi dành cho các thành viên mới học VBA.

Tôi có thủ tục sau:

Mã:
Sub ForNext()
[COLOR=#0000cd][B]    Dim i As Long[/B][/COLOR]
    For [COLOR=#ff0000][B]i = 1 To 1000[/B][/COLOR]
        ''Your code here
    Next
End Sub

Các bạn nhìn vào code, khoan thử vội, đố các bạn biết sau khi chạy code, i sẽ có giá trị là bao nhiêu?

Đây cũng chính là chỗ e khó hiểu nhất. Mặc dù mình cho nó chạy tới 1000 mà sau khi hết vòng lặp nó luôn luôn là 1000+1.
Mong a giải thích cho e hiểu chỗ này được không ah !
 
Upvote 0
Đây là câu hỏi dành cho các thành viên mới học VBA.

Tôi có thủ tục sau:

Mã:
Sub ForNext()
[COLOR=#0000cd][B]    Dim i As Long[/B][/COLOR]
    For [COLOR=#ff0000][B]i = 1 To 1000[/B][/COLOR]
        ''Your code here
    Next
End Sub

Các bạn nhìn vào code, khoan thử vội, đố các bạn biết sau khi chạy code, i sẽ có giá trị là bao nhiêu?
Mặc định i = 0 trước For được xem là 1 giá trị hay không?
Còn lại là như 2 bạn trên đã trả lời, nếu cộng luôn i = 0 thì tổng cộng có 1002 giá trị.
 
Upvote 0
Đây cũng chính là chỗ e khó hiểu nhất. Mặc dù mình cho nó chạy tới 1000 mà sau khi hết vòng lặp nó luôn luôn là 1000+1.
Mong a giải thích cho e hiểu chỗ này được không ah !

Thì "nó" gọi là "code" mà, đâu phải NGƯỜI
Nó phải cộng 1 vào và so sánh nó mới biết thằng mới cộng vào lớn hơn giới hạn chứ
Tóm lại: Giải thuật For... Next nó là như vậy. Nếu không thích có thể dùng Do... Loop
 
Upvote 0
Cám ơn các "tềnh iu" đã trả lời, thế thì với các bạn, trong code của tôi, các bạn bằng thủ tục nào để xác định được i vậy? Ý tôi nói các bạn đặt thủ tục nào, tại đâu trong code của tôi thủ tục của bạn để nhận biết được i?
 
Upvote 0
Cám ơn các "tềnh iu" đã trả lời, thế thì với các bạn, trong code của tôi, các bạn bằng thủ tục nào để xác định được i vậy? Ý tôi nói các bạn đặt thủ tục nào, tại đâu trong code của tôi thủ tục của bạn để nhận biết được i?
Em dùng Msgbox i sau next
 
Upvote 0
Không phải tôi bắt bẻ bạn. Cái khó là tuỳ theo cách học mà vấn đề X đối với A là cơ bản trong khi đối với B là sâu.
Tốt hơn hết là định nghĩa cái giới hạn của cơ bản trước đã.
Đối với tôi chẳng hạn, chỉ riêng việc cóp range ra array để xử lý đã thuộc về cao cấp rồi.
Anh Vetmini đã có những gợi ý (trong topic hỏi cơ bản về VBA của tungnguyen_tk và topic này) mình cảm thấy "rất cần" cho những người đang đi những bước cơ bản với VBA (như mình). Trong topic này, nên chăng có những giới hạn, những chỉ mục cơ bản, cái cơ bản có sự lựa chọn, có quản lý và có định hướng.
Ở một số diễn đàn có những Event để thành viên theo dõi, tham gia. Như những câu đố hàng năm của Thầy Mỹ vào mỗi dịp sinh nhật GPE, thành viên hào hứng tham gia và mỗi kết quả là những bài học bổ ích. Topic Đối vui căn bản về VBA này cũng vậy, mong là anh Nghĩa và các Thầy duy trì thường xuyên, cập nhật câu hỏi mỗi ngày.
Với tinh thần chơi mà học, học mà chơi, nên tôi đã mở ra topic này, hy vọng các thành viên tham gia, nhất là các thành viên mới biết về VBA.
....
Hiện tại, đáp án nhanh nhất mà tôi có được đã gửi mail riêng (nhằm ghi lại thời gian gửi, để tránh nói ăn gian).
Có thể mở Tag HIDE trên GPE được không, nếu được thì thuận tiện hơn khi gửi bài (chỉ có BQT mới xem được).
 
Lần chỉnh sửa cuối:
Upvote 0
Đây cũng chính là chỗ e khó hiểu nhất. Mặc dù mình cho nó chạy tới 1000 mà sau khi hết vòng lặp nó luôn luôn là 1000+1.
Mong a giải thích cho e hiểu chỗ này được không ah !
Xin giải thích một lần nữa về vòng lặp For: Xét vòng lặp For như sau:
[GPECODE=vb]For i = Dau To Cuoi
'Dãy lệnh
Next[/GPECODE]
Vòng lặp For này sẽ được thực hiện thông qua một số bước như sau:
[NOTE2=""]Bước 1: Gán i = Dau
Bước 2: Kiểm tra điều kiện i <= Cuoi
- Nếu thỏa mãn thì qua Bước 3;
- Nếu không thỏa mãn thì thoát khỏi vòng lặp.
Bước 3:
3.1. Thực hiện Dãy lệnh ở trong vòng For
3.2. Gán i = i + 1 và quay lại Bước 2.[/NOTE2]
Do đó:
- Nếu Dau > Cuoi thì Dãy lệnh trong vòng lặp không được thực hiện và kết quả là i = Dau (do đã được gán từ Bước 1)
- Nếu Dau <= Cuoi thì Dãy lệnh được thực hiện ít nhất 1 lần và kết quả là i = Cuoi + 1, vì ở lần cuối cùng Dãy lệnh được thực hiện thì i = Cuoi, sau đó tại bước 3.2, phép gán i = i + 1 sẽ cho i = Cuoi + 1 (đương nhiên sau phép gán này thì điều kiện ở Bước 2 bị vi phạm nên vòng lặp kết thúc).

Nhân đây, xin đố các bạn: Nếu thay vòng lặp trên bởi vòng lặp sau thì sau khi chạy, i sẽ bằng bao nhiêu:
[GPECODE=vb]For i = Dau To Cuoi Step n
'Dãy lệnh
Next[/GPECODE]
Cụ thể hơn là:
[GPECODE=vb]For i = 1 To 1000 Step 6
'Dãy lệnh
Next[/GPECODE]
(tất nhiên là theo đúng tinh thần của topic này đưa ra: Đố vui căn bản về VBA)
 
Upvote 0
Để cho có hệ thống, nghiaphuc vui lòng ngắt phần này ra thành bài mới và đánh số bài đố tiếp tục được không ạ? cám ơn!

====================

Đọc xong xin vui lòng xóa bài này!
Thực ra thì câu đố của em chỉ là phát triển, hay nói chính xác là tổng quát câu 5 của anh thôi mà, nó không phải là một câu hỏi mới, vì nguyên lý hoạt động của vòng For là chỉ có một, và câu 5 của anh chỉ là trường hợp riêng trong câu của em, ứng với n = 1 thôi mà.
 
Upvote 0
Xin giải thích một lần nữa về vòng lặp For: Xét vòng lặp For như sau:
[GPECODE=vb]For i = Dau To Cuoi
'Dãy lệnh
Next[/GPECODE]
Vòng lặp For này sẽ được thực hiện thông qua một số bước như sau:
[NOTE2=""]Bước 1: Gán i = Dau
Bước 2: Kiểm tra điều kiện i <= Cuoi
- Nếu thỏa mãn thì qua Bước 3;
- Nếu không thỏa mãn thì thoát khỏi vòng lặp.
Bước 3:
3.1. Thực hiện Dãy lệnh ở trong vòng For
3.2. Gán i = i + 1 và quay lại Bước 2.[/NOTE2]
Do đó:
- Nếu Dau > Cuoi thì Dãy lệnh trong vòng lặp không được thực hiện và kết quả là i = Dau (do đã được gán từ Bước 1)
- Nếu Dau <= Cuoi thì Dãy lệnh được thực hiện ít nhất 1 lần và kết quả là i = Cuoi + 1, vì ở lần cuối cùng Dãy lệnh được thực hiện thì i = Cuoi, sau đó tại bước 3.2, phép gán i = i + 1 sẽ cho i = Cuoi + 1 (đương nhiên sau phép gán này thì điều kiện ở Bước 2 bị vi phạm nên vòng lặp kết thúc).

Nhân đây, xin đố các bạn: Nếu thay vòng lặp trên bởi vòng lặp sau thì sau khi chạy, i sẽ bằng bao nhiêu:
[GPECODE=vb]For i = Dau To Cuoi Step n
'Dãy lệnh
Next[/GPECODE]
Cụ thể hơn là:
[GPECODE=vb]For i = 1 To 1000 Step 6
'Dãy lệnh
Next[/GPECODE]
(tất nhiên là theo đúng tinh thần của topic này đưa ra: Đố vui căn bản về VBA)

i=dau+INT((cuoi-dau+1)/n)*n+n
Trường hợp n=6, dau=1, cuoi=1000=>i=1002
 
Lần chỉnh sửa cuối:
Upvote 0
Mặc định i = 0 trước For được xem là 1 giá trị hay không?
Còn lại là như 2 bạn trên đã trả lời, nếu cộng luôn i = 0 thì tổng cộng có 1002 giá trị.
Đó là nhận xét của leonguyenz, tuy nhiên dù là trước nó có là 0 hay bất cứ số nào (0, 76, 100, 999, v.v...) nó luôn trả về giá trị i = giá trị cuối + 1.

Chẳng hạn:

Mã:
Sub test()
    For i = [COLOR=#ff0000]999 [/COLOR]To 1000
    Next
    Debug.Print i
End Sub

Các bạn có thể thay 999 bằng 1 số bất kỳ miễn đừng lớn hơn 1000 là được, riêng trường hợp lớn hơn 1000 thì i sẽ bằng chính số đó.
 
Upvote 0
Mình có một thắc mắc tại sao MsgBox i có kết quả là 1003?
Đúng vậy, khi Step là 6 thì bắt đầu nó lặp như sau

Với For i = 1 to 1000 Step 6

vòng lặp đầu tiên nó sẽ có giá trị i = 1
vòng thứ 2 nó có giá trị i = 7
vòng thứ 3 nó có giá trị i = 13
....

Nói chung là căn cứ vào giá trị đầu nó sẽ chạy ở vòng lặp đầu tiên, sau đó nó mới tính đến bước step.

Như thế khi nó chạy đến số 997 tức 1 + 6 * 166 lần lặp nó vẫn chưa đạt đến số 1000 vì thế nó phải lặp thêm 1 vòng nữa cho đủ. Vì bước step nó là 6 nên nó "rướn" lên đến 1003, như thế đã không thỏa điều kiện là 1000 nên nó thoát vòng lặp!
 
Upvote 0
Đó là nhận xét của leonguyenz, tuy nhiên dù là trước nó có là 0 hay bất cứ số nào (0, 76, 100, 999, v.v...) nó luôn trả về giá trị i = giá trị cuối + 1.

Chẳng hạn:

Mã:
Sub test()
    For i = [COLOR=#ff0000]999 [/COLOR]To 1000
    Next
    Debug.Print i
End Sub

Các bạn có thể thay 999 bằng 1 số bất kỳ miễn đừng lớn hơn 1000 là được, riêng trường hợp lớn hơn 1000 thì i sẽ bằng chính số đó.
theo mình thì i ko nhất thiết là giá trị cuối cộng 1
mà chính là giá trị i cuối + với giá trị step
nên cái 1002 của leonguyenz có được khi step là 2
quote_icon.png
Nguyên văn bởi leonguyenz
Mặc định i = 0 trước For được xem là 1 giá trị hay không?
Còn lại là như 2 bạn trên đã trả lời, nếu cộng luôn i = 0 thì tổng cộng có 1002 giá trị.


nếu i bằng 0 to 1000 (step là 1) thì nó có 1001 vòng lập chứ ko phải 1002 được
for i = 0 to 10 step 4
a=a+4 'vi du vay
next i
và cai i cuối cùng dùng so với 10 theo mình nó sẽ là 12
và các giá tri i của nó sẽ là 0, 4 , 8 và 12
nó chỉ cộng 1 khi ta ko có gia tri step hoặc đặt giá tri step là 1

để ko tạo bài rác mình trích dẫn trong bài
Ở bài đố 5, tôi có thêm bước step nào không vậy? Nếu không có bước step nào thì mặc định chúng là 1. Điều này không cần giải thích nhé!

Good-Luck coi lại bài #76 nhé! Tôi đã giải thích rất rõ rồi đấy!
Anh nghia quá nhảy cảm rồi, do thấy bài LeonguyeenZ nên mình giải thích thêm phần leonguyenz thắt mắt thôi
 
Lần chỉnh sửa cuối:
Upvote 0
theo mình thì i ko nhất thiết là giá trị cuối cộng 1
mà chính là giá trị i cuối + với giá trị step
nên cái 1002 của leonguyenz có được khi step là 2

for i = 0 to 10 step 4
a=a+4 'vi du vay
next i
và cai i cuối cùng dùng so với 10 theo mình nó sẽ là 12
và các giá tri i của nó sẽ là 0, 4 , 8 và 12
nó chỉ cộng 1 khi ta ko có gia tri step hoặc đặt giá tri step là 1

Ở bài đố 5, tôi có thêm bước step nào không vậy? Nếu không có bước step nào thì mặc định chúng là 1. Điều này không cần giải thích nhé!

Good-Luck coi lại bài #76 nhé! Tôi đã giải thích rất rõ rồi đấy!
 
Upvote 0
theo mình thì i ko nhất thiết là giá trị cuối cộng 1
mà chính là giá trị i cuối + với giá trị step
nên cái 1002 của leonguyenz có được khi step là 2

for i = 0 to 10 step 4
a=a+4 'vi du vay
next i
và cai i cuối cùng dùng so với 10 theo mình nó sẽ là 12
và các giá tri i của nó sẽ là 0, 4 , 8 và 12
nó chỉ cộng 1 khi ta ko có gia tri step hoặc đặt giá tri step là 1
Với Câu đố 5 (#61) thì đúng là Step bằng 1 đó bạn.
 
Upvote 0
Thật ra đăc tính của vòng lặp FOR tôi đã diễn giải một lần rồi. Ở thớt nào quên mất tiêu.
Trong đó tôi có nhấn mạnh:
Vòng lặp sẽ kết thúc khi trị số của biến đếm (i) vượt quá giới hạn cuối (trị ngay sau từ khoá "TO"). Vì vậy sau khi chạy hết vòng lặp, trị của biến này sẽ lớn hơn hoặc nhỏ hơn tuỳ theo bước dương hay âm, nhưng không bao giờ bằng.
Chỉ có một trường hợp ngoại lệ duy nhất là khi vòng lặp được thoát sớm, bằng lệnh Exit hay lệnh Goto, Resume,...

Để giải thích lý do tại sao từ "TO" lại có nghĩa như vậy phải trở về lịch sử ngôn ngữ lập trình từ thập niên 1950's

Công thức của honagminhtien không đúng:
1. Nó không tính đến trường hợp trị đầu lớn hơn giới hạn cuối. Trường hợp này, trị của biến đếm là trị đầu.
2. FOR i = 1 TO 1000 STEP 6 : Trị đầu là số lẻ, bước là số chẵn. Suy ra i luôn luôn là số lẻ, không thể = 1002.
 
Upvote 0
i=dau+INT((cuoi-dau+1)/n)*n+n
Trường hợp n=6, dau=1, cuoi=1000=>i=1002
Công thức của Tiến không đúng với trường hợp cuoi chia hết cho n.
Mình thử công thức như vậy, nhờ mọi người rút gọn:
Mã:
=1+INT(1000/J9)*J9+IF(MOD(1000,J9),J9,MOD(1000,J9))
 
Upvote 0
Như vậy, khi các bạn làm tới đây tôi đã có một bài ứng dụng nhỏ xíu cho các bạn khi sử dụng For ... Next:

Thủ tục:

Mã:
Sub UngDung()
    For i = 1 To 5
        With Sheet1.Range("B" & i)
            If .Value = "" Then
                MsgBox "ban chua nhap tai cell " & .Address(0, 0)
                .Select
                Sheet1.[B6] = ""
                Exit Sub
            End If
        End With
    Next
    Sheet1.Range("B6") = [SUM(B1:B5)]
End Sub

Các bạn xem file rồi bấm nút lệnh, có thắc mắc gì có liên quan thì cứ đưa câu hỏi lên nhé!
 

File đính kèm

Upvote 0
Như vậy, khi các bạn làm tới đây tôi đã có một bài ứng dụng nhỏ xíu cho các bạn khi sử dụng For ... Next:

Thủ tục:

Mã:
Sub UngDung()
    For i = 1 To 5
        With Sheet1.Range("B" & i)
            If .Value = "" Then
                MsgBox "ban chua nhap tai cell " & .Address(0, 0)
                .Select
                Sheet1.[B6] = ""
                Exit Sub
            End If
        End With
    Next
    Sheet1.Range("B6") = [SUM(B1:B5)]
End Sub

Các bạn xem file rồi bấm nút lệnh, có thắc mắc gì có liên quan thì cứ đưa câu hỏi lên nhé!

Anh nên tập thói quen khai báo tường minh cho người mới nhé.
 
Upvote 0
Anh nên tập thói quen khai báo tường minh cho người mới nhé.
Mình cố tình làm như thế đó Hai Lúa Miền Tây à (thử nhìn từ đầu bài đến bài kế bài thực hành, có thủ tục nào mình không khai báo tường minh không?), mình nghĩ một số bạn mới học VBA sẽ có thắc mắc nhiều thứ (như trong file có With ... End With, .Address(0,0) v.v...), và khai báo biến, vì thế nên mới thòng câu này:

Các bạn xem file rồi bấm nút lệnh, có thắc mắc gì có liên quan thì cứ đưa câu hỏi lên nhé!

Đồng thời, đó cũng là đề tài để đố ở câu hỏi sau! Chờ các bạn mới có sự phản hồi hoặc đặt câu hỏi đã.
 
Lần chỉnh sửa cuối:
Upvote 0
Đây là câu hỏi dành cho các thành viên mới học VBA.

Tôi có thủ tục sau:

Mã:
Sub ForNext()
[COLOR=#0000cd][B]    Dim i As Long[/B][/COLOR]
    For [COLOR=#ff0000][B]i = 1 To 1000[/B][/COLOR]
        ''Your code here
    Next
End Sub

Các bạn nhìn vào code, khoan thử vội, đố các bạn biết sau khi chạy code, i sẽ có giá trị là bao nhiêu?

Kiểu dữ liệu cần khai báo phải phù hợp với dữ liệu của mình để tiết kiệm bộ nhớ.
Anh nên đưa ra bảng chung về kiểu dữ liệu để người mới áp dụng cho phù hợp.
 
Upvote 0
Mình cố tình làm như thế đó Hai Lúa Miền Tây à (thử nhìn từ đầu bài đến bài kế bài thực hành, có thủ tục nào mình không khai báo tường minh không?), mình nghĩ một số bạn mới học VBA sẽ có thắc mắc nhiều thứ (như trong file có With ... End With, .Address(0,0) v.v...), và khai báo biến, vì thế nên mới thòng câu ...
Đúng là thiếu khai báo biến, em thử code này:
Mã:
Sub test()
Dim I As Long, Kq As Long
    For I = 1 To 5
        If Cells(I, 2) = "" Then
            MsgBox "ban chua nhap tai cell " & [COLOR=#ff0000]Cells(I, 2)[/COLOR].Address(0, 0)
            [COLOR=#ff0000]Cells(I, 2)[/COLOR].Select
            Cells(6, 2) = ""
            Exit Sub
        Else
            Kq = Kq + Cells(I, 2)
        End If
    Next
    Cells(6, 2) = Kq
End Sub
Lúc đầu không có phần màu đỏ, code không chạy được, anh Nghĩa giải thích dùm với.
 
Lần chỉnh sửa cuối:
Upvote 0
Để nói tiếp tinh thần, tôi hỏi các bạn điều sau:

Mã:
' vòng lặp kiểu FOR
dau = 1
cuoi = 10
buoc = 1
FOR i = dau to cuoi STEP buoc
' code làm cái gì đó
NEXT i

' vòng lặp kiểu WHILE
dau = 1
cuoi = 10
buoc = 1
i = dau
WHILE i <= cuoi
' code làm cái gì đó
i = i + buoc
WEND

Nếu loại trừ yếu tố tốc độ xử lý, hai vòng lặp trên có giống nhau hay không?
 
Upvote 0
Công thức của Tiến không đúng với trường hợp cuoi chia hết cho n.
Mình thử công thức như vậy, nhờ mọi người rút gọn:
Mã:
=1+INT(1000/J9)*J9+IF(MOD(1000,J9),J9,MOD(1000,J9))
Đúng là công thức trước của mình chưa chuẩn, mình chỉnh lại như sau (bỏ +1)
Đã thử test lại như trong file
 

File đính kèm

Upvote 0
Để nói tiếp tinh thần, tôi hỏi các bạn điều sau:

Mã:
' vòng lặp kiểu FOR
dau = 1
cuoi = 10
buoc = 1
FOR i = dau to cuoi STEP buoc
' code làm cái gì đó
NEXT i

' vòng lặp kiểu WHILE
dau = 1
cuoi = 10
buoc = 1
i = dau
WHILE i <= cuoi
' code làm cái gì đó
i = i + buoc
WEND

Nếu loại trừ yếu tố tốc độ xử lý, hai vòng lặp trên có giống nhau hay không?
bản chất :
Vòng lặp for --> biết trước số lần lặp
While ..Wend --> không biết trước số lần lặp
---------------------------------------------
Kết quả của 2 cú pháp trên : i = 11
---------------------------------------------
1 điểm khác biệt nữa là dùng cú pháp While... Wend ta sẽ không thể thoát khỏi vòng lặp bằng lệnh như kiểu exit do, exit for được
----------------------------------------------
 
Upvote 0
Đếm xem trong topic này có bao nhiêu thành viên mới:

Capture.JPG









Đó là chỉ VÀO XEM thôi nha!
Từ đầu đến giờ chỉ thấy có bé Tiền tham gia hỏi đáp, còn lại toàn là "cựu" thành viên
Buồn nhỉ?
 
Upvote 0
Đếm xem trong topic này có bao nhiêu thành viên mới:
Đó là chỉ VÀO XEM thôi nha!
Từ đầu đến giờ chỉ thấy có bé Tiền tham gia hỏi đáp, còn lại toàn là "cựu" thành viên
Buồn nhỉ?
Thì cũng gần 50% rồi mà Thầy, topic cũng mới (chiều nay em mới tham gia), mong là nhiều người biết đến Topic này.
 
Upvote 0
Không phải tôi bắt bẻ bạn. Cái khó là tuỳ theo cách học mà vấn đề X đối với A là cơ bản trong khi đối với B là sâu.
Tốt hơn hết là định nghĩa cái giới hạn của cơ bản trước đã.

Đối với tôi chẳng hạn, chỉ riêng việc cóp range ra array để xử lý đã thuộc về cao cấp rồi.
xem qua các bài của bạn này viết mình cảm thấy đc câu này hình như ko đúng sự thật rồi "dòng màu đỏ" quá khiêm tốn
 
Upvote 0
Đúng là thiếu khai báo biến, em thử code này:
Mã:
Sub test()
Dim I As Long, Kq As Long
    For I = 1 To 5
        If Cells(I, 2) = "" Then
            MsgBox "ban chua nhap tai cell " & [COLOR=#ff0000]Cells(I, 2)[/COLOR].Address(0, 0)
            [COLOR=#ff0000]Cells(I, 2)[/COLOR].Select
            Cells(6, 2) = ""
            Exit Sub
        Else
            Kq = Kq + Cells(I, 2)
        End If
    Next
    Cells(6, 2) = Kq
End Sub
Lúc đầu không có phần màu đỏ, code không chạy được, anh Nghĩa giải thích dùm với.

Phải khẳng định với nhau rằng, nếu ta không khai báo biến rõ ràng thì code vẫn chạy nha các bạn! Khi không khai báo biến thì biến mà ta đặt vào thủ tục mặc nhiên đó là một biến Varriant Data Type, phần biến này tôi sẽ lần lượt giới thiệu cho các bạn ở những bài sau.

Xin các cao thủ đừng bắt lỗi gì ở đoạn văn trên, vì tôi đang hướng tới cho các thành viên mới nhận thức về biến.

(Tôi gọi thành viên mới có nghĩa là tôi gọi thành viên mới tập tành về VBA chứ không phải là thành viên mới tham gia diễn đàn nhé các bạn).

Quay lại code của leonguyenz, mới tập viết code mà đã biến chuyển được từ code của anh ra như vậy là một tín hiệu đáng mừng, xin chúc mừng Thảo!

Mình nói nôm na nhé, đối với Range() chúng có các phương thức đi kèm, chẳng hạn Range().Select, Range().Address, Range().Value, Range().Offset, Range().Resize v.v...

Cells() là một "tế bào" của Range() vì thế nó có tính chất đầy đủ của một Range(), tuy nhiên với Range() có thể là 1 cell và cũng có thể là một vùng với nhiều cell, còn với Cells() thì chỉ tham chiếu đúng 1 cell không hơn không kém.

Trong code của tôi có dùng With ... End With, Khi dùng With xxx thì xxx là đại diện cho 1 Range(), 1 Cells(), 1 Object v.v... nào đó.

Tôi lấy một ví dụ một macro đã ghi lại thao tác của tôi:

Mã:
Sub Macro1()
[B]    Range("B1:B6")[COLOR=#ff0000].Select[/COLOR][/B]
    With [B][COLOR=#ff0000]Selection[/COLOR][/B].[COLOR=#0000cd]Interior[/COLOR]
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 65535
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
End Sub

Tôi chọn vùng [B1:B6] cái vùng được chọn đó được gọi là Selection

(Selection trong trường hợp này nó là một biến Range nên nó có đầy đủ tính chất của một Range nha các bạn).

Trong trường hợp với macro trên, nếu ta không dùng With thì ta viết như sau:

Mã:
Sub Macro1()
    Range("B1:B6").Select
    [COLOR=#0000cd]Selection.Interior[/COLOR].Pattern = xlSolid
    [COLOR=#0000cd]Selection.Interior[/COLOR].PatternColorIndex = xlAutomatic
    [COLOR=#0000cd]Selection.Interior[/COLOR].Color = 65535
    [COLOR=#0000cd]Selection.Interior[/COLOR].TintAndShade = 0
    [COLOR=#0000cd]Selection.Interior[/COLOR].PatternTintAndShade = 0
End Sub

Như vậy khi dùng With là như ta "rút ra thừa số chung" trong toán học để tính vậy.

Với code mà Thảo đã sửa theo ý mình, nhưng không có những cái màu đỏ thì code không chạy là vì "không có người bảo lãnh", tức không có "anh" With Range() đại diện cho những phương thức sau Range() nên chúng phát sinh ra lỗi.

Và buộc lòng phải có:

Cells(I, 2).Address(0, 0)
Cells(I, 2).Select

Nói tóm lại, Có Range() mới chạy được các phương thức sau nó, nếu các phương thức "đứng chơ vơ" thì như "rắn không đầu" sẽ phát sinh lỗi.

Không biết mình diễn giải dông dài, rườm ra như thế các bạn có hiểu không nhỉ?
 
Lần chỉnh sửa cuối:
Upvote 0
Đó là chỉ VÀO XEM thôi nha!
Từ đầu đến giờ chỉ thấy có bé Tiền tham gia hỏi đáp, còn lại toàn là "cựu" thành viên
Buồn nhỉ?


Đúng là vậy & phải thôi. Vì vài bài đầu mà các bác chơi ngay vô mảng thì đầu óc nào tiếp thu đây?

Qua nghiên cứu về FIND method thì Thầy Nghĩa quá chủ quan về

Find Next

(Đó là việc thứ hai)

Việc thứ ba thì 1 số người đến topic này để thể hiện, bày tỏ kiến thức của mình, chứ không để học hỏi hay để người khác học hỏi!

Các bạn cần bớt đi cái tôi đi, thì mục tiêu của topic mới đạt!

Thân ái & cũng xin lỗi đã làm phật ý một số người!



 
Upvote 0
Câu hỏi 5: Bằng câu lệnh nào khi chạy code sẽ thông báo lỗi khi không khai báo biến?

Như các bạn đã biết, biến là một đại diện cho một kiểu dữ liệu nào đó để ta thực hiện các thủ tục, như tôi đã nói ở bài trước:

Phải khẳng định với nhau rằng, nếu ta không khai báo biến rõ ràng thì code vẫn chạy nha các bạn! Khi không khai báo biến thì biến mà ta đặt vào thủ tục mặc nhiên đó là một biến Varriant Data Type

Khi không khai báo biến cũng không ảnh hưởng gì đến code của bạn, nhưng khi khai báo biến, chúng sẽ trở nên rõ ràng, tường minh và xác định rõ chúng thuộc loại kiểu dữ liệu loại nào.

Thế thì câu đố đặt ra là Bằng câu lệnh nào khi chạy code sẽ thông báo lỗi khi không khai báo biến? (rất dễ trả lời đấy các bạn)
 
Lần chỉnh sửa cuối:
Upvote 0
À cái này mình biết, đó là

Option Explicit
ngay dòng đầu tiên luôn

Cũng nói thêm với các bạn mới tập toẹ VBA như mình, rằng:

Sau For j = 1 to 9
ta nên
Next j

Để làm chi vậy?
Thưa rằng sau Next j ta có thể mạnh dạn xài j vô việc khác bân dưới mà ít sợ mắc lỗi

Việc này nhằm gởi đền các bậc thầy ở đây rằng:
Khi ta truyền đạt kiến thức, thì không nên dăng bãy các học trò như bọn mình, rồi . . . .
 
Upvote 0
Phải khẳng định với nhau rằng, nếu ta không khai báo biến rõ ràng thì code vẫn chạy nha các bạn! Khi không khai báo biến thì biến mà ta đặt vào thủ tục mặc nhiên đó là một biến Varriant Data Type, phần biến này tôi sẽ lần lượt giới thiệu cho các bạn ở những bài sau.

Xin các cao thủ đừng bắt lỗi gì ở đoạn văn trên, vì tôi đang hướng tới cho các thành viên mới nhận thức về biến.

(Tôi gọi thành viên mới có nghĩa là tôi gọi thành viên mới tập tành về VBA chứ không phải là thành viên mới tham gia diễn đàn nhé các bạn).

Quay lại code của leonguyenz, mới tập viết code mà đã biến chuyển được từ code của anh ra như vậy là một tín hiệu đáng mừng, xin chúc mừng Thảo!

Mình nói nôm na nhé, đối với Range() chúng có các phương thức đi kèm, chẳng hạn Range().Select, Range().Address, Range().Value, Range().Offset, Range().Resize v.v...

Cells() là một "tế bào" của Range() vì thế nó có tính chất đầy đủ của một Range(), tuy nhiên với Range() có thể là 1 cell và cũng có thể là một vùng với nhiều cell, còn với Cells() thì chỉ tham chiếu đúng 1 cell không hơn không kém.

Trong code của tôi có dùng With ... End With, Khi dùng With xxx thì xxx là đại diện cho 1 Range(), 1 Cells(), 1 Object v.v... nào đó.

Tôi lấy một ví dụ một macro đã ghi lại thao tác của tôi:

Mã:
Sub Macro1()
[B]    Range("B1:B6")[COLOR=#ff0000].Select[/COLOR][/B]
    With [B][COLOR=#ff0000]Selection[/COLOR][/B].[COLOR=#0000cd]Interior[/COLOR]
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 65535
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
End Sub

Tôi chọn vùng [B1:B6] cái vùng được chọn đó được gọi là Selection

(Selection trong trường hợp này nó là một biến Range nên nó có đầy đủ tính chất của một Range nha các bạn).

Trong trường hợp với macro trên, nếu ta không dùng With thì ta viết như sau:

Mã:
Sub Macro1()
    Range("B1:B6").Select
    [COLOR=#0000cd]Selection.Interior[/COLOR].Pattern = xlSolid
    [COLOR=#0000cd]Selection.Interior[/COLOR].PatternColorIndex = xlAutomatic
    [COLOR=#0000cd]Selection.Interior[/COLOR].Color = 65535
    [COLOR=#0000cd]Selection.Interior[/COLOR].TintAndShade = 0
    [COLOR=#0000cd]Selection.Interior[/COLOR].PatternTintAndShade = 0
End Sub

Như vậy dùng With là để ta "rút ra thừa số chung" để tính là như vậy.

Với code mà Thảo đã sửa theo ý mình, nhưng không có những cái màu đỏ thì code không chạy là vì "không có người bảo lãnh", tức không có "anh" With Range() đại diện cho những phương thức sau Range() nên chúng phát sinh ra lỗi.

Và buộc lòng phải có:

Cells(I, 2).Address(0, 0)
Cells(I, 2).Select

Nói tóm lại, Có Range() mới chạy được các phương thức sau nó, nếu các phương thức "đứng chơ vơ" thì như "rắn không đầu" sẽ phát sinh lỗi.

Không biết mình diễn giải dông dài, rườm ra như thế các bạn có hiểu không nhỉ?
tóm lại mình xin rút gọn ý anh nghĩa thế này,
with khai báo tổng thể
các phần nằm trong tổng thể đó
end with

ví dụ : with TPHCM\QUAN10
.PHUONG7
.PHUONG8
END WITH
tức là bạn đang nói tới phuong7, phuong8 của quận 10 TPHCM, nếu bạn ko có cái dòng màu đỏ trên thì ko biết nó là phường 7 phường8 của quận nào hay thành phố nào , nên nếu gửi thư sẽ bị lạc mất nên ko thể gửi (báo lỗi)
 
Upvote 0
À cái này mình biết, đó là

Option Explicit
ngay dòng đầu tiên luôn

Cũng nói thêm với các bạn mới tập toẹ VBA như mình, rằng:

Sau For j = 1 to 9
ta nên
Next j

Để làm chi vậy?
Thưa rằng sau Next j ta có thể mạnh dạn xài j vô việc khác bân dưới mà ít sợ mắc lỗi

Việc này nhằm gởi đền các bậc thầy ở đây rằng:
Khi ta truyền đạt kiến thức, thì không nên dăng bãy các học trò như bọn mình, rồi . . . .


Tôi đã xem qua nhiều bài viết của bạn, và tôi không nghĩ bạn mới tập tành viết code đâu nhỉ? Vì thế hãy để những người mới thực sự tham gia đi bạn, đừng hạ mình làm người mới biết code nha bạn!

Chẳng hạn:

Như hình thứ hai thì E2003 không thực hiện được đâu bạn à!

Bạn chỉ có thể dùng các phương thiện sau cho mã code của bạn:

- Thụt đầu dòng các khối lệnh cho thẳng cột;

- Đánh số các dòng lệnh để phân biệt các khối lệnh;

- Bò dòng hay chèn các dòng ghi chú, như

Rem Cái Này Là Vòng Lap

' Cái Này Là Bien Chung Trong Toàn Bang Tinh'

. . . . .

& Cuối cùng là các dòng trang trí như:

' * * * * *'
'** ** ** ** '
 
Lần chỉnh sửa cuối:
Upvote 0
Kiểu dữ liệu cần khai báo phải phù hợp với dữ liệu của mình để tiết kiệm bộ nhớ.
Anh nên đưa ra bảng chung về kiểu dữ liệu để người mới áp dụng cho phù hợp.

Bảng chung về kiểu dữ liệu, xem hình nhé các bạn. À, ai đã từng học lớp căn bản vừa qua cho tôi được biết các bạn có được dạy về Khai báo biến không để tiện tạo điều kiện cho các bạn thực hành nhỉ?

DataType.jpg
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Xin thưa với Sư phụ, dòng màu đỏ là không chính xác! Code đầu do nó không tính tới hàng 1 nên nếu số hàng có "Nghia" là 5 thì nó lặp đúng 5 lần chứ không phải là 6 lần như đã sửa ở code sau đâu Sư phụ!



Ta đâu cần sử dụng biến i làm gì! Miễn số vòng lặp tối thiểu bằng tổng số hàng mà chúng duyệt qua là được, đừng nhỏ hơn sẽ bị thiếu nếu dữ liệu 100% có từ "Nghia".



Chính vì thế mà em mới sửa code để tối ưu nó (sáng làm vội nên chưa check nên sau đó check lại và sửa lại), nhưng đó cũng là một phương pháp để giải toán.

Anh ptm0412 góp ý cho bạn là đúng rồi. Bạn nên dùng Do ... Loop.

Tôi hơi tò mò. Chả nhẽ bạn có thói quen biến mọi vòng Do ... Loop thành vòng For ... Next?
Anh ptm0412 đã buông tha cho bạn nhưng tôi thử "đeo bám" chút.

Dùng Do ... Loop hay For ... Next trong trường hợp bài này thì đều phải kiểm tra đk để ra khỏi vòng lặp. Tức thao tác kiểm tra đk là như nhau. Nhưng với Do ... Loop thì chỉ có duy nhất thao tác đã nói ở trên, nếu đk không thỏa thì lại "chơi" tiếp vòng mới. Còn với For ... Next thì bạn hãy để ý là khi không thực hiện Exit For thì sao? Thì sẽ có 2 thao tác phải làm thêm:

1. Tăng i thêm 1
2. Kiểm tra xem i có vượt quá cận trên không.

Như vậy nếu có 1000 ô có Nghia thì sẽ có 2000 thao tác thêm so với dùng Do ... Loop. Nếu có 10000 ô Nghia thì có 20000 thao tác phải làm thêm. Những thao tác đó cần nhiều hay ít "điện nước" tôi không bàn nhưng rõ ràng đó là những thao tác thừa. Mà bạn đang tiết kiệm thời gian từng "chớp mắt", đúng không?

Mà bài #49 code hơi lủng củng. Trước hết phải cho Find ra khỏi vòng lặp. Find chỉ gọi 1 lần mà thôi. Chả nhẽ vòng lặp chạy 1000 lần (có 1000 ô Nghia) thì thực hiện Find 1000 lần? Bạn hãy thử dùng Do ... Loop. Chắc chắn code sẽ "đẹp" hơn.
 
Upvote 0

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

Back
Top Bottom