Cần giúp tối ưu hóa Code vòng lặp (Để Delete các dòng khi thỏa mãn điều kiện) (2 người xem)

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

hung2412

Thành viên tích cực
Tham gia
5/8/08
Bài viết
934
Được thích
240
Giới tính
Nam
Chào các bạn GPE!
Nhờ các bạn giúp đỡ cho tôi vấn đề này với, cụ thể như sau:
Dọc theo cột A có các ô chứa ký tự được phân cách như theo hình vẽ:
1.jpg
Nếu ô nào ở cột A có 2 ký tự "CT" ở bên trái ngoài cùng trong ô thì: Delete các dòng tính từ dòng chứa ô có 2 ký tự "CT" ở bên trái ngoài cùng trong ô đến xuống dòng kế bên trên dòng chứa ô có ký tự ở dưới.
Ví dụ: ô A6 có 2 ký tự "CT" ở bên trái ngoài cùng trong ô => Thỏa mãn điều kiện để Delete => Delete các dòng từ dòng 6 (Dòng này chứa ô A6) đến dòng 11 (Dòng này nằm ngay kế bên trên ô A12 có chứa ký tự).
=> Tôi xài Code như vầy:
PHP:
Sub Delete()
Dim t As Long
For t = [A65536].End(xlUp).Row To 6 Step -1
        If Left(Cells(t, 1), 2) = "CT" Then
           Range(Cells(t, 1), Cells(t, 1).End(xlDown).Offset(-1, 0)).EntireRow.Delete
        End If
Next
End Sub
=> Vấn đề được đặt ra ở đây là: Code trên duyệt từng ô từ dưới lên trên rất mất thời gian => Có Code vòng lặp nào duyệt trực tiếp các ô có chứa ký tự để xét có 2 ký tự "CT" ở bên trái ngoài cùng trong ô không? (Nghĩa là duyệt ô "nhảy cóc" từ ô chứa ký tự bên dưới đến ô chứa ký tự ngay bên trên, "bỏ qua" không duyệt các ô trống)
Mong các bạn GPE chỉ giáo!
Trân trọng cảm ơn!
P/s: Có 01 File đính kèm.
 

File đính kèm

  • 1.xls
    1.xls
    103.5 KB · Đọc: 57
Lần chỉnh sửa cuối:
Chào các bạn GPE!
Nhờ các bạn giúp đỡ cho tôi vấn đề này với, cụ thể như sau:
Dọc theo cột A có các ô chứa ký tự được phân cách như theo hình vẽ:
View attachment 133032
Nếu ô nào ở cột A có 2 ký tự "CT" ở bên trái ngoài cùng trong ô thì: Delete các dòng tính từ dòng chứa ô có 2 ký tự "CT" ở bên trái ngoài cùng trong ô đến xuống dòng kế bên trên dòng chứa ô có ký tự ở dưới.
Ví dụ: ô A6 có 2 ký tự "CT" ở bên trái ngoài cùng trong ô => Thỏa mãn điều kiện để Delete => Delete các dòng từ dòng 6 (Dòng này chứa ô A6) đến dòng 11 (Dòng này nằm ngay kế bên trên ô A12 có chứa ký tự).
=> Tôi xài Code như vầy:
PHP:
Sub Delete()
Dim t As Long
For t = [A65536].End(xlUp).Row To 6 Step -1
        If Left(Cells(t, 1), 2) = "CT" Then
           Range(Cells(t, 1), Cells(t, 1).End(xlDown).Offset(-1, 0)).EntireRow.Delete
        End If
Next
End Sub
=> Vấn đề được đặt ra ở đây là: Code trên duyệt từng ô từ dưới lên trên rất mất thời gian => Có Code vòng lặp nào duyệt trực tiếp các ô có chứa ký tự để xét có 2 ký tự "CT" ở bên trái ngoài cùng trong ô không? (Nghĩa là duyệt ô "nhảy cóc" từ ô chứa ký tự bên dưới đến ô chứa ký tự ngay bên trên, "bỏ qua" không duyệt các ô trống)
Mong các bạn GPE chỉ giáo!
Trân trọng cảm ơn!
P/s: Có 01 File đính kèm.
Mình thì có thói quen xóa kiểu này
PHP:
Sub xoa()
Dim data(), Res(), i, j, k
data = Range("A6", [H65536].End(3)).Value
ReDim Res(1 To UBound(data), 1 To UBound(data, 2))
For i = 1 To UBound(data)
   If data(i, 1) <> "" Then
      If Left(data(i, 1), 2) <> "CT" Then
         Do
            k = k + 1
            For j = 1 To UBound(data, 2)
               Res(k, j) = data(i, j)
            Next
            i = i + 1
         Loop Until Left(data(i, 1), 2) = "CT" Or i >= UBound(data)
      End If
   End If
