I hope you can help.
I have a piece of code that is currently removing all the text from the cells in Column G. What I need is for this code to instead of removing the text I would like it to remove the numbers, and I only want it to remove the numbers at the beginning of the string/cell the rest of the data I would like to remain the same.
I have attached a picture PIC.1 for betting understanding.
PIC1
The code I currently have and I hope can be amended is below and as always any and all help is greatly appreciated.
CODE
Sub RemoveNonDigits()
Dim X As Long, Z As Long, LastRow As Long, CellVal As String
Const StartRow As Long = 1
Const DataColumn As String = "G"
Application.ScreenUpdating = False
LastRow = Cells(Rows.Count, DataColumn).End(xlUp).Row
For X = StartRow To LastRow
CellVal = Cells(X, DataColumn)
For Z = 1 To Len(CellVal)
If Mid(CellVal, Z, 1) Like "[!0-9]" Then Mid(CellVal, Z, 1) = " "
Next
With Cells(X, DataColumn)
.NumberFormat = "#"
.Value = Replace(CellVal, " ", "")
End With
Next
Application.ScreenUpdating = True
End Sub
CellVal = LTrimDigits(Cells(X, DataColumn))
With this fairly efficient:
Public Function LTrimDigits(value As String) As String
Dim i As Long
For i = 1 To Len(value) '//loop each char
Select Case Mid$(value, i, 1) '//examine current char
Case "0" To "9" '//permitted chars
Case Else: Exit For '// i is the cut off point
End Select
Next
LTrimDigits = Mid$(value, i) '//strip lead
End Function
See the modified code below:
Sub RemoveNonDigits()
Dim X As Long, Z As Long, LastRow As Long, CellVal As String
Const StartRow As Long = 1
Const DataColumn As String = "G"
Application.ScreenUpdating = False
LastRow = Cells(Rows.Count, DataColumn).End(xlUp).Row
For X = StartRow To LastRow
CellVal = Cells(X, DataColumn)
While IsNumeric(Left(CellVal, 1)) ' Here
CellVal = Mid(CellVal, 2) ' all digits at the start
Wend ' are removed
Cells(X, DataColumn) = Trim(CellVal)
Next
Application.ScreenUpdating = True
End Sub
That is, while the starting char in CellVal is a digit, get the substring starting with the second char, and go on until no match.
This function will strip leading digits and spaces from a string
Function RemoveLeadingDigits(str As String) As String
Dim i As Long
Dim chr As String
' Loop through string
For i = 1 To Len(str)
' Get character i
chr = Mid(str, i, 1)
' Keep looping until character is not a number or space
If Not IsNumeric(chr) And Not chr = " " Then
' If it is a number or space, strip checked characters
' from str (because they'll be numeric or space)
str = Right(str, Len(str) - i + 1)
' Stop looping as non-numeric characters encountered
Exit For
End If
Next i
' Return the value of str
RemoveLeadingDigits = str
End Function
You can call it from your code by
Sub RemoveNonDigits()
Dim X As Long, LastRow As Long, CellVal As String
Const StartRow As Long = 1
Const DataColumn As String = "G"
Application.ScreenUpdating = False
LastRow = Cells(Rows.Count, DataColumn).End(xlUp).Row
For X = StartRow To LastRow
CellVal = Cells(X, DataColumn).Value
' ----------------------------------------
CellVal = RemoveLeadingDigits(CellVal)
' ----------------------------------------
Next
Application.ScreenUpdating = True
End Sub
A note on your code though:
You should really fully qualify your cells. For instance, wrap the whole looping section in With ThisWorkbook.Sheets("YourSheet") and then accessing cells using .Cells(row, col) rather than just Cells(row, col).
A bit hacky shorter alternative (assuming all values start with integer)
For Each cell in Range([G7], [G7].End(xlDown))
cell.Value2 = Trim(Mid(cell, Len(Str(Val(cell)))))
Next
I think a simple change like this:
For X = StartRow To LastRow
Cells(X, DataColumn).Formular1c1 = Application.Trim(Replace(Cells(X, DataColumn).Text, Val(Cells(X, DataColumn).Text), ""))
Next X
will solve your problem...
Related
I'm trying to cut out a part of a cell value.
This is how it should look:
So far I got this:
For Each item In arr
pos = InStr(item, "No")
If pos > 0 Then
ActiveSheet.Range("B" & row).Value = item
row = row + 1
Else
ActiveSheet.Range("B" & row).Value = " N/A "
row = row + 1
End If
This returns me the rows but i still need to cut out the Values
-----Update-----
This is what i have now:
Sub cut()
Call Variables
Dim arr() As Variant
Dim element As Variant
Dim element2 As Variant
Dim rows As Integer
Dim rows2 As Integer
arr = Array("test352532_No223", _
"testfrrf43tw_No345figrie_ge", _
"test123_No32_fer", _
"test_Nhuis34", _
"teftgef_No23564.345")
With ThisWorkbook.Worksheets("Numbers").Activate
rows = 1
rows2 = 1
For Each element In arr
Range("A" & rows).Value = element
With regEx
.Pattern = "(No[1-9][\.\d]+[a-z]?)"
Set mc = regEx.Execute(element)
For Each element2 In mc
ActiveSheet.Range("B" & rows2).Value = element2
rows2 = rows2 + 1
Next element2
End With
rows = rows + 1
Next element
End With
End Sub
And this is what it results:
So the problem is, that the Value in B4 should be in B5...
Formula:
Formula in B1:
=IFERROR("No"&-LOOKUP(1,-MID("_"&SUBSTITUTE(A1,".","|"),FIND("_No","_"&A1)+3,ROW($1:$99))),"")
Notes:
Add leading _ to allow for match at start of string;
FIND() is case-sensitive;
SUBSTITUTE() out the dot to prevent longer match with FIND();
The above will not work well when 1st digit after No is a zero.
VBA:
If VBA is a must, try an UDF, for example:
Function GetNo(s As String) As String
With CreateObject("vbscript.regexp")
.Pattern = "^(?:.*?_)?(No\d+)?.*$"
GetNo = .Replace(s, "$1")
End With
End Function
On your worksheets in B1, invoke through typing =GetNo(A1).
Here I used regular expressions to 'cut' the substring you are after. See an online demo. The pattern means:
^ - Start-line anchor;
(?:.*?_)? - Optional non-capture group to match 0+ (Lazy) characters upto underscore. This would also allow No at start of string;
(No\d+)? - Optional capture group to match No (case-sensitive) followed by 1+ digits;
.* - 0+ Characters;
$ - End-line anchor.
EDIT: You can also call the function in your VBA-project:
Sub Test()
arr = Range("A1:A5").Value
For x = LBound(arr) To UBound(arr)
arr(x, 1) = GetNo(CStr(arr(x, 1)))
Next
Range("B1").Resize(UBound(arr)).Value = arr
End Sub
Please, test the next function:
Function extractNoStr(x As String) As String
Dim frst As Long, last As Long, i As Long
frst = InStr(1, x, "No", vbBinaryCompare)
For i = frst + 2 To Len(x)
If Not IsNumeric(Mid(x, i, 1)) Then last = i: Exit For
Next i
If i > Len(x) And last = 0 Then last = Len(x) + 1
extractNoStr = Mid(x, frst, last - frst)
End Function
It can be tested as:
Sub testExtractNoStr()
Dim x As String
x = "test2345_No345figrie_ge"
Debug.Print extractNoStr(x)
Debug.Print activeCell.value 'select a cell containing such a string...
End Sub
To process all range of column A:A, returning in B:B, please use the next code:
Sub extractAll()
Dim sh As Worksheet, lastR As Long, arr, arrFin, i As Long
Set sh = ActiveSheet
lastR = sh.Range("A" & sh.rows.count).End(xlUp).row
arr = sh.Range("A1:A" & lastR).Value2 'place the range in an array for faster processing
ReDim arrFin(1 To UBound(arr), 1 To 1) 'ReDim the final array to receive all occurrences
For i = 1 To UBound(arr)
arrFin(i, 1) = extractNoStr(CStr(arr(i, 1)))
Next i
'drop the processed array content, at once:
sh.Range("B1").Resize(UBound(arrFin), 1).Value2 = arrFin
End Sub
Here I have code that only counts the number of words and I dont know what to do to make it count the words that start with letter "A" and "a" in column M
Sub CountWords()
Dim xRg As Range
Dim xRgEach As Range
Dim xAddress As String
Dim xRgVal As String
Dim xRgNum As Long
Dim xNum As Long
On Error Resume Next
xAddress = ActiveWindow.RangeSelection.Address
Set xRg = Application.InputBox("Introduceti diapazonul:", "Selectare", xAddress, , , , , 8)
If xRg Is Nothing Then Exit Sub
Application.ScreenUpdating = False
If Application.WorksheetFunction.CountBlank(xRg) = xRg.Count Then
MsgBox "Numarul de cuvinte este: 0", vbInformation, ""
Exit Sub
End If
For Each xRgEach In xRg
xRgVal = xRgEach.Value
xRgVal = Application.WorksheetFunction.Trim(xRgVal)
If xRgEach.Value <> "" Then
xNum = Len(xRgVal) - Len(Replace(xRgVal, " ", "")) + 1
xRgNum = xRgNum + xNum
End If
Next xRgEach
MsgBox "Numarul de cuvinte: " & Format(xRgNum, "#,##0"), vbOKOnly, "Raspuns"
Application.ScreenUpdating = True
End Sub
Assuming that each cell contains a single word, use:
Sub ACount()
Dim i As Long, N As Long, Kount As Long
Dim ch As String
Kount = 0
N = Cells(Rows.Count, "M").End(xlUp).Row
For i = 1 To N
ch = Left(Cells(i, "M").Value, 1)
If ch = "a" Or ch = "A" Then Kount = Kount + 1
Next i
MsgBox Kount
End Sub
EDIT#1:
If the cells can contain more than one word (separated by spaces), the use:
Sub ACount()
Dim i As Long, N As Long, Kount As Long
Dim ch As String
Kount = 0
N = Cells(Rows.Count, "M").End(xlUp).Row
For i = 1 To N
arr = Split(Cells(i, "M").Value, " ")
For Each A In arr
ch = Left(A, 1)
If ch = "a" Or ch = "A" Then Kount = Kount + 1
Next A
Next i
MsgBox Kount
End Sub
Alternative via arrays including found word list display
It might be helpful to include a list of all valid words to the demanded count result.
Just to demonstrate a similar approach as Gary, but using arrays instead of a range loop,
I condensed the main procedure to three steps using a help function for step [1]:
[1] get data and provide for a sufficient wrds array by calling a help function getData()
[2] count & collect valid words in a loop through all words,
[3] display count cnt (or: UBound(wrds) plus list of valid words (►1-based 1-dim arraywrds)
Furthermore it's possible to analyze single words as well as word groups separated by spaces.
Sub ACount2()
Const SEARCHLETTER As String = "a" ' << change to any wanted search letter
'[1] get data and provide for sufficient wrds array
Dim allWrds, wrds: allWrds = getData(Sheet1, wrds) ' << change Sheet1 to your sheet's Code(Name)
'[2] count & collect valid words
Dim i As Long, letter As String, cnt As Long
For i = LBound(allWrds) To UBound(allWrds) ' loop through original words
letter = LCase(Left(allWrds(i), 1)) ' compare with search letter (lower case)
If letter = SEARCHLETTER Then cnt = cnt + 1: wrds(cnt) = allWrds(i)
Next i
ReDim Preserve wrds(1 To cnt)
'[3] display count plus list of valid words
MsgBox cnt & " words starting with {A|a}:" & _
vbNewLine & vbNewLine & _
Join(wrds, ", "), vbInformation
End Sub
Help function getData() called by above procedure
Function getData(sht As Worksheet, wrds, Optional ByVal col = "M", Optional ByVal StartRow As Long = 2)
'Purpose: get column data of a given worksheet and return to a "flat" array; provide for a sufficient wrds array
'a) get 2-dim data (starting in cell M2 by default) and transpose to 1-dim "flat" array
Dim lastRow As Long: lastRow = sht.Cells(sht.Rows.Count, col).End(xlUp).Row
Dim data: data = Split(Join(Application.Transpose(sht.Range(col & StartRow & ":" & col & lastRow)), " "), " ")
'b) provide for maximum elements in found words in calling procedure (implicit ByRef!)
ReDim wrds(1 To UBound(data))
'c) return 1-based "flat" 1-dim data array
getData = data
End Function
I have the following issue: In one workbook I have multiple sheets.
On Sheet 2 in column "D" starting on row 2, Is a list of 300+ prefixes of 4 digits long e.g. XFTZ, GHTU, ZAQS etc.
On Sheet 1 in column "R" starting on row 3, Is a list of 1000+ values that can have the following values e.g.: AAAA1234556 and ZAQS12565865.
The first value AAAA...... is allowed, where the second value ZAQS..... Should throw an error message when running the VBA code.
The list of values in both sheets can grow over time, so I would like to avoid hard coding of records. I would expect best solution here is to use something like this:
LastRowNr = Cells(Rows.Count, 1).End(xlUp).Row
Try something like the following, replacing Sheet1 with the name in which the actual data is located
Option Explicit
Private Sub searchPrefix()
Dim RangeInArray() As Variant
Dim LastRow1 As Long
Dim LastRow2 As Long
Dim tmpSrch As String
Dim i As Long
LastRow1 = Worksheets("Sheet1").Cells(Rows.Count, 18).End(xlUp).Row
LastRow2 = Worksheets("PREFIXES").Cells(Rows.Count, 4).End(xlUp).Row
RangeInArray = Application.Transpose(Worksheets("PREFIXES").Range("D1:D" & LastRow2).Value)
For i = 3 To LastRow1
If Len(Worksheets("Sheet1").Cells(i, 18).Value) >= 3 Then
tmpSrch = Left(Worksheets("Sheet1").Cells(i, 18).Value, 4) '18: column R
If IsInArray(tmpSrch, RangeInArray) Then
Worksheets("Sheet1").Cells(i, 18).Interior.ColorIndex = xlNone
Worksheets("Sheet1").Cells(i, 18).Font.ColorIndex = 0
Worksheets("Sheet1").Cells(i, 18).Font.Bold = False
Else
Worksheets("Sheet1").Cells(i, 18).Interior.Color = RGB(252, 134, 75)
Worksheets("Sheet1").Cells(i, 18).Font.Color = RGB(181, 24, 7)
Worksheets("Sheet1").Cells(i, 18).Font.Bold = True
End If
End If
Next
End Sub
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Option Explicit
Sub searchPrefix()
Sheets("PREFIXES").Select
Dim CellCntnt As String
Dim tmpSrch As String
Dim isFound As Boolean
isFound = False
Dim QtySrchChar As Integer
QtySrchChar = 4
Dim Cnt As Integer
Cnt = 0
Dim Tag As Integer
Cells.Range("A1").Select
Do Until IsEmpty(ActiveCell)
Cnt = Cnt + 1
ActiveCell.Offset(1, 0).Select
Loop
For Tag = 1 To Cnt - 1
CellCntnt = Cells(1 + i, 1).Value
tmpSrch = Left(CellCntnt, QtySrchChar)
Cells.Range("G1").Select
Do Until IsEmpty(ActiveCell)
If Left(ActiveCell.Value, QtySrchChar) = tmpSrch Then
QtySrchChar = QtySrchChar + 1
tmpSrch = Left(CellCntnt, QtySrchChar)
isFound = True
MsgBox ("True Tags introduced with Std.Prefix " & tmpSrch)
End If
If isFound Then
isFound = False
MsgBox ("False Tags introduced with Std.Prefix " & tmpSrch)
Cells.Range("G1").Select
Else
ActiveCell.Offset(1, 0).Select
End If
Loop
Next Tag
End Sub
Disclaimer- my case is specific, and in my case my code works because I know the pattern.
I was looking for an answer everywhere, and the codes I tried were not quite what I was looking for, this is my solution if you are looking for a set of numbers.
In my case, I was looking for 7 digits, starting with digit 1 in a a column with random strings, some string had the number some others didn't.
The number will appear in these three scenarios "1XXXXXX", "PXXXXXXXX", "PXXXXXXXXX"(this has more digits because there is a slash).
Here are the examples of strings:
9797 P/O1743061 465347 Hermann Schatte Earl Lowe
9797 Po 1743071 404440 Claude Gaudette Jose Luis Lopez
9817 1822037 463889 Jean Caron Mickelly Blaise
My Code
Sub getnum()
'i don't use explicit so i didn't declare everything
Dim stlen As String
Dim i As Integer
Dim arra() As String
Dim arran() As String
Orig.AutoFilterMode = False
Call BeginMacro
LastRow = Orig.Cells(Rows.Count, 1).End(xlUp).Row
Orig.Range("J2:J" & LastRow).Clear
'loop though column
For n = 2 To LastRow
celref = Orig.Cells(n, 4).Value
'split string on white spaces
arra() = Split(celref, " ")
'turn string to multiple strings
For counter = LBound(arra) To UBound(arra)
strin = arra(counter)
'remove white spaces from string
storage = Trim(strin)
lenof = Len(storage)
'if string has 9 characthers, check for conditions
If lenof = 9 Then
'position of first and last charachter
somstr = Mid(storage, 1, 1)
somot = Mid(storage, 9, 1)
If somstr = "P" Or somstr = "p" And IsNumeric(somot) = True Then
'removes Po or PO and keeps only 7 digits
storage = Right(storage, 7)
'stores in column J
Orig.Cells(n, 10).Value = storage
End If
ElseIf lenof = 10 Then
somstr = Mid(storage, 1, 1)
somot = Mid(storage, 10, 1)
'other conditions
If somstr = "P" Or somstr = "p" And IsNumeric(somot) = True Then
'removes Po or PO and keeps only 7 digits
storage = Right(storage, 7)
'stores in column J
Orig.Cells(n, 10).Value = storage
End If
End If
'eliminate comma within
arran() = Split(storage, ",")
If Orig.Cells(n, 10).Value <> storage Then
For counter2 = LBound(arran) To UBound(arran)
strin2 = arran(counter2)
storage2 = Trim(strin2)
'final condition if is 7 digits and starts with 1
If IsNumeric(storage2) = True And Len(storage2) = 7 Then
car = Mid(storage2, 1, 1)
If car = 1 Then
'stores in columns J at specific position
Orig.Cells(n, 10).Value = storage2
End If
Else
If isnumeric(orig.cells(n,10).value) =true and _
len(orig.cells(n,10).value = 7 then
orig.cells(n,10).value = orig.cells(n,10).value
else
Orig.Cells(n, 10).Value = "no po# in D"
End If
Next counter2
End If
Next counter
Next n
Call EndMacro
End Sub
you may try this
Option Explicit
Sub getnum()
Dim position As Variant
Dim cell As Range
With Worksheets("Orig") ' change it to your actual sheet name
With Intersect(.UsedRange, Columns("J"))
.Replace what:="P/O", replacement:="P/O ", lookat:=xlPart
For Each cell In .Cells
position = InStr(cell.Text, " 1")
If position > 0 Then cell.Value = Mid(cell.Value, position + 1, 7)
Next
End With
End With
End Sub
This code paste two formulas one in column G and one in column J). The first formula checks for a "P" in the first character of the cell in column 2 and if there is a "P" it extracts the last 7 characters in the string and puts them in column G. The second formula checks if there is not a "P" and if not extracts the last 7 characters in the string and puts them in column J.
Sub Extract()
Dim ws As Worksheet
Dim lRow As Long
Set ws = ThisWorkbook.Sheets("Sheet3")
lRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
ws.Range("G2:G" & lRow).Formula = "=IF(LEFT(B2)=""P"",(RIGHT(B2,7)),"""")"
ws.Range("J2:J" & lRow).Formula = "=IF(LEFT(B2)<>""P"",(RIGHT(B2, 7)),"""")"
End Sub
You may use the RegEx to extract the number in desired format.
Please give this a try...
Function Get10DigitNumber(ByVal Str As String) As String
Dim RE As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "1\d{6}"
End With
If RE.test(Str) Then
Get10DigitNumber = RE.Execute(Str)(0)
End If
End Function
Then if you want to use this function on the worksheet itself, assuming your string is in A2, try this...
=Get10DigitNumber(A2)
OR
You may use this function in another sub routine/macro like this...
Debug.Print Get10DigitNumber(<pass your string variable here>)
Edited Function:
Function Get10DigitNumber(ByVal Str As String) As String
Dim RE As Object, Matches As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "[Pp]?\/?[Oo]?(1\d{6})\b"
End With
If RE.test(Str) Then
Set Matches = RE.Execute(Str)
Get10DigitNumber = Matches(0).SubMatches(0)
End If
End Function
And use if as already described above.
After understanding what you were doing, I think this will work. Any feedback would be appreciated.
Dim cell As Range, LRow As Long
LRow = ActiveSheet.Cells(Rows.Count, 4).End(xlUp).Row
For Each cell In Range("D2:D" & LRow)
If cell.Value Like "*Po *" Then
cell.Offset(0, 6).Value = Split(cell.Value, " ")(2)
Else: cell.Offset(0, 6).Value = Split(cell.Value, " ")(1)
End If
Next cell
For Each cell In Range("J2:J" & LRow)
If Len(cell.Value) > 7 Then
cell.Value = Right(cell.Value, 7)
End If
Next
I am trying to implement a SPLIT() function to parse a string in EXCEL and distribute the result to adjacent cells on the same form. The following is working as expected, except when a parsed term is a Number.
"0.25" parses to "1/0/1900 6:00:00 AM"
I tried cstr() on the resulting strAttr value, but seems to have no effect. Any ideas?
Sub splitText()
Dim i As Integer
Dim x As Integer
Dim strText As String
Dim strAttr As Variant
Dim strFirst As String
Dim NumRows As Integer
strFirst = "A4"
Sheets("Data").Activate
Range(strFirst).Select
NumRows = Range(strFirst, Range(strFirst).End(xlDown)).Rows.Count
For x = 1 To NumRows
ActiveCell.Offset(1, 0).Select
strText = ActiveCell.Value
strAttr = Split(strText, " ")
For i = 0 To UBound(strAttr)
Cells(x + 4, i + 2).Value = strAttr(i)
Next i
Next
End Sub
Note:
I forced a (') to the front of the string, which seems to provide the desired result, except everything will be = TEXT:
For i = 0 To UBound(strAttr)
Cells(x + 4, i + 2).Value = "'" & strAttr(i)
Next i
Is there a better way to accomplish this and retain "0.25" as a Number value?
Thanks,
mark
This may help. Replace:
strText = ActiveCell.Value
with:
strText = ActiveCell.Text