Hỗ trợ hàm UDF thay đổi giá trị cuối cùng trong chuỗi

Liên hệ QC

Nguyenhoangphong0902

Đường trần muôn vạn ngã ba.........
Tham gia
27/7/21
Bài viết
56
Được thích
22
Em chào anh chị. Em có bài tập thực tế nhờ anh chị hỗ trợ. Bình thường em làm là tách dữ liệu ra rồi dùng CTRL + H => thay đổi X thành x, và thay đổi MM thành mm. Nhưng chỉ thay đổi dữ liệu cuối cùng thôi. Em phải làm thủ công mấy chập thì mới ra được kết quả mong muốn. Em mong muốn viết lại bằng VBA nhưng không viết bằng Sub mà phải viết bằng Function để tạo hàm UDF như kết quả trong file. Em cảm ơn anh chị.
 

File đính kèm

  • GPE.JPG
    GPE.JPG
    144.9 KB · Đọc: 24
  • Nhờ hỗ trợ tạo hàm UDF bằng Function.xlsb
    10.3 KB · Đọc: 15
Em chào anh chị. Em có bài tập thực tế nhờ anh chị hỗ trợ. Bình thường em làm là tách dữ liệu ra rồi dùng CTRL + H => thay đổi X thành x, và thay đổi MM thành mm. Nhưng chỉ thay đổi dữ liệu cuối cùng thôi. Em phải làm thủ công mấy chập thì mới ra được kết quả mong muốn. Em mong muốn viết lại bằng VBA nhưng không viết bằng Sub mà phải viết bằng Function để tạo hàm UDF như kết quả trong file. Em cảm ơn anh chị.
Theo hình của bạn thì điều kiện "dữ liệu cuối cùng" chỉ đúng cho MM thôi, không đúng cho X.
Cần xác định rõ ràng muốn gì.

1633954118604.png
 
Upvote 0
Em chào anh chị. Em có bài tập thực tế nhờ anh chị hỗ trợ. Bình thường em làm là tách dữ liệu ra rồi dùng CTRL + H => thay đổi X thành x, và thay đổi MM thành mm. Nhưng chỉ thay đổi dữ liệu cuối cùng thôi. Em phải làm thủ công mấy chập thì mới ra được kết quả mong muốn. Em mong muốn viết lại bằng VBA nhưng không viết bằng Sub mà phải viết bằng Function để tạo hàm UDF như kết quả trong file. Em cảm ơn anh chị.
Dùng hàm code dỏm dỏm này (có lẽ biết dùng RegEx thì hay hơn)
Rich (BB code):
Function GPE(strX As String)
Dim iNo&, iStop&
    iNo = 1: iStop = InStrRev(strX, "X") + 1
    If InStr(iNo, strX, "X", vbBinaryCompare) Then
        Do
            iNo = InStr(iNo, strX, "X", vbBinaryCompare) + 1
            If IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) - 1, 1)) And IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) + 1, 1)) Then
                strX = Left(strX, iNo - 2) & "x" & Right(strX, Len(strX) - iNo + 1)
            End If
        Loop Until iNo = iStop
        strX = Left(strX, Len(strX) - 2) & "mm"
    End If
    GPE = strX
End Function
 
Upvote 0
Dùng hàm code dỏm dỏm này (có lẽ biết dùng RegEx thì hay hơn)
Rich (BB code):
Function GPE(strX As String)
Dim iNo&, iStop&
    iNo = 1: iStop = InStrRev(strX, "X") + 1
    If InStr(iNo, strX, "X", vbBinaryCompare) Then
        Do
            iNo = InStr(iNo, strX, "X", vbBinaryCompare) + 1
            If IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) - 1, 1)) And IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) + 1, 1)) Then
                strX = Left(strX, iNo - 2) & "x" & Right(strX, Len(strX) - iNo + 1)
            End If
        Loop Until iNo = iStop
        strX = Left(strX, Len(strX) - 2) & "mm"
    End If
    GPE = strX
End Function
Em cảm ơn Maika 8008 nhiều. Mà cho em hỏi RegEx là gì, có phải là hàm Regexextract của Google Sheet không ạ. Nếu sử dụng Regexextract trong trường hợp này thì dùng như thế nào anh. Người ta nói: 1 bài toán luôn có nhiều cách giải, và em thì rất muốn biết nhiều cách giải. Cảm ơn anh.
 
Upvote 0
Em cảm ơn Maika 8008 nhiều. Mà cho em hỏi RegEx là gì, có phải là hàm Regexextract của Google Sheet không ạ. Nếu sử dụng Regexextract trong trường hợp này thì dùng như thế nào anh. Người ta nói: 1 bài toán luôn có nhiều cách giải, và em thì rất muốn biết nhiều cách giải. Cảm ơn anh.
RegEx viết tắt của Regular Expression là một mẫu được tạo thành từ một chuỗi các ký tự, có thể sử dụng để tìm một mẫu phù hợp trong một chuỗi khác. Để sử dụng Regex trong VBA, sử dụng đối tượng RegExp với khai báo CreateObject("VBScript.RegExp")
 
Upvote 0
Dùng hàm code dỏm dỏm này (có lẽ biết dùng RegEx thì hay hơn)
Rich (BB code):
Function GPE(strX As String)
...
End Function
Công việc khá giản dị. Tội gì phải lôi cả cỗ máy Regex vào. Code và dài vừa rắc rối hơn.

Hàm GPE ở trên cho kết quả sai ở trường hợp sau:

1633958750083.png
 
Upvote 0
Upvote 0
...Function GPE(strX As String)
Dim iNo&, iStop&
iNo = 1: iStop = InStrRev(strX, "X") + 1
If InStr(iNo, strX, "X", vbBinaryCompare) Then
Do
iNo = InStr(iNo, strX, "X", vbBinaryCompare) + 1
If IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) - 1, 1)) And IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) + 1, 1)) Then
strX = Left(strX, iNo - 2) & "x" & Right(strX, Len(strX) - iNo + 1)
End If
Loop Until iNo = iStop
strX = Left(strX, Len(strX) - 2) & "mm"
End If
GPE = strX
End Function
Bạn cũng cần cẩn thận về tham số hàm. Tham kiểu String mặc định kiểu truyền là ByRef. Bên trong hàm bạn đổi trị của nó là cái tham nạp sẽ bị đổi.
Code của bạn bắt buộc phải dùng tham ByVal.

Chú thêm:
Để sử dụng RegEx. Môi trường VBA phải có reference tới thư viện của Script (Microsoft VBScript Regular Expressions).
Tuy nhiên, VBA cũng có cách cho kết nối trễ qua hàm CreateObject. Với cách kết nối trễ này, code gọi hàm CreateObject để tạo một đối tượng COM (Compoment Object Model). Tạo như vậy tốn một mớ tài nguyên. Nếu bộ nhớ nhiều thì sau khi tạo xong, code của đối tượng này nằm luôn trong bộ nhớ, khi cần lại chỉ việc kết nối lại. Nếu bộ nhớ ít thì có thể lúc xài xong code có thể bị đẩy ra khỏi bộ nhớ và khi cần phải load lại.
Nói túm lại, dùng RegEx ở những trường hợp đơn giản như đề bài này là không hiệu quả.
 
Upvote 0
Không hiểu sao bạn lại thích viết UDF, chứ UDF là một trong những nguyên nhân làm cho ứng dụng chạy chậm nếu các hàm UDF được sử dụng theo cách Fill.
Bạn có thể tham khảo code:

PHP:
Function ReplaceE(ByVal Text)
  Dim l, s
  l = Len(Text)
  If l >= 2 Then
    Text = left(Text, l - 2) & VBA.Replace(Text, "mm", "mm", l - 1, 1, 1)
    s = Split(Text, " ")
    s(UBound(s)) = VBA.Replace(s(UBound(s)), "x", "x", , , 1)
    Text = join(s, " ")
  End If
   ReplaceE = Text
End Function

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

Dưới đây là một UDF Dynamic Array:
Bạn chỉ cần gõ một hàm duy nhất cho cả mảng như sau: =S_ReplaceE(A2:A20000)


PHP:
Option Explicit

Private Type TypeArguments
  Action As Long
  Cells As Excel.Range
  Caller As Range
  Formula As String
End Type

#If VBA7 Then
  Private Declare PtrSafe Function SetTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long
  Private Declare PtrSafe Function KillTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long) As Long
#End If
''///////////////////////////////////////////////////////
#If VBA7 And Win64 Then
  Private gTimerID As LongPtr
#Else
  Private gTimerID As Long
#End If
''///////////////////////////////////////////////////////
Private Works() As TypeArguments


Function S_ReplaceE(ByVal Cells As Range)
  On Error Resume Next
  S_ReplaceE = ReplaceE(Cells(1, 1).Value)
  Dim r As Object, k%, n%, i%, s$, f$
  s = Cells.Address(0, 0)
  Set r = Application.Caller
  f = r.Formula
  k = UBound(Works)
  k = k + 1
  ReDim Preserve Works(1 To k)
  With Works(k)
    Set .Caller = r
    Set .Cells = Cells
    .Formula = f
  End With
n:
  Set r = Nothing
  If gTimerID = 0 Then
    gTimerID = SetTimer(0&, 0&, 0, AddressOf S_ReplaceE_callback)
  End If
  On Error GoTo 0
End Function

Private Sub S_ReplaceE_callback()
  On Error Resume Next
  Call KillTimer(0&, gTimerID)
  gTimerID = 0
  S_ReplaceE_working
  On Error GoTo 0
End Sub

Private Sub S_ReplaceE_working()
 
  On Error Resume Next
  Dim UB As Integer, a As Object, b As TypeArguments, i&, k&, su As Boolean, Ac As Boolean, v As Variant
  UB = UBound(Works)
  Dim s$
  For i = 1 To UB
    b = Works(i)
    Select Case b.Action
    Case 0
      If b.Caller.Formula = b.Formula Then
        If a Is Nothing Then
          Set a = b.Cells.Parent.Parent.Parent
          su = a.ScreenUpdating
          Ac = a.Calculation
          If su Then a.ScreenUpdating = False
          If Ac = xlCalculationAutomatic Then a.Calculation = xlCalculationManual
        End If

        Works(i).Action = 1
        Dim r, c, c2, d
       
        c = b.Cells.Rows.Count
        r = b.Cells(c + 10, 1).End(3).Row - b.Cells(2, 1).Row + 1
        If r > 0 Then
          c = r
          d = b.Cells(2, 1).Resize(c + 1000, b.Cells.Columns.Count).Value
          For r = 1 To c
            For c2 = 1 To UBound(d, 2)
              d(r, c2) = ReplaceE(d(r, c2))
            Next
          Next
          b.Caller(2, 1).Resize(UBound(d), UBound(d, 2)).Value = d
        End If
      Else
        Works(i).Action = 3
      End If
      k = k + 1
    End Select
n:
  Next
  If k >= UB Then
    Erase Works
  End If
  If Not a Is Nothing Then
    If su And a.ScreenUpdating <> su Then
      a.ScreenUpdating = su
    End If
    If Ac = xlCalculationAutomatic And Ac <> a.Calculation Then
      a.Calculation = Ac
    End If
    Set a = Nothing
  End If
  On Error GoTo 0
End Sub

Function ReplaceE(ByVal Text)
  Dim l, s
  l = Len(Text)
  If l >= 2 Then
    Text = Left(Text, l - 2) & VBA.Replace(Text, "mm", "mm", l - 1, 1, 1)
    s = Split(Text, " ")
    s(UBound(s)) = VBA.Replace(s(UBound(s)), "x", "x", , , 1)
    Text = Join(s, " ")
  End If
   ReplaceE = Text
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Em cảm ơn Maika 8008 nhiều. Mà cho em hỏi RegEx là gì, có phải là hàm Regexextract của Google Sheet không ạ. Nếu sử dụng Regexextract trong trường hợp này thì dùng như thế nào anh. Người ta nói: 1 bài toán luôn có nhiều cách giải, và em thì rất muốn biết nhiều cách giải. Cảm ơn anh.
Qua góp ý của tác giả bài #6 và #8, tôi sửa lại code 1 chút:
Rich (BB code):
Function GPE(ByVal strX As String)
Dim iNo&, iStop&
    iNo = 1: iStop = InStrRev(strX, "X") + 1
    If InStr(iNo, strX, "X", vbBinaryCompare) Then
        Do
            iNo = InStr(iNo, strX, "X", vbBinaryCompare) + 1
            If IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) - 1, 1)) And IsNumeric(Mid(strX, InStr(iNo - 1, strX, "X", vbBinaryCompare) + 1, 1)) Then
                strX = Left(strX, iNo - 2) & "x" & Right(strX, Len(strX) - iNo + 1)
            End If
        Loop Until iNo = iStop
        If Ucase(Right(Trim(strX),2)) = "MM" Then
            strX = Left(Trim(strX), Len(Trim(strX)) - 2) & "mm"
        End If
    End If
    GPE = strX
End Function
 
Upvote 0
Cũng xin được mọi người cho ý kiến về hàm bên dưới.
Hàm xác định vị trí ký tự cuối cùng là số --> đổi toàn bộ các ký tự phía sau về ký tự thường
Đối với "X", chuyển về thường nếu phía trước và phía sau ký tự "X" đó là số
PHP:
Function LowerCharacter(SourceString As String)
    Dim I As Integer
    
    For I = Len(SourceString) To 1 Step -1
        If IsNumeric(Mid(SourceString, I, 1)) Then
            Mid(SourceString, I + 1) = LCase(Mid(SourceString, I + 1))
            Exit For
        End If
    Next I
    
    For I = Len(SourceString) To 1 Step -1
        If Mid(SourceString, I, 1) = "X" Then
            If IsNumeric(Mid(SourceString, I - 1, 1)) And IsNumeric(Mid(SourceString, I + 1, 1)) Then
                Mid(SourceString, I) = "x"
            End If
        End If
    Next I

    LowerCharacter = SourceString
End Function
 
Upvote 0
Muốn "nhiều cách giải" thì đây nè:

1633964489344.png

"Nhiều" làm gì chả biết nữa. Code ở đây toàn là code chữa cháy. Không code nào có chú thích giải thuật. Học khùng luôn và 3 bữa quên mất.
 
Upvote 0
Mình tham gia diễn đàn cũng lâu rồi, chỉ toàn nhờ vả, nhờ xong sưu tầm rồi cũng mở ra và ko biết sử dụng và cũng ko hiểu tác dụng làm gì.
Mình thì ko biết viết vba, đọc cứ loạn lên và ko đủ kiên nhẫn để hiểu.
Nhưng gần đây vô tình biết được tính năng của substitute mà hình như rất ít người biết (theo đánh giá chủ quan thôi).
Mọi người tham khảo cho vui.
 

File đính kèm

  • Nhờ hỗ trợ tạo hàm UDF bằng Function.xlsb
    9.3 KB · Đọc: 8
Upvote 0
Mình tham gia diễn đàn cũng lâu rồi, chỉ toàn nhờ vả, nhờ xong sưu tầm rồi cũng mở ra và ko biết sử dụng và cũng ko hiểu tác dụng làm gì.
Mình thì ko biết viết vba, đọc cứ loạn lên và ko đủ kiên nhẫn để hiểu.
Nhưng gần đây vô tình biết được tính năng của substitute mà hình như rất ít người biết (theo đánh giá chủ quan thôi).
Mọi người tham khảo cho vui.
Công thức thì mình cũng có nhé bạn: =SUBSTITUTE(A2,TRIM(RIGHT(SUBSTITUTE(A2," ",REPT(" ",99)),99)),LOWER(TRIM(RIGHT(SUBSTITUTE(A2," ",REPT(" ",99)),99)))) công thức này cũng giải quyết được vấn đề của mình. Nhưng mà bạn có để ý không 1 công thức nó dài thòng lòng như thế này, ai đâu mà nhớ. Khi dùng lại rất là mất công. Trong khi 1 công thức dài thế này, khi tạo được bằng VBA thì nó thành 1 công thức ngắn gọn. VD: =thay_doi(A2) đơn giản dễ nhớ, dùng được cho nhiều file, nhiều sheet khi tạo bằng Addin nữa. Nói thêm: công thức của bạn và công thức của mình, copy từ sheet 1 sang sheet 3 cũng hơn 1 phút, trong khi VBA thì chỉ 3s. Mình trao đổi không phải chê gì công thức nhé bạn, nhưng mình muốn nói đến sự thần kỳ của VBA thôi. VBA luôn là số 1.
 
Upvote 0
Web KT
Back
Top Bottom