I want to use VBA to split the contents of a cell into three separate parts, such as [city], [state] [zip code] are put into three different columns on the same row while leaving the original cell unchanged.
I had thought that split would work, but unfortunately I have encountered some complicating issues, first split seems to only carry over the what's on the left, leaving behind the rest, second, I don't see how I can incorporate two delimiters into a single split.
Any idea how to surmount these issues?
Cells(Row1, ColA).Select
Location = ActiveCell.Value
Cells(Row1, ColC) = Split(Location, ",")
Cells(Row1, ColA).Select
Cells(Row1, ColD) = Split(Location, " ")
Cells(Row1, ColA).Select
Cells(Row1, ColE) = Split(Location, " ")
Split() returns an array of strings, you need to iterate through this array and assign the value of each element to the corresponding cell (take a look at this answer).
Also, you don't need to select cells to assign values to it, you may use:
Cells(Row,Column).Value = X
Regarding 2 delimiters, you may do it as described here.
Use the Split, I use an array in the middle just to store the split String (in case you will want to use it later), it also saves me the time of the iteration.
Sub Split_toThree()
Dim lrow As Long
Dim LastRow As Long
Dim SplitArr() As String
' find last row in Column A (where you keep your full string)
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For lrow = 2 To LastRow
SplitArr() = Split(Cells(lrow, "a"), ",")
Range("C" & lrow & ":E" & lrow) = SplitArr()
Next lrow
End Sub
Related
I am working on a workbook in which I need to count how many times the "St/" substring is present in a Range (Column Q). Note: I am interested in all the occurrences, not just the number of cells in which the substring is present.
Here is the code I am trying to work with (based on the comment of Santhosh Divakar - https://stackoverflow.com/a/23357807/12536295), but I receive a runtime error (13) when running it. What am I missing / doing wrong?
Dim lastrow, q as Integer
lastrow = Range("A1").End(xlToRight).End(xlDown).Row
With Application
q = .SumProduct((Len(Range("Q1:Q" & lastrow)) - Len(.Substitute(Range("Q1:Q" & lastrow), "St/", ""))) / Len("St/"))
End With
See if the code below helps you:
Public Sub TestCount()
lastrow = Range("Q" & Rows.Count).End(xlUp).Row
strformula = "=SUMPRODUCT(LEN(Q1:Q" & lastrow & ")-LEN(SUBSTITUTE(UPPER(Q1:Q" & lastrow & "),""/ST"","""")))/LEN(""/St"")"
MsgBox Evaluate(strformula)
End Sub
I think you can count the number of characters, replace your "St/" with nothing and then count the characters again and divide by len("St/"). Here's an example.
'''your existing code
Dim lCount As Long
Dim lCount_After As Long
'''set a Range to column Q
Set oRng = Range("Q1:Q" & lRow_last)
'''turn that range into a string
sValues = CStr(Join(Application.Transpose(oRng.Value2)))
lCount = Len(sValues)
lCount_After = lCount - Len(Replace(sValues, "St/", ""))
lCount_After = lCount_After / 3
Debug.Print lCount_After
Using ArrayToText() function
a) If you dispose of Excel version MS365 you can shorten a prior string building by evaluating the tabular ARRAYTOTEXT()
formula to get a joined string of all rows at once (complementing #Foxfire 's valid solution).
Note that it's necessary to insert the range address as string;
in order to fully qualify the range reference I use an additional External:=True argument.
b) VBA's Split() function eventually allows to return the number of found delimiters (e.g. "St/") via
UBound() function. It returns the upper boundary (i.e. the largest available subscript) for this
zero-based 1-dimensional split array.
Example: If there exist eight St/ delimiters, the split array consists
of nine elements; as it is zero-based the first element has index 0
and the last element gets identified by 8 which is already the wanted function result.
Function CountOccurrencies(rng As Range, Optional delim as String = "St/")
'a) get a final string (avoiding to join cells per row)
Dim txt As String
txt = Evaluate("ArrayToText(" & rng.Address(False, False, External:=True) & ")")
'b) get number of delimiters
CountOccurrencies = UBound(Split(txt, delim))
End Function
Not the cleanest one, but you can take all into arrays and split by St/. Size of that array would be how many coincidences you got:
Sub test()
Dim LR As Long
Dim MyText() As String
Dim i As Long
Dim q As Long
LR = Range("Q" & Rows.Count).End(xlUp).Row
ReDim Preserve MyText(1 To LR) As String
For i = 1 To LR Step 1
MyText(i) = Range("Q" & i).Value
Next i
q = UBound(Split(Join(MyText, ""), "St/"))
Debug.Print q
Erase MyText
End Sub
The output i get is 8
Please, note this code is case sensitive.
The TextJoin() function in Excel 2019+ is used:
Sub CalcSt()
Const WHAT = "St/": Dim joined As String
joined = WorksheetFunction.TextJoin("|", True, Columns("Q"))
Debug.Print (Len(joined) - Len(Replace(joined, WHAT, ""))) / Len(WHAT)
End Sub
The main problem started when I wanted to "convert to number" by the green triangle (I know I can do it by hand, but there are a lot of cells like that and in the future I only want to use code).
So I wanted to do it by code, and I came across with this code that helps, but I have a problem with the number format which removes the decimal numbers.
Sub Valor3()
Dim LastRow As Long, i As Long
LastRow = Sheets("Hoja3").Range("A" & Rows.Count).End(xlUp).Row
'Sheets("Hoja3").Range("A1:A" & LastRow).NumberFormat = "# ##0,00"
For i = 1 To LastRow
If Val(Sheets("Hoja3").Range("A" & i).Value) <> 0 Then _
Sheets("Hoja3").Range("A" & i).Formula = _
Val(Sheets("Hoja3").Range("A" & i).Value)
Next i
End Sub
I've been trying many formats but none of them seems to help.
It might be because here we use the comma as a decimal separator and there is no miles separator.
What number format would help me?
The issue is that you use Val function in combination with a non-us-english decimal separator, which is not a proper solution to your issue.
The Val function recognizes only the period ( .) as a valid decimal separator. When different decimal separators are used, as in international applications, use CDbl instead to convert a string to a number.
Source: Microsoft documentation Val function.
Since the Val function does not convert a text into a value but extracts
The Val function only works with a dot . as decimal separator.
Example:
Val("2.55") 'will return 2.55 as number
Val("2,55") 'will return 2 as number (because it cuts off all text and the comma is not considered as decimal separator)
To get rid of the green triangle and convert a number that is saved as text into a real number properly, use the following:
Option Explicit
Public Sub ConvertNumberAsTextIntoRealNumber()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Hoja3")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
With ws.Range("A1", "A" & LastRow)
.NumberFormat = "# ##0.00" 'set your desired number format
.Value = .Value 'this will in most cases already convert to real numbers.
End With
'But if your numbers are hard coded to text and begin with a `'` you need the following additionally:
Dim iRow As Long
For iRow = 1 To LastRow
With ws.Cells(iRow, "A")
If IsNumeric(.Value) Then 'can the value be interpreted as a number
If .Value <> 0 Then 'is the value not zero
.Value = CDbl(.Value) 'then convert it into a real number
End If
End If
End With
Next iRow
End Sub
I know you are looking for VBA solution, but here's a small Excel trick that you might find useful:
Enter 1 (numeric value) somewhere in the file and copy it:
Select your range (A1:A6) and go to Paste > Paste Special > select Multiply:
The final result is all your text values being converted to numbers:
The same trick will work with other combinations, e.g. Operation: Add while having 0 copied, etc.
I was trying to copy from column D to column E first two words of each row but still can not find where the error is....
Range("E1:E" & lLastRow).Formula = "=LEFT(D1,FIND("" "",D1,FIND("" "",D1)+1)-1)"
Another option, instead of using a Formula, you can use the Split function.
Code
Dim i As Long, LastRow As Long
Dim WordsArr As Variant
' loop through rows
For i = 1 To LastRow
WordsArr = Split(Range("D" & i).Value, " ") ' use Split and space to read cell words to array
If UBound(WordsArr) >= 1 Then ' make sure the cell contents is at least 2 words
Range("E" & i).Value = WordsArr(0) & " " & WordsArr(1) ' insert only the first 2 words
Else ' in case there are less than 2 words
' do someting....
End If
Next i
End Sub
Try this instead ...
Range("E1:E" & lLastRow).FormulaR1C1 = "=LEFT(RC[-1],FIND("" "",RC[-1],FIND("" "",RC[-1])+1)-1)"
I find using R1C1 better for those sort of operations, especially given you want your references to be dynamic, not absolute.
Alternatively, add the formula you had normally and simply fill down.
I have an Excel project which has a few thousand rows containing strings which need sorting out.
Typically one cell in each row should have a six digit number 123456 but many are 123456/123456/234567 etc. which need to have the / deleted and then be separated onto individual rows. There is other information in the surrounding columns which needs to stay with these six digit numbers.
I decided to approach this by firstly making copies of the rows the appropriate number of times and then deleting the surplus information
This code below deals with the copying part and it works.. but it's really slow. Is there a quicker way to achieve what I'm trying to do?
Thanks for any help.
Chris
Sub Copy_extra_rows()
Application.ScreenUpdating = False
s = 2
Do Until s = Range("N20000").End(xlUp).Row
'checks for / in Mod list
If InStr(1, Range("N" & s), "/") Then
'determines number of /
x = Len(Range("N" & s)) - Len(Replace(Range("N" & s), "/", ""))
'loops x times and copies new row
For a = 1 To x
Range("J" & s & ":O" & s).Select
Selection.Copy
Range("J" & s + 1).Select
Selection.Insert Shift:=xlDown
s = s + 1
Next a
Else
End If
s = s + 1
Loop
End Sub
I would have approached this differently to optimize the process and improve the overall efficiency of code.
Firstly, I would load the entire column into an array. This way it's always faster to access the elements of that array rather then referring Cells() multiple times in loops. Working with objects in memory is much faster because your client doesn't need to for example update the UI. Generally, arrays big O is O(1) which means you instantly can access an object/data stored at a specific index.
Let's consider an SSCCE.
Then the code (*Note: I have added comments in the code in the right places, hopefully that helps you understand what is going on)
Sub Main()
Dim columnArray As Variant
' create an array from Range starting at L2 to the last row filled with data
columnArray = Range("N2:N" & Range("N" & Rows.Count).End(xlUp).Row)
Dim c As New Collection
' add separate 6 digit numbers to the collection as separate items
' iterate the columnArray array and split the contents
Dim element As Variant
For Each element In columnArray
If NeedSplitting(element) Then
Dim splittedElements As Variant
splittedElements = Split(element, "/")
Dim splittedElement As Variant
For Each splittedElement In splittedElements
c.Add splittedElement
Next
Else
c.Add element
End If
Next
' print the collection to column Q
PrintToColumn c, "Q"
End Sub
Private Sub PrintToColumn(c As Collection, ByVal toColumn As String)
Application.ScreenUpdating = False
' clear the column before printing
Columns(toColumn).ClearContents
' iterate collection and print each item on a new row in the specified column
Dim element As Variant
For Each element In c
Range(toColumn & Range(toColumn & Rows.Count).End(xlUp).Row + 1) = element
Next
Application.ScreenUpdating = True
End Sub
Private Function NeedSplitting(cell As Variant) As Boolean
' returns true if the cell needs splitting
If UBound(Split(cell, "/")) > 0 Then
NeedSplitting = True
End If
End Function
After running the code all your numbers should appear as separate elements in column Q
NOTE: Why use a Collection?
Collections in VBA are dynamic. It means you don't have to know the size of a collection in order to use it - unlike arrays. You can re-dim your array multiple times to increase its size but that's rather considered a bad practice. You can add nearly as many items to a Collection as you want with a simple Collection.Add method and you don't have to worry about increasing the size manually - it's all done for you automatically. In this scenario the processing happens in memory so it should be much quicker then replacing cells contents inside a loop.
Try this:
Dim s As Integer
Dim splitted_array() As String
s = 2 'Assuming data starts at row 2
Do Until Range("N" & s).Value = vbNullString Or s >= Rows.Count
'Split the array
splitted_array = Split(Range("N" & s).Value, "/")
If UBound(splitted_array) > 0 Then
'Set the first value on the first row
Range("N" & s).Value = splitted_array(0)
For i = 1 To UBound(splitted_array)
'Add subsequent rows
Rows(s + i).Insert xlDown
Range("J" & s + i & ":O" & s + i).Value = Range("J" & s & ":O" & s).Value
Range("N" & s + i).Value = splitted_array(i)
Next
End If
s = s + 1 + UBound(splitted_array)
Loop
This code turns this:
into this:
I am using the below code to transpose a column off data, however, it still leaves trailing commas. Can someone help to clean this up?
Sub ReorganizeData()
Dim X As Long, LastRow As Long, Index As Long
Dim Sheet As Excel.Worksheet
Const Interval As Long = 1000
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For X = 1 To LastRow Step Interval
Index = Index + 1
Cells(Index, "B") = Application.Trim(Join(Application.Transpose(Cells(X, "A").Resize(Interval).Value), ","))
Next
Range("A:A").Delete xlShiftToLeft
End Sub
The below would be a quick fix but in the process actually suppress any two consecutive commas in the resulting strings regardless of position - if that may be of any concern. The trailing commas are due to joining empty cells, so there may be a better approach to just avoid joing empty cells in the first place
Sub ReorganizeData()
Dim X As Long, LastRow As Long, Index As Long
Dim Sheet As Excel.Worksheet
Const Interval As Long = 1000
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For X = 1 To LastRow Step Interval
Index = Index + 1
Cells(Index, "B") = Application.Trim(Join(Application.Transpose(Cells(X, "A").Resize(Interval).Value), ","))
'Replaces any ",," substring in the above resulting string with an empty string
Cells(Index, "B") = Replace(Cells(Index, "B"), ",,", "")
'Cleans up any possible extra single trailing ","
If Mid(Cells(Index, "B"), Len(Cells(Index, "B")), 1) = "," Then
Cells(Index, "B") = Left(Cells(Index, "B"), Len(Cells(Index, "B")) - 1)
End If
Next
Range("A:A").Delete xlShiftToLeft
End Sub
Edit: # A Hugues - are you saying this is what you are observing after putting the above code to the test? If so I am confused, the extra lines that have been added compared to your original code just consist of replacing any ",," substring within your resulting strings with an empty string - + cleaning up any extra single "," still trailing after that. If that is actually the case - is there any chance you can test as an aside that the replace function actually works as expected?
FYI I confirm this works fine on my end. I have also added a couple comments in the code for clarity
Edit2: there also potentially is the possibility that the 0s that you are seeing now actually already existed in your original strings but went unnoticed after what seemed to be a long series of traling commas, would be great if you could provide a confirmation on this