Next
[I6].Resize(k, UBound(data)) = Res
End Sub
 
Upvote 0
...
=> Vấn đề được đặt ra ở đây là: Code trên duyệt từng ô từ dưới lên trên rất mất thời gian => Có Code vòng lặp nào duyệt trực tiếp các ô có chứa ký tự để xét có 2 ký tự "CT" ở bên trái ngoài cùng trong ô không? (Nghĩa là duyệt ô "nhảy cóc" từ ô chứa ký tự bên dưới đến ô chứa ký tự ngay bên trên, "bỏ qua" không duyệt các ô trống)
Mong các bạn GPE chỉ giáo!
Trân trọng cảm ơn!
P/s: Có 01 File đính kèm.

Code này viết theo đúng ý bạn: Duyệt từ trên xuống; không duyệt các ô trống... và đương nhiên chỉ phục vụ riêng cho bài này.
(với điều kiện khác thì tôi nghĩ bạn đã biết cách sửa code để vận dụng).
Mã:
Sub Macro1()
    On Error Resume Next
    With Range("a6:a" & [c65000].End(3).Row).SpecialCells(4)
        For i = 1 To .Areas.Count
            If Left(.Areas(i)(0), 2) = "CT" Then .Areas(i).EntireRow.Delete
        Next
    End With
End Sub
 
Upvote 0
Mình thì có thói quen xóa kiểu này
PHP:
Sub xoa()
Dim data(), Res(), i, j, k
data = Range("A6", [H65536].End(3)).Value
ReDim Res(1 To UBound(data), 1 To UBound(data, 2))
For i = 1 To UBound(data)
   If data(i, 1) <> "" Then
      If Left(data(i, 1), 2) <> "CT" Then
         Do
            k = k + 1
            For j = 1 To UBound(data, 2)
               Res(k, j) = data(i, j)
            Next
            i = i + 1
         Loop Until Left(data(i, 1), 2) = "CT" Or i >= UBound(data)
      End If
   End If
Next
[I6].Resize(k, UBound(data)) = Res
End Sub
Tôi đã text Code của bạn => Nhưng bị Debug tại dòng:
PHP:
[I6].Resize(k, UBound(data)) = Res
=> Không hiểu là tại làm sao.
Nếu có thể, bạn Up File của bạn đã làm, có được chăng?
 
Upvote 0
Code này viết theo đúng ý bạn: Duyệt từ trên xuống; không duyệt các ô trống... và đương nhiên chỉ phục vụ riêng cho bài này.
(với điều kiện khác thì tôi nghĩ bạn đã biết cách sửa code để vận dụng).
Mã:
Sub Macro1()
    On Error Resume Next
    With Range("a6:a" & [c65000].End(3).Row).SpecialCells(4)
        For i = 1 To .Areas.Count
            If Left(.Areas(i)(0), 2) = "CT" Then .Areas(i).EntireRow.Delete
        Next
    End With
End Sub
Cảm ơn bạn, nhưng có sự sơ xuất là: Code của bạn không Delete dòng chứa ô ở cột A có 2 ký tự "CT" ở bên trái ngoài cùng trong ô => Chỉ Delete các dòng trống bên dưới thôi ạ. Bạn thử mà xem.
 
Upvote 0
Code của chàng 2uang Hải chạy trên E2003 sẽ báo lỗi.

Code của chàng Trung Chính nhanh hơn của 2uang Hải.

Với dữ liệu trong file thì code này cũng có thể cạnh tranh được với chàng 2uang Hải:

PHP:
Option Explicit
Dim Tmr As Double
Sub TìmVàXoá()
 Dim Rng As Range, sRng As Range, dRg As Range
 Dim fAdd As String
 
 Tmr = Timer()
 [d5].End(xlDown).Offset(, -3).Value = "GPE.COM"
 Set Rng = Range([A5], [a65500].End(xlUp))
 Set sRng = Rng.Find("CT", , xlFormulas, xlPart)
 Set dRg = Rows("65501:65501")
 If Not sRng Is Nothing Then
    fAdd = sRng.Address
    Do
        If Left(sRng.Value, 2) = "CT" Then _
            Set dRg = Union(Rows(sRng.Row & ":" & sRng.Offset(1, 2).End(xlDown).Row), dRg)
        Set sRng = Rng.FindNext(sRng)
    Loop While Not sRng Is Nothing And sRng.Address <> fAdd
 End If
 [f1].Value = Timer() - Tmr
 If Not dRg Is Nothing Then MsgBox dRg.Address
End Sub
 
Upvote 0
Tôi đã text Code của bạn => Nhưng bị Debug tại dòng:
PHP:
[I6].Resize(k, UBound(data)) = Res
=> Không hiểu là tại làm sao.

Sửa dòng đó thành

[I6].Resize(k, UBound(data, 2)) = Res

là hết lỗi, còn chuyện khác nữa thì bạn xét thêm!
 
Upvote 0
PHP:
Option Explicit
Dim Tmr As Double
Sub TìmVàXoá()
 Dim Rng As Range, sRng As Range, dRg As Range
 Dim fAdd As String
 
 Tmr = Timer()
 [d5].End(xlDown).Offset(, -3).Value = "GPE.COM"
 Set Rng = Range([A5], [a65500].End(xlUp))
 Set sRng = Rng.Find("CT", , xlFormulas, xlPart)
 Set dRg = Rows("65501:65501")
 If Not sRng Is Nothing Then
    fAdd = sRng.Address
    Do
        If Left(sRng.Value, 2) = "CT" Then _
            Set dRg = Union(Rows(sRng.Row & ":" & sRng.Offset(1, 2).End(xlDown).Row), dRg)
        Set sRng = Rng.FindNext(sRng)
    Loop While Not sRng Is Nothing And sRng.Address <> fAdd
 End If
 [f1].Value = Timer() - Tmr
 If Not dRg Is Nothing Then MsgBox dRg.Address
End Sub
Cảm ơn bạn. Tôi chạy Code của bạn thì nó hiện ra cái Msg Box này (Chỉ rõ những dòng cần xóa):
1.jpg
=> Nhấn OK => Vẫn giữ nguyên (Nghĩa là không Delete, không làm chi cả) => Phải làm sao bây giờ hả bạn?
 
Upvote 0
Chẳng hiểu sao lại [I6].Resize(k, UBound(data)) = Res mà bỏ rơi số 2 đâu mất
Mình vẫn khoái thuật toán đem hết lên trời, tính toán xong xuôi đập 1 phát xuống sheet mới sướng tay, trừ khi phải định dạng dữ liệu thì đành chịu.
Sử dụng Union sẽ xóa dòng cũng nhanh như xử lý trên mảng, tuy nhiên khi vượt quá đối số giới hạn thì bị lỗi
 
Upvote 0
Cảm ơn bạn. Tôi chạy Code của bạn thì nó hiện ra cái Msg Box này (Chỉ rõ những dòng cần xóa):

=> Nhấn OK => Vẫn giữ nguyên (Nghĩa là không Delete, không làm chi cả) => Phải làm sao bây giờ hả bạn?

Vì chưa có lệnh để nó delete;
Dổi dòng lệnh đó lại vầy:
PHP:
If Not dRg Is Nothing Then dRg.Delete
 
Upvote 0
Cảm ơn bạn, nhưng có sự sơ xuất là: Code của bạn không Delete dòng chứa ô ở cột A có 2 ký tự "CT" ở bên trái ngoài cùng trong ô => Chỉ Delete các dòng trống bên dưới thôi ạ. Bạn thử mà xem.

Không phải sơ xuất mà do không đọc kỹ bài cứ nghĩ là không xóa dòng đó

Vậy sửa lại code như sau:
Mã:
Sub Macro1()
    On Error Resume Next
    With Range("a6:a" & [c65000].End(3).Row).SpecialCells(4)
        For i = 1 To .Areas.Count
            If Left(.Areas(i)(0), 2) = "CT" Then .Areas(i)(0).Resize(.Areas(i).Rows.Count + 1).EntireRow.Delete
        Next
    End With
End Sub
 
Upvote 0
Không phải sơ xuất mà do không đọc kỹ bài cứ nghĩ là không xóa dòng đó

Vậy sửa lại code như sau:
Mã:
Sub Macro1()
    On Error Resume Next
    With Range("a6:a" & [c65000].End(3).Row).SpecialCells(4)
        For i = 1 To .Areas.Count
            If Left(.Areas(i)(0), 2) = "CT" Then .Areas(i)(0).Resize(.Areas(i).Rows.Count + 1).EntireRow.Delete
        Next
    End With
End Sub
Cảm ơn bạn, Code này đơn giản mà hiệu quả không kém gì so với Code "hầm hố" của các bạn kia. Nếu không phiền, bạn có thể giải thích từng dòng lệnh, có được chăng?
 
Upvote 0
Code này cũng là một cách:
PHP:
Sub Delete()
[A6].Select
Vonglap:
   If Left(ActiveCell, 2) = "CT" Then
       Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
   Else
      ActiveCell.End(xlDown).Select
            If ActiveCell.Row = 65536 Then
                GoTo Thoat
            End If
   End If
GoTo Vonglap
Thoat:
End Sub
 
Upvote 0
PHP:
Option Explicit
Dim i As Long
 Sub Macro1()
    On Error Resume Next
    With Range("A6:A" & [A65000].End(xlUp).Row).SpecialCells(4)
        For i = 1 To .Areas.Count
            If Left(.Areas(i)(0), 2) = "CT" Then .Areas(i)(0).Resize(.Areas(i).Rows.Count + 1).EntireRow.Delete
        Next
    End With
    On Error GoTo 0
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Code này cũng là một cách hay:
PHP:
Sub Delete()
[A5].Select
Vonglap:
If Not Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole) Is Nothing Then
          Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole).Activate
          Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
Else
          GoTo Thoat
End If
GoTo Vonglap
Thoat:
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Code này cũng là một cách hay:
PHP:
Sub Delete()
[A5].Select
Vonglap:
If Not Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole) Is Nothing Then
          Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole).Activate
          Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
Else
          GoTo Thoat
End If
GoTo Vonglap
Thoat:
End Sub
trong lập trình người ta hạn chế dùng goto, rất khó kiểm soát
 
Upvote 0
Code vòng vo thế mà cũng gọi là hay.
Nếu muốn dùng goto thì đâu có cần cái label Thoat.
Bỏ hẳn luôn phần else, đem cái End If đặt sau Goto VongLap
 
Upvote 0
Code vòng vo thế mà cũng gọi là hay.
Nếu muốn dùng goto thì đâu có cần cái label Thoat.
Bỏ hẳn luôn phần else, đem cái End If đặt sau Goto VongLap
Cảm ơn bạn đã giúp đỡ, đúng là gọn đi thật. Mà Code này hay ít ra đối với tôi. Mỗi người có phương pháp riêng mà => Phương pháp của ai thì người đó sẽ bảo là phương pháp của tôi là hay mà (Có gì lạ đâu).
PHP:
Option Explicit
Sub Delete()
[A5].Select
Vonglap:
If Not Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole) Is Nothing Then
         Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole).Activate
         Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
GoTo Vonglap
End If
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
(hắc là thoát để fòng ngừa (chưa đến nơi đến chốn) khi tìm không ra ô nào có dữ liệu cần tìm.

Thực ra macro này đã có ở bài viết bên trên, vã lại nó chưa chặc chẽ bằng.
 
Upvote 0
... Mỗi người có phương pháp riêng mà => Phương pháp của ai thì người đó sẽ bảo là phương pháp của tôi là hay mà (Có gì lạ đâu)....

Khi bạn đạt đến trình độ kha khá rồi thì bạn sẽ tự biết là câu này rất buồn cười.
 
Upvote 0
Bỏ hẳn If và End If:
PHP:
Sub Delete()
[A5].Select
Vonglap:
              On Error GoTo Thoat
              Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole).Activate
              Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
GoTo Vonglap
Thoat:
End Sub
 
Upvote 0
Bỏ hẳn If và End If:
PHP:
Sub Delete()
[A5].Select
Vonglap:
              On Error GoTo Thoat
              Range(ActiveCell, [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole).Activate
              Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
GoTo Vonglap
Thoat:
End Sub
Nếu nói về ngắn gọn nhất thì mình là 1 trong những người có thể đấy.
Nhưng ngắn gọn không phải là thể hiện khả năng và thuật toán đâu
Ai có thể ngắn hơn?
PHP:
Sub Shortest()
On Error Resume Next
Range([A:A].Find("CT*"), [A:A].Find("CT*").End(4)(-0)).EntireRow.Delete
End Sub
 
Upvote 0
Nếu nói về ngắn gọn nhất thì mình là 1 trong những người có thể đấy.
Nhưng ngắn gọn không phải là thể hiện khả năng và thuật toán đâu
Ai có thể ngắn hơn?
PHP:
Sub Shortest()
On Error Resume Next
Range([A:A].Find("CT*"), [A:A].Find("CT*").End(4)(-0)).EntireRow.Delete
End Sub
Hình như không ổn bạn ơi, Code trên xóa dòng tiêu đề rồi đến...End Sub. Bạn thử mà xem.
 
Upvote 0
Hình như không ổn bạn ơi, Code trên xóa dòng tiêu đề rồi đến...End Sub. Bạn thử mà xem.
Vì không có vòng lặp thì đương nhiên nó mần chỉ có 1 lần rồi nghỉ. Chỉ cần cho nó cái Do Loop vào là nó chạy tới hết.
PHP:
Sub Shortest()
Do While Not [A:A].Find("CT*") Is Nothing
   Range([A:A].Find("CT*"), [A:A].Find("CT*").End(4)(-0)).EntireRow.Delete
Loop
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Vì không có vòng lặp thì đương nhiên nó mần chỉ có 1 lần rồi nghỉ. Chỉ cần cho nó cái Do Loop vào là nó chạy tới hết.
PHP:
Sub Shortest()
Do While Not [A:A].Find("CT*") Is Nothing
   Range([A:A].Find("CT*"), [A:A].Find("CT*").End(4)(-0)).EntireRow.Delete
Loop
End Sub
Code này hay, tuy nhiên nó Delete luôn dòng tiêu đề bạn ạ.
 
Upvote 0
...
Nhưng ngắn gọn không phải là thể hiện khả năng và thuật toán đâu
...

Tại chủ thớt chỉ đề cập đến hai từ "tối ưu", nhưng không xác định điều kiện của tối ưu.
Tối ưu là một từ có tính chất tương đối và chủ quan. Tuỳ theo mức độ nhu cầu mà điểm tối ưu được đánh giá khác nhau.
Nếu cần khoe code thì đặt nặng trên số dòng code.
Nếu cần chạy nhanh thì đặt nặng trên cấu trúc dữ liệu.
Nếu cần tổng quát thì đặt nặng trên cấu trúc của yêu cầu.
Nếu cần... có cả đống khía cạnh.

Một ví dụ của tổng quát, và code dài sọc.

Mã:
Sub t()
[COLOR=#008000]' code delete những dòng có cột A bắt đầu bằng "CT" cùng các dòng phụ thuộc của chúng
' dòng phụ thuộc được định nghĩa là các dòng kế tiếp không có trị ở cột A này.
[/COLOR]Dim c As Range, firstAddress As String
Dim a As String, rgA As String
With Range("a5", Range("A65536").End(xlUp))
    Set c = .Find("CT*", lookat:=xlWhole)
    If Not c Is Nothing Then
        firstAddress = c.Address
        Do
            a = a & "," & Range(c, c.End(xlDown).Offset(-1)).Address(False, False)
            Set c = .FindNext(c)
        Loop While Not c Is Nothing And c.Address <> firstAddress
    End If
End With
If Len(a) > 0 Then Range(Mid(a, 2)).EntireRow.Delete
End Sub
 
Upvote 0
Một ví dụ của tổng quát, và code dài sọc.

Nếu nói dài lòng thòng thì bài 2 cũng thuộc hạng có cỡ là dài đó. Dài nhưng tốc độ thì đáng nể. Cho nên cái gì cần dài thì nên dài 1 chút. Nhưng dài quá thì cũng hơi khó coi vì hỏng giống ai.
 
Upvote 0
Vì không có vòng lặp thì đương nhiên nó mần chỉ có 1 lần rồi nghỉ. Chỉ cần cho nó cái Do Loop vào là nó chạy tới hết.
PHP:
Sub Shortest()
Do While Not [A:A].Find("CT*") Is Nothing
   Range([A:A].Find("CT*"), [A:A].Find("CT*").End(4)(-0)).EntireRow.Delete
Loop
End Sub
Code này Delete luôn những ô nào có 2 ký tự "CT" bất kể nó nằm ở chỗ nào. Không ổn bạn ạ.
 
Upvote 0
Thường thì kết quả là quan tâm trước tiên => Đến tốc độ chạy Code => Cấu trúc Code đơn giản, gọn nhẹ:
[GPECODE1=vb]Sub Delete()
Do While Not Range([A5], [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole) Is Nothing
Range([A5], [A65536].End(xlUp)).Find("CT*", LookAt:=xlWhole).Activate
Range(ActiveCell, ActiveCell.End(xlDown).Offset(-1, 0)).EntireRow.Delete
Loop
End Sub[/GPECODE1]
 
Upvote 0
Code này Delete luôn những ô nào có 2 ký tự "CT" bất kể nó nằm ở chỗ nào. Không ổn bạn ạ.
Mình nghĩ là chỉ cần thêm mấy cái dấu phẩy. Đoán thế chứ không thử code vì chẳng có xài chỗ nào được cho dạng code ví dụ giả sử...
PHP:
Sub Shortest()
Do While Not [A:A].Find("CT*", , , 1) Is Nothing
   Range([A:A].Find("CT*"), [A:A].Find("CT*").End(4)(-0)).EntireRow.Delete
Loop
End Sub
 
Upvote 0

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

Back
Top Bottom