I have 50,000 name and address strings each occupying one cell. In order to Split the cell out to different Name, Street Number, Street, City etc I am trying to split the cells to columns that match either Street Number and Or Street Name.
Cell Examples all in column E:
Row Col: E
aparts. 56 Johnston Terrace Keyham Road
90 & 92 Wolseley Road
2 Ainslie Terrace
Dyer & Cleaner 10 & 12 Mount Gold Road
48b Alexander Road
Dairy Farmer Stratham Priory Road
NewCell Result in columns;
Row. Col.F | Col.G | Col.H
aparts.| 56 | Johnston Terrace Keyham Road
'*' | 90 & 92 | Wolseley Road
'*' | 2 | Ainslie Terrace
Dyer & Cleaner | 10 & 12 | Mount Gold Road
'*' | 48b | Alexander Road
Dairy Farmer | '*' | Stratham Priory Road
At the present my Excel Sheet does not have specific column names, only A; B; C etc. I have VBA code that will separate each cell. However, the Street Number and/or Street Name will be split differently depending on the "textnumbertext" string in each cell.
I have separate VBA code to add an asterisk in front of any entry that starts with a Street Number (see Code). This then places each cell in the correct column (I can delete the asterisk later). However, I feel that this code is inefficient and perhaps could be less verbose if perhaps I were to use the Case function.
A further complication is some Street numbers will be 14A or 12B or 10c, or 12a. If I add these options to the below code then everything becomes very long winded and inefficient. Any thoughts please?
Sub ReplaceFirstNumber()
'If the first character in the string starts with a number between 1-9 THEN
'ADD a * to the string
Dim r As Range
Dim c As Range
On Error Resume Next
Set r = Range(Range("E1"), Range("E" & Rows.Count).End(xlDown))
For Each c In r
If Left(c.Value, 1) = "1" _
Or Left(c.Value, 1) = "2" _
Or Left(c.Value, 1) = "3" _
Or Left(c.Value, 1) = "4" _
Or Left(c.Value, 1) = "5" _
Or Left(c.Value, 1) = "6" _
Or Left(c.Value, 1) = "7" _
Or Left(c.Value, 1) = "8" _
Or Left(c.Value, 1) = "9" Then
c.Value = " * " & c.Value
End If
Next c
End Sub
The function below will hopefully help you make this task a tad easier. It strips all numeric characters from the address string, and will include any trailing single letters.
Function getnumbersfromstring(address As String) As String
For i = 1 To Len(address)
If IsNumeric(Mid(address, i, 1)) Then getnumbersfromstring = getnumbersfromstring & Mid(address, i, 1)
Next i
CharAfterNumber = Mid(address, Instr(1, address, getnumbersfromstring) + Len(getnumbersfromstring), 1)
If IsNumeric(CharAfterNumber) = False And Not CharAfterNumber = " " And Not CharAfterNumber = "" Then
getnumbersfromstring = getnumbersfromstring & CharAfterNumber
End If
End Function
This function can be called in a regular Sub like so
Sub breakupaddress()
Dim r As Range
Dim c As Range
Dim addressnr As String
On Error Resume Next
Set r = Range(Range("E1"), Range("E" & Rows.Count).End(xlDown))
For Each c In r
addressnr = getnumbersfromstring(c.Value)
MsgBox "The address number is '" & addressnr & "'.", vbInformation, "Information"
Next c
End Sub
I'm curious about how you will code everything, but about your question, something that might work would be:
Sub ReplaceFirstNumber()
'If the first character in the string starts with a number between 1-9 THEN
'ADD a * to the string
Dim r As Range
Dim c As Range
Set r = Range("E1:E" & Range("E" & Rows.Count).End(xlUp).Row)
For Each c In r
If IsNumeric(Left(c.Value, 1))=True Then c.Value = "*" & c.Value
Next c
End Sub
In your code you use Range(Range("E1"), Range("E" & Rows.Count).End(xlDown)). This means all cells in column E!. And that's like a million cells in Excel 2007 or higher. In my code, the range is Set r = Range("E1:E" & Range("E" & Rows.Count).End(xlUp).Row) This will select only all cells between E1 and the last non blank cell in Column E, so it will improve executing times a lot if you have only 50.000 rows of data.
Also, if you are learning VBA, I strongly encourage you to never use On Error Resume Next statement, because it hides errors, but they still occur.
Hope you can finally code this, or at least, find this answer helpful.
But anyways, you still got a lot to code.
Related
I'm trying desperately to learn Excel well enough to do this on my own but I can't figure this out. I really appreciate any help you can give me. I posted before with not nearly enough information, so this is the repost with more info.
A document is pasted in cell A9.
It fills every cell below it with lines of data, up to A200.
The lines of data look like this:
192800002001 19280 G RG474 56 DAY PMI COMPLETE
19280A001001 19280 G CB359 AN/PRC-152A 56 DAY PMI
19280A005001 19280 G CB360 AN/PRC-152A 56 DAY PMI
I need the program to search each cell in column A for the words that look like "RG474" or "CB359" and search in a reference table on a different sheet in the same book. The table on the reference table looks like this
RG474 | xxx474 0 | 0 | IN RACK | AF6
CB915 | xxx359 0 | 0 | IN RACK | AF6
For every match found it pastes the row from the reference table into the row of the match next to the pasted document (columns L-Q).
I've found some code online that I've tried to no avail, the two things I tried are here:
Dim lastRw1, lastRw2, nxtRw, m
'Determine last row with data, refrene
lastRw1 = Sheets("380 Refrence").Range("A" & Rows.Count).End(xlUp).Row
'Determine last row with data, Import
lastRw2 = Sheets("analyser").Range("A" & Rows.Count).End(xlUp).Row
'Loop through Import, Column A
For nxtRw = 9 To lastRw2
'Search Sheet1 Column C for value from Import
With Sheets("380 Refrence").Range("A9:A" & lastRw1)
Set m = .Find(Sheets("analyser").Range("A" & nxtRw), LookIn:=xlValues, lookat:=xlWhole)
'Copy Import row if match is found
If Not m Is Nothing Then
Sheets("analyser").Range("A" & nxtRw & ":F" & nxtRw).Copy _
Destination:=Sheets("380 Refrence").Range("L" & m.Row)
End If
End With
Next
End Sub
Sub CopyImportData()
Dim lastRw1, lastRw2, nxtRw, m
Dim code As String, RefRow As Integer
Dim rowValues
'Determine last row with data, 380 Refrencerene
lastRw1 = Sheets("380 Refrence").Range("A" & Rows.Count).End(xlUp).Row
'Determine last row with data, Import
lastRw2 = Sheets("analyser").Range("A" & Rows.Count).End(xlUp).Row
For Row = 9 To lastRw2
With Sheets("analyser").Cell(Row, 1)
'meet the laziest error handling ever to find your 380 Refrenceerence value
code = WorksheetFunction.Mid(.Value, WorksheetFunction.IfError(WorksheetFunction.IfError(WorksheetFunction.Search("CB??? ", .Value), WorksheetFunction.Search("RG??? ", .Value)), 1), 5)
End With
With Sheets("380 Refrence")
'Use Excel Match to find the 380 Refrenceerence row, which is offset by 8
'I swear I'll stop using iferror
380 RefrenceRow = WorksheetFunction.IfError(WorksheetFunction.Match(code, .Range("A9:A" & lastRw1), 0) + 8, -1)
'-1 is our safeword, copy the range
If RefRow <> -1 Then
.Range("A" & RefRow & ":F" & RefRow).Copy Destination:=Worksheets("analyser").Range("L" & Row)
End If
End With
Next Row
End Sub
I didn't write either of these and don't fully understand them, but I do get the Gist of it.
Here's a very trimmed down duplicate of the workbook: https://drive.google.com/open?id=1qCz8DUCz6tA5-KbxKDnvRq_KiBDkl4W5
This worked for me - I skipped some of the "find last cell" bits so you'll need to adjust for that
Sub Tester()
Dim c As Range, v, f
Dim ws380 As Worksheet, wsAn As Worksheet
Set ws380 = ThisWorkbook.Sheets("380 Reference")
Set wsAn = ThisWorkbook.Sheets("analyser")
For Each c In wsAn.Range("A1:A50") 'for example
If Len(c.Value) > 0 Then
v = GetMatch(c.Value)
Debug.Print c.Address, v
If Len(v) > 0 Then
'got a value - look it up...
Set f = ws380.Range("A9:A5000").Find(v, lookat:=xlWhole, _
lookin:=xlValues)
If Not f Is Nothing Then
f.Resize(1, 6).Copy c.EntireRow.Cells(1, "L") 'copy found row
End If
End If
End If
Next c
End Sub
Function GetMatch(txt As String)
Dim re As Object, allMatches, m
Set re = CreateObject("VBScript.RegExp")
'looking for two upper-case letters then 3 digits, or 3 letters plus 2 digits
' with a word boundary on each end
re.Pattern = "(\b([A-Z]{2}\d{3}\b)|(\b[A-Z]{3}\d{2})\b)"
re.ignorecase = False
re.Global = True
Set allMatches = re.Execute(txt)
For Each m In allMatches
GetMatch = m.Value
Exit For
Next m
End Function
Here's a good vbscript regexp reference:
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974570(v=msdn.10)?redirectedfrom=MSDN
I've just created a brand new macro. Took function down below from internet (all credits goes to trumpexcel.com), code down below
Function CONCATENATEMULTIPLE(Ref As Range, Separator As String) As String
Dim Cell As Range
Dim Result As String
For Each Cell In Ref
Result = Result & Cell.Value & Separator
Next Cell
CONCATENATEMULTIPLE = Left(Result, Len(Result) - 1)
End Function
Then I proceed to extract data from various columns and into the one (my table is 20 rows x 10 columns)
Sub conact_data()
Dim i As Integer
For i = 2 To Cells(Rows.Count, "A").End(xlUp).Row
Cells(i, "M").Value = Cells(i, "A").Value & " " & _
Cells(i, "B").Value & " / " & Cells(i, "D").Value & "; "
Next i
End Sub
Thanks to that I've got combined data from column A, B and D, so its 20 rows. All I want to do now is to concatenate data from M2:M21 using CONCATENATEMULTIPLE function therefore I try various approach (I want this huge line in P2 cell) like :
Cells(2, 16).Value = CONCATENATEMULTIPLE (M2:M21, " ")
or
Range("P2") = "CONCATENATEMULTIPLE (M2:M21, " ")"
I don't really know how to apply that
Secondly, I'd like withdraw the Cells(i, "B").Value as percentage. Can I do that in one line like Cells(i, "B").NumberFormat="0.00%".Value (which is not working for me obviously) else I need to copy column B into another column with number format and then combine the new column, properly formatted instead of column B?
Thanks in advance
Percent format: Range("B" & i).NumberFormat = "0.00%"
CONCATENATEMULTIPLE
In VBA, CHR(32) = " "
In Excel, CHAR(32) = " "
With that being said...
'Value
Range("P2").Value = CONCATENATEMULTIPLE(Range("M2:M21"), CHR(32))
'Formula
Range("P2").Formula = "=CONCATENATEMULTIPLE(M2:M21, CHAR(32))"
You should really qualify all of your ranges with a worksheet
Say your workbook has 10 sheets. When you say Range("P2"), how do we (VBE) know what sheet you mean? Objects need to be properly qualified. Sometimes this is not a huge issue, but when you are working across multiple sheets, not qualifying ranges can lead to some unexpected results.
You can qualify with a worksheet a few ways.
Directly: ThisWorkbook.Sheets("Sheet1").Range("P2").Copy
Or use a variable like so
Dim ws as Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
ws.Range("P2").Copy
Now there is no room for ambiguity (potential errors) as to the exact location of Range("P2")
First of all, remove your ConcatenateMultiple() code, and instead use Excel worksheet function CONCAT(), which takes a range and a delimiter as parameters.
Here is how you can handle the percentage issue and supply a default for non-numeric items. I've also cleaned up the way you reference your data range.
Sub concat_data()
Dim rngRow As Range, vResult As Variant
Const DEFAULT = 0 'Can also be set to a text value, eg. "Missing"
For Each rngRow In [A2].CurrentRegion.Rows
If IsNumeric(rngRow.Cells(, 4)) Then vResult = rngRow.Cells(, 4) * 100 & "%" Else vResult = DEFAULT
Range("M" & rngRow.Row) = rngRow.Cells(, 1) & rngRow.Cells(, 2) & "/" & vResult & ";"
Next
[M2].End(xlDown).Offset(1).Formula = "=CONCAT(M2:M" & [M2].End(xlDown).Row & ",TRUE,"" "")"
End Sub
I'm not a fan of hard-coding range references, like the [A2] or Range("M"), but will leave that for another time.
I have a sheet called "Table" where I have the table I'm looking up its A2:B20,
A2:A20 contains numbers in "XX" format these are the numbers I will be looking up.
The B2:B20 part of the table contains text is this text I want to use to replace values with.
I have my main sheet (currently called "Test") which contains my data, I want to look in Column M and check if I can find a value where the first 2 chars match any one of the values in A2:A20, if I do find a match I then want to replace the value of column F on my data sheet (Test) with the corresponding value from B2:B20 if not I want to leave it as is and move on.
I'm running into problems as the data in column M is numbers stored as text and it is replacing the wrong value when the table list 1 or 11 or 2 and 22.
'
Dim MyString As String
Counter = 2
1:
MyString = Sheets("Table").Range("A" & Counter).Value
For X = 1 To Range("M" & Rows.Count).End(xlUp).Row
If Replace(MyString, Left(Sheets("TEST").Range("M" & X).Value, 2), "") <> MyString Then Sheets("TEST").Range("F" & X).Value = Sheets("Table").Range("B" & Counter).Value
Next
Counter = Counter + 1
If Counter <= Range("M" & Rows.Count).End(xlUp).Row Then
GoTo 1:
Else
End If
End Sub
I solved my own problem, I was doing too much - simplified it forces values to .text and my issues went away.
Sub BBK_Name()
'Checks column U for start of data (1st 2 chars)
' if they match an entry in bank table changes entry in column G to match table entry.
'
Dim MyString As String
Counter = 2
1:
MyString = Sheets("Table").Range("A" & Counter).Text
RplcValue = Sheets("Table").Range("B" & Counter).Text
For X = 1 To Range("M" & Rows.Count).End(xlUp).Row
If Left(Sheets("TEST").Range("M" & X).Value, 2) = MyString Then _
Sheets("TEST").Range("F" & X).Value = RplcValue
Next
Counter = Counter + 1
If Counter <= Range("M" & Rows.Count).End(xlUp).Row Then
GoTo 1:
Else
End If
End Sub
I have an excel sheet with a column for Car number. It is currently downloaded as a report with the format "58 58" for car number 58.
I would like to replace each occurrence down column H and replace "58 58" with a numeric "58" | "60 60" with "a numeric "60" | "90 90" with a numeric "90" and so on.
This is all done in VBA.
Thank you
UPDATED CODE:
Dim X As Long
For X = 2 To Range("I" & Rows.Count).End(xlUp).Row 'Change 1 to 2 if you have a heading in row 1
Range("I" & X).Formula = Split(Range("I" & X).Text, " ")(0)
I used the above code, but it gave me runtime error (9) subscript out of range
Probably the easiest way is to loop. Going backwards alleviates possible issues with 101 being messed up if you start at 1.
Sub DoubleToSingle()
Dim X As Long
For X = 100 To 1 Step -1
Cells.Replace X & " " & X, X
Next
End Sub
This works on all cells, if you want it on just a column replace Cells. with Columns(2). where 2 is column B and 3 would be C etc.
Edit: use this code for your updated questions:
Sub DoubleToSingle2()
Dim X As Long
For X = 2 To Range("I" & Rows.Count).End(xlUp).Row
If InStr(1, Range("I" & X).text, " ") > 0 Then Range("I" & X).Formula = Split(Range("I" & X).text, " ")(0)
Next
End Sub
How it works: It polls through all cells in column I that have data and for each cell it splits the text of the cell into an array using space, then it takes just the first element in the array (basically everything before the first space) and posts that back to the cell.
this is my first time using the site, so forgive me for any inept explaining. I have a working macro to hide/unhide rows based on content of the rows, I just want it to be faster. Using a check box, when the box is checked, all rows with an "x" in column D get unhidden, those without an "x" get hidden. Same thing happens when it is unchecked, except it references column C, not D.
Right now, this code works. It's just a little slower than I'd like, since I'm sharing this with a bunch of people. Any ideas for how to speed it up? I'm pretty darn new to VB (the internet is astoundingly wise and a good teacher), but that doesn't matter. I already improved the code - before it selected each row, then referenced the column, and it was awful. Any ideas to speed it up (preferably without moving the screen) would be great.
Thanks so much folks,
DS
Sub NewLuxCheck()
Dim x As Integer
NumRows = Range("A42", "A398").Rows.Count
Range("A42").Select
If ActiveSheet.Shapes("checkbox2").OLEFormat.Object.Value = 1 Then
For x = 42 To NumRows + 41 Step 1
If Worksheets("Base").Range("D" & x).Value = "x" Then
Worksheets("Base").Range(x & ":" & x).EntireRow.Hidden = False
Else
Worksheets("Base").Range(x & ":" & x).EntireRow.Hidden = True
End If
Next
Else
For x = 42 To NumRows + 41 Step 1
If Worksheets("Base").Range("C" & x).Value = "x" Then
Worksheets("Base").Range(x & ":" & x).EntireRow.Hidden = False
Else
Worksheets("Base").Range(x & ":" & x).EntireRow.Hidden = True
End If
Next
End If
MsgBox ("Done")
End Sub
You could use array formula and let Excel to return array with row-numbers where 'x' value occures. It will be quicker but you'll have to reorganise your code and create separate functions etc.
Here example where array formula finds rows whre in column 'D' the cell has value 'x'. Then string of this row numbers is created in form of "A1,A5,A10" ...means 'x' was found in rows 1,5,10. And finally Range(rowsJoind).EntireRow.Hidden is used for all the rows to be hidden/un-hidden in one step.
For rows with value different then 'x' you'll have to use formula like '=IF({0}<>""x"", ROW({0}), -1)'.
Sub test()
Dim inputRange As Range
Dim lastRow As Long
Dim myFormula As String
Dim rowsJoined As String, i As Long
Dim result As Variant
With Worksheets("Base")
lastRow = .Range("D" & .Rows.Count).End(xlUp).Row
Set inputRange = .Columns("D").Resize(lastRow)
Application.ReferenceStyle = xlR1C1
myFormula = "=IF({0}=""x"", ROW({0}), -1)"
myFormula = VBA.Strings.Replace(myFormula, "{0}", inputRange.Address(ReferenceStyle:=xlR1C1))
result = Application.Evaluate(myFormula)
result = Application.Transpose(result)
Application.ReferenceStyle = xlA1
For i = LBound(result) To UBound(result)
If (result(i) > -1) Then
rowsJoined = rowsJoined & "A" & result(i) & IIf(i < UBound(result), ",", "")
End If
Next i
.Range(rowsJoined).EntireRow.Hidden = False
End With
End Sub