I am trying to split a string and create a loop for going through the cells in the column.There are a few challenges:
Split works for ActiveCell only.
Loop goes through all cells until LastRow but populates all cells
with split string values from ActiveCell only.
Split of Array starts with i = 0 even though there is Option Base 1
at the beginning of the Module.
How can I change the location of destination (e.g. instead of
splitting string next to existing data, is there an option to manage
column numbers)?
Thank you
Option Explicit
Option Base 1
Sub SplitStringLoop()
Dim txt As String
Dim i As Integer
Dim y As Integer
Dim FullName As Variant
Dim LastRow As Single
ReDim FullName(3)
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
txt = ActiveCell.Value
FullName = Split(txt, "-")
For y = 2 To LastRow
For i = 1 To UBound(FullName)
Cells(y, i + 1).Value = FullName(i)
Next i
Next y
End Sub
Chris Nelisen outlined the reasons, I had this code written before he posted, so I'll post it anyway.
Option Explicit
Sub SplitStringLoop()
Dim txt As String
Dim i As Integer
Dim y As Integer
Dim FullName As Variant
Dim LastRow As Single
ReDim FullName(3)
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For y = 2 To LastRow
Cells(y, 1).Select
txt = ActiveCell.Value
FullName = Split(txt, "-")
For i = 0 To UBound(FullName)
Cells(y, i + 2).Value = FullName(i)
Next i
Next
End Sub
To address the issues you list
Split acts on the string you pass to it. You are passing the active cell value to it.
You don't update the result of split (FullName) inside the loop. So what else do you expect?
Split Returns a zero-based, one-dimensional array. It says so right there in the help. Option Base 1 specifies the default lower bound, for when you don't specify it in a Dim statement.
You are specifying the column in your code Cells(y, i + 1) (i + 1 in this case). If you want it somewhere else, specify a different column.
this is my sulotion
Public Function CheckEmailsValid(EmailAddresses As String) As Boolean
On Error GoTo Err_1
Dim V_Tempi As Integer
Dim V_Email As Variant
For Each V_Email In Split(EmailAddresses, ";")
V_Tempi = V_Tempi + 1
If CheckEmailValid(V_Email) = False Then
MyMsgBox 2, "Email " & V_Tempi & " Is invalid"
CheckEmailValidFew = False
Exit Function
End If
Next
CheckEmailValidFew = True
Exit_1:
Exit Function
Err_1:
MyMsgBox 2, "Error !!" & vbCr & Err.Number & vbCr & Err.Description
End Function
Related
Is there a clean and tidy way to get cells split ONLY by sheet name? I have a bunch of cells that look something like this.
=(Xlookup($A2,Staff!A:A,Client!K:K)*E2
=B3*(Xlookup(E3,Auto!1:1,Desc!3:3)
And, all kinds of other stuff. Basically, I am trying to parse out only the sheet names from each cell. Each sheet name ends with a '!' character. So, I am trying to split one cell into multiple columns, based on the '!' character, and ignore any text that is not a sheet name. I tested the script below, but all it does is a basic split from one cell into multiple columns, which includes the sheet name, but all kinds of superfluous text, which I don't want.
Sub SplitData()
Const SrcCol = 1 ' A
Const TrgCol = 2 ' B
Const FirstRow = 1
Dim LastRow As Long
Dim SrcRow As Long
Dim TrgRow As Long
Dim TheVal As String
Dim TheArr As Variant
Dim Num As Long
Application.ScreenUpdating = False
TrgRow = 1
LastRow = Cells(Rows.Count, SrcCol).End(xlUp).Row
For SrcRow = FirstRow To LastRow
TheVal = Cells(SrcRow, SrcCol).Value
TheArr = Split(TheVal, ",")
Num = UBound(TheArr) + 1
Cells(TrgRow, TrgCol).Resize(ColumnSize:=Num).Value = TheArr
TrgRow = TrgRow + 1
Next SrcRow
Application.ScreenUpdating = True
End Sub
Now:
Desired:
If you have O365, this will work for you ...
=LET(x, TRANSPOSE(FILTERXML("<d><r>" & SUBSTITUTE(A1, ",", "</r><r>") & "</r></d>", "//r[contains(text(),""!"")]")), MID(x, 1, FIND("!", x)))
... here's hoping you do, a lot easier.
Alternatively, I created my own VBA routine with the assumption that everything to the right of the formula is free to load into, just adjust for errors, names, performance, etc. as required ...
Public Sub GetWorksheets()
Dim lngRow As Long, lngColumn As Long, strFormula As String
Dim arrFormula() As String, i As Long, arrSubFormula() As String
With Sheet1
For lngRow = 1 To .Cells(.Rows.Count, 1).End(xlUp).Row
strFormula = Trim(.Cells(lngRow, 1))
lngColumn = 2
If strFormula <> "" Then
arrFormula = Split(strFormula, "!")
For i = 0 To UBound(arrFormula) - 1
arrSubFormula = Split(arrFormula(i), ",")
strFormula = arrSubFormula(UBound(arrSubFormula)) & "!"
.Cells(lngRow, lngColumn) = strFormula
lngColumn = lngColumn + 1
Next
End If
Next
End With
End Sub
I have a sheet that I need to paste data to according to the validation lists in those sheets. In the sheet, there are many columns each with their own data validation list - some are written directly as "yes;no" others are references "='$$VALUES$$'!$IJ$1:$IJ$12".
What I need is to find a way to add each item in each list to an array. Using the code below I could find the references above.
Debug.Print Cells(2, 6).Validation.Formula1
Is there any elegant way to store the output as a list containing each valid input. My only idea so far is to first check which type of output I get, and then if it is the list form of "yes;no" then look for the number of ; and then split it item by item. And in case its the sheet range reference split it by sheet and range and convert that range to an array.
Something like this, will do it. I'd set a range rather than using activecell, and also check validation is present to reduce your errors.
Sub get_val_lists()
Dim arrOutput() As Variant
If Left(ActiveCell.Validation.Formula1, 1) <> "=" Then
arrOutput = Split(ActiveCell.Validation.Formula1, ",")
Else
arrOutput = Application.Transpose( _
Range(Mid(ActiveCell.Validation.Formula1, 2)).value)
End If
End Sub
I was a bit pressed for time so I ended up doing an inelegant solution myself. Posting it here in case somebody else runs into the same problem.
Sub ValidList()
Dim strFormula As String
Dim intLastSemi As Integer
Dim intCurSemi As Integer
Dim intSemi As Integer
Dim aryList() As Variant
Dim intLen As Integer
Dim blnCont As Boolean
Dim strSheet As String
Dim strRange As String
Dim intSplit As Integer
Dim ws As Worksheet
Dim rng As Range
Dim e As Variant
Dim Row As Integer
Dim Col As Integer
'This is just an example, turning it into a fucntion based on row and col later
'so now my test validation list is just in A1
Row = 1
Col = 1
strFormula = Cells(Row, Col).Validation.Formula1
intLen = Len(strFormula)
If InStr(1, strFormula, "=") Then 'Sheet reference
intSplit = InStr(1, strFormula, "!")
strSheet = Right(Left(strFormula, intSplit - 1), intLen - intSplit - 3)
strRange = Right(strFormula, intLen - intSplit)
Set ws = Worksheets(strSheet)
Set rng = ws.Range(strRange)
aryList() = rng
ElseIf Not InStr(1, strFormula, ";") Then 'Hardcoded list
intSemi = 0
intLastSemi = 0
blnCont = True
While blnCont
intCurSemi = InStr(intLastSemi + 1, strFormula, ";")
If intCurSemi <> 0 Then
intSemi = intSemi + 1
ReDim Preserve aryList(intSemi)
aryList(intSemi) = Right(Left(strFormula, intCurSemi - 1), intCurSemi - intLastSemi - 1)
intLastSemi = intCurSemi
ElseIf intCurSemi = 0 Then
intSemi = intSemi + 1
ReDim Preserve aryList(intSemi)
aryList(intSemi) = Right((strFormula), intLen - intLastSemi)
blnCont = False
End If
Wend
End If
'For my attempt at passing the array to a function
'For Each e In aryList
' MsgBox e
'Next
'ReDim ValidList(UBound(aryList))
'ValidList = aryList
End Sub
I'm using the split function to split text using spaces. I have gotten my macro to split the text but I am having difficulties getting the loop to move to the next row below to split.
Sub Split_Text_Test1()
Dim Txt As String
Dim i As Integer
Dim FullName As Variant
Range("A1").Select
Txt = ActiveCell.Value
FullName = Split(Txt, " ")
For i = 0 To UBound(FullName)
Cells(1, i + 1).Value = FullName(i)
Next i
End Sub
You probably need to change the bit inside your loop thus as you are starting at A1. This assumes you want the entries in A2 and down. Not generally advisable to use Select/Activate, not very robust.
Edited to move across columns rather than down rows.
For i = 0 To UBound(FullName)
Range("A1").Offset(,i + 1).Value = FullName(i)
Next i
That said, you can avoid a loop altogether and use
Range("B1").Resize(, UBound(FullName) + 1).Value = FullName
In this case I would use a loop (and your solution was not that far from this):
Dim Txt As String
Dim i As Integer
Dim FullName As Variant
Dim R As Integer, C As Integer, MaxR as Integer
C = 1 ' can be another loop as well
For R = 1 to 1000
Txt = Trim(Cells(r,1).Value) ' will remove space from start and end
FullName = Split(Txt, " ")
For i = 0 To UBound(FullName)
Cells(R , C + 1 + i ).Value = FullName(i)
Next i
Next R
Sub Split_Text_Test1()
Dim Txt As String
Dim i As Integer
Dim FullName As Variant
Dim R As Integer, C As Integer
Range("A1").Select ' assumes that the cells below that are empty
Txt = ActiveCell.Value
FullName = Split(Txt, " ")
R = ActiveCell.Row
C = ActiveCell.Column
For i = 0 To UBound(FullName)
Cells(R + 1 + i, C).Value = FullName(i)
Next i
End Sub
I added few thing to your code, see if that serves your purpose. However, as SJR said Text to columns option in Data menu would do the same with less effort.
Sub Split_Text_Test1()
Dim Txt As String
Dim i As Integer
Dim FullName As Variant
Dim lastRow As Long
Dim myRange As Range
With ActiveSheet.UsedRange
lastRow = .Rows(.Rows.Count).Row
End With
Debug.Print lastRow
'Range("A1").Select
Set myRange = ActiveSheet.Range("A1", "A" & lastRow)
For Each cell In myRange
Txt = cell.Value
FullName = Split(Txt, " ")
For i = 0 To UBound(FullName)
Cells(cell.Row, i + 1).Value = FullName(i)
Next i
Next cell
End Sub
I'm trying to parse a column that contains data in the following format in each cell -
pull: test1
or
pull: test2|pull: test3|.....
or
other: blah...
I only want a grab each "Pull: test" and place 1 in each row in a new worksheet like below, and ignore any parts of the cell that don't begin with "pull: " -
pull: test1
pull: test2
pull: test3
...
What I have so far just pulls the entire column and pastes into the same spreadsheet, I'm not sure how to separate the items in each cell into their own rows. I also can't get it to pull to a different worksheet correctly either (commented out my attempt)
Sub InStrDemo()
Dim lastrow As Long
Dim i As Integer, icount As Integer
'Sheets.Add.Name = "TEST"
lastrow = ActiveSheet.Range("A10000").End(xlUp).Row
For i = 1 To lastrow
If InStr(1, LCase(Range("E" & i)), "pull:") <> 0 Then
icount = icount + 1
'Sheets("TEST").Range("A" & icount & ":E" & icount) = Worksheets("SearchResults").Range("A" & i & ":E" & i).Value
Range("L" & icount) = Range("E" & i).Value
End If
Next i
End Sub
Untested, written on mobile.
Option Explicit
Sub testDemo()
Dim sourceSheet as worksheet
Set sourceSheet = ActiveSheet ' would be more reliable to qualify the workbook and worksheet by name'
Dim outputSheet as worksheet
Set outputSheet = thisworkbook.worksheets.add
Dim lastRow As Long
lastrow = sourceSheet.Range("A10000").End(xlUp).Row
' I assume column E needs to be parsed'
Dim arrayOfValues() as variant
arrayOfValues = sourceSheet.range("E1:E" & lastRow)
Dim rowIndex as long
Dim columnIndex as long
Dim splitString() as string
Dim cumulativeOffset as long
Dim toJoin(0 to 1) as string
toJoin(0) = "pull: test" ' Might speed up string concatenation below'
Dim outputArray() as string
With outputsheet.range("A1") ' The first row you want to start stacking from'
For rowIndex = 1 to lastRow
' Single dimensional, 0-based array'
splitString = VBA.strings.split(vba.strings.lcase$(arrayOfValues(rowIndex,1)), "pull: test",-1, vbbinarycompare)
Redim outputArray(1 to (ubound(splitString)+1), 1 to 1)
For columnIndex = lbound(splitString) to ubound(splitString)
toJoin(1) = splitString(columnIndex)
Outputarray(columnIndex+1,1) = VBA.strings.join(toJoin, vbnullstring)
Next columnIndex
'Instead of splitting upon a delimiter, then prepending the delimiter to each array element (as is done above), you could repeatedly call instr(), use mid$() to extract the sub-string, then increase the argument passed to the "Start" parameter in instr() (effectively moving from start to end of the string) -- until instr() returns 0. Then move on to the next string in the outer loop.'
.offset(cumulativeOffset,0).resize(Ubound(outputArray, 1), 1).value2 = outputArray
cumulativeOffset = cumulativeOffset + ubound(splitString)
Next rowIndex
End Sub
basically I need to split a cell that has a few values, seperated by a comma into more cells. Then i need to create the exact amount of the cells under the new cells to be able to transpose this range later to have a new table.
In the picture you can see an example of what I have and what I need. I needed to anonymyze the data. Also I have hundreds of rows that need to changed like the 2 in the example.
Ths is my current code:
Sub texttocolumns()
Dim rng As Range
Dim x As Integer
x = ActiveSheet.UsedRange.Rows.Count
For i = x - 2 To 1
Cells(2 + i, 8).texttocolumns _
Destination:=Cells(2 + i, 9), _
Comma:=True
k = Application.WorksheetFunction.CountA("A" & "2 + i"" & "":" & "AT1")
Cells(2 + i, 1).Rows(k).Insert
Next i
End Sub
I can't find my mistake at the moment, could someone please help me out? thanks!
Since the output result is posted to a different location the expensive task of inserting rows can be avoided.
Try this procedure, which also avoids working with the source range by generating from it two Arrays:
An array containing the fixed fields
An array containing the field that needs to be split
The Procedure:
Sub Range_Split_A_Field()
Dim wsTrg As Worksheet, rgOutput As Range
Dim aFld_1To5 As Variant, aFld_6 As Variant
Dim aFld As Variant
Dim lRow As Long, L As Long
lRow = 3
Set wsTrg = ThisWorkbook.Sheets("Sht(2)")
Application.Goto wsTrg.Cells(1), 1
With wsTrg.Cells(lRow, 1).CurrentRegion
Set rgOutput = .Rows(1).Offset(0, 10)
.Rows(1).Copy
rgOutput.PasteSpecial
Application.CutCopyMode = False
aFld_1To5 = .Offset(1, 0).Resize(-1 + .Rows.Count, 5).Value2
aFld_6 = .Offset(1, 5).Resize(-1 + .Rows.Count, 1).Value2
End With
lRow = 1
For L = 1 To UBound(aFld_1To5)
aFld = aFld_6(L, 1)
If aFld = vbNullString Then
rgOutput.Offset(lRow).Resize(1, 5).Value = WorksheetFunction.Index(aFld_1To5, L, 0)
rgOutput.Offset(lRow, 5).Resize(1, 1).Value = aFld
lRow = 1 + lRow
Else
aFld = Split(aFld, Chr(44))
aFld = WorksheetFunction.Transpose(aFld)
rgOutput.Offset(lRow).Resize(UBound(aFld), 5).Value = WorksheetFunction.Index(aFld_1To5, L, 0)
rgOutput.Offset(lRow, 5).Resize(UBound(aFld), 1).Value = aFld
lRow = lRow + UBound(aFld)
End If: Next
End Sub
Please see the following pages for a better understanding of the resources used:
Application.Goto Method (Excel)
With Statement
Range Object (Excel)
Chr Function
UBound Function
WorksheetFunction Object (Excel)
Would something like this work:
'A1 = A,B,C,D,E,F,G
'A2 = 1,2,3,4,5,6,7
'A3 = A!B!C!D!E!F!G
'Test procedure will result in:
'A - G in cells A1:A7
'1,2,3,4,5,6,7 in cell A8.
'A - G in cells A9:A15
Sub Test()
TextToColumns Sheet1.Range("A1")
TextToColumns Sheet1.Range("A9"), "!"
End Sub
Public Sub TextToColumns(Target As Range, Optional Delimiter As String = ",")
Dim rng As Range
Dim lCount As Long
Dim x As Long
'How many delimiters in target string?
lCount = Len(Target) - Len(Replace(Target, Delimiter, ""))
'Add the blank rows.
For x = 1 To lCount + 1
Target.Offset(1).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Next x
'Split the string.
Target.TextToColumns Target, xlDelimited, xlTextQualifierNone, , , , , , True, Delimiter
'Use TRANSPOSE formula to paste to rows and then remove formula.
With Target.Offset(1).Resize(lCount + 1, 1)
.FormulaArray = "=TRANSPOSE(R" & Target.Row & "C:R" & Target.Row & "C" & lCount + 1 & ")"
.Value = .Value
End With
'Delete the original text string.
Target.EntireRow.Delete
End Sub
Edit:
To use from the Macro dialog box you could add this small procedure:
Public Sub Test()
Dim y As Long
y = ActiveSheet.UsedRange.Rows.Count
With ActiveSheet
For y = 5 To 1 Step -1
TextToColumns .Cells(y, 1)
Next y
End With
End Sub
Note: ActiveSheet.UsedRange.Rows.Count is a terrible way to find the last row.
See this thread: Error in finding last used cell in VBA