Split String to multiple cells - excel

I have a problem.
I have this:
("boufous;othman;212544;casa")
I want to split this cell to multiple cells like:
cell[1]=boufous cell[2]=othman cell[3]=212544
cell[4]=casa
For Each olMailItem In olItems
//Code here
i = i + 1
Next olMailItem

There really are two ways that I personally like working through if you must use VBA. Imagine the following data:
1. Split
As per the comments given, you can utilize a function called Split. Hereby a small script which shows how you could approach this involving a small loop:
Sub UseSplit()
Dim arr As Variant
Dim lr As Long, x As Long
With Sheet1 'Change CodeName accordingly
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A1:A" & lr)
For x = LBound(arr) To UBound(arr)
.Cells(x, 2).Resize(1, UBound(Split(arr(x, 1), ";")) + 1) = Split(arr(x, 1), ";")
Next x
End With
End Sub
2. TextToColumns
A second approach would be to utilize the build-in function to write delimited text to other columns using the TextToColumns function. This would not involve a loop. Underneath a small example:
Sub UseSplit()
Dim rng As Range
Dim lr As Long
With Sheet1 'Change CodeName accordingly
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
Set rng = .Range("A1:A" & lr)
rng.TextToColumns Destination:=.Range("B1"), Semicolon:=True
End With
End Sub
The advantage with this is that, whereas Split will return an array of string values, text to columns will not:
Values that are meant to be numeric, are actually numeric.
The question remains, do you really need to work through VBA? Either way, good luck with your project =)

Assuming olMailItem as a string and below is the code
Dim str1, str2
olMailItem = "boufous;othman;212544;casa"
str1 = Split(olMailItem, ";")
str2 = UBound(str1)
For i = 0 To str2
Debug.Print str1(i)
Next i

Related

Load a variable range into an array

I want to store a range of variable size in an one-dimensional array. The range starts at A2 and goes to the last row of the same column. My approach looks like that. It's flawed.
Option Explicit
Sub Range_to_Array()
Dim i, j, k As Integer
Dim arr1(), arr2(), arr3(), arr4() As Variant
With Worksheets("table1")
arr1() = .Cells(.Range("A2"), .Range("A1").End(xlDown).Row)
End With
End Sub
Please, try the next way. Application.Transpose transforms a 2D array with a column in a 1D type. No iteration needed:
Sub Array1DFromColumnRange()
Dim ws As Worksheet, lastR As Long, arr
Set ws = Worksheets("table1")
lastR = ws.Range("A" & ws.rows.count).End(xlUp).Row
arr = Application.Transpose(ws.Range("A2:A" & lastR).Value)
Debug.Print Join(arr, "|") 'just to visually see the result in Immediate Window (Ctrl + G)...
End Sub
The returned 1D array is 1 based, as the 2D array directly extracted from a range. To transform it in zero based type, can be done without iteration, too:
'change the array type to be zero based:
ReDim Preserve arr(0 To UBound(arr) - 1)
Debug.Print LBound(arr)
Debug.Print Join(arr, "|")
Your problem is that your Range-Definition is wrong.
Cells expect 2 parameters (row and column) to address one cell. This is not what you want, and even if, your parameters would be wrong.
What you need in your case is Range.
Now Range can be called either with one or two parameters.
If you call it with one parameter, this defines the whole range.
Examples: Range("A1") or Range("B2:C5") or Range("B:B")
Whats often used in VBA is something like Range("A1:A" & lastRow)
If you call it with two parameters, those two parameters define the first and last cell of the range.
Examples: Range(Range("A1"), Range("C10")) or Range(Cells(1, 1), Cells(10, 3))
I would advice to define an intermediate variable to save the Range - makes it much easier to debug. Also the row number of the last cell should go into an intermediate variable.
In your case you could use one of the following
Dim r As Range, lastRow As Long
' Get the last row in use (usually its better to go up from the end of the sheet)
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
' 2 parameters
Set r = .Range(.Cells(2, 1), .Cells(2, lastRow))
' 1 Parameter, last row is concatenated to the range definition
Set r = .Range("A2:A" & lastRow)
' Use Resize
Set r = .Range("A2").Resize(lastRow-1, 1) ' -1 because you start at row 2
arr1 = r.Value
Try this instead.
Sub Range_to_Array()
Dim i As Integer, j As Integer, k As Integer
Dim arr1() As Variant, arr2() As Variant, arr3() As Variant, arr4() As Variant
Dim myRange As Range
With Worksheets("table1")
arr1 = .Range(.Range("A2"), .Range("A1").End(xlDown)).Value
End With
Debug.Print arr1(1, 1)
End Sub
Also please note that in order to properly declare variables, you need to specify data type for each variable separately.
Dim i, j, k As Integer
actually means
Dim i As Variant, j As Variant, k As Integer

Sum values on column A based on a string criteria in column B

I have the following columns:
A B
23 75001
42 94
1 13
3 75002
4 12
I would like to sum values of column A if the two first digits of the number in column B is matching 75 or 12.
In this example, the result would be 23 + 3 + 4 = 30.
I tried to use =SUMIFS but it looks like I can only use a criteria based on a integer ...
Do you have an idea how to do this ?
EDIT : I am trying to make a VBA macro
One option using SUMPRODUCT:
=SUMPRODUCT(A1:A5*((--LEFT(B1:B5,2)=75)+(--LEFT(B1:B5,2)=12)))
Similarly, using FILTER if you have access to it:
=SUM(FILTER(A1:A5,((--LEFT(B1:B5,2)=75)+(--LEFT(B1:B5,2)=12))))
Even shorter:
=SUMPRODUCT(A1:A5*(LEFT(B1:B5,2)={"75","12"}))
or if you don't want to put quotes around the numbers
=SUMPRODUCT(A1:A5*(--LEFT(B1:B5,2)={75,12}))
though keeping the quotes around 75 and 12 is a better approach if you have one or more blank cells in B1:B5.
You could use:
=SUMPRODUCT(IF(ISNUMBER(SEARCH({"|75";"|12"},"|"&B1:B5)),A1:A5,0))
Though, if you'd have ExcelO365, you could use SUM() instead. If you need this to be VBA you could mimic this quite easily. Don't let it be mistaken, the other answer is the go-to method, however, this way you could easily add more criteria if need be.
To mimic this in VBA you could use an array and Select Case if you would need to add more criteria in the future:
Sub Test()
Dim lr As Long, x As Long, sm As Double, arr As Variant
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
With ws
lr = .Cells(.Rows.Count, 2).End(xlUp).Row
arr = .Range("A1:B" & lr).Value
For x = LBound(arr) To UBound(arr)
Select Case Left(arr(x, 2), 2)
Case "75", "12": sm = sm + arr(x, 1)
End Select
Next
Debug.Print sm
End With
End Sub
Try the next function, please:
Function countOccLeft(arr As Variant, Cr1 As String, Cr2 As String) As Long
Dim dict As Object, i As Long: Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(arr)
If Left(arr(i, 2), 2) = Cr1 Or Left(arr(i, 2), 2) = Cr2 Then
If Not dict.Exists("key_sum") Then
dict.Add "key_sum", arr(i, 1)
Else
dict("key_sum") = dict("key_sum") + arr(i, 1)
End If
End If
Next i
countOccLeft = dict("key_sum")
End Function
It can be called in this way:
Sub testCountOccL()
Dim sh As Worksheet, arr As Variant, strSearch As String, lastRow As Long
Set sh = ActiveSheet
lastRow = sh.Range("A" & Rows.Count).End(xlUp).Row
arr = sh.Range("A2:B" & lastRow).Value
Debug.Print countOccLeft(arr, "75", "12")
End Sub

Using VBA to vlookup each comma separated value in one cell and return emails

I am hoping someone knows how to vlookup multiple comma separated values in one cell and provide semicolon separated output in the adjacent cell.
I have noticed two other instances of this question on Stack Overflow but, unfortunately, both referred to using formulas (textjoin and vlookup) to solve this issue. Due to another VBA formula I am using, I need the final output to result solely in the text information, not in a formula. Is there any way to do this using VBA? Thanks in advance.
Figured out how to use the vlookup with the split using Ben's suggestion. Only issue is it puts a semicolon at the start of my email string, which is no issue for me but may be for another user.
Sub FINDEM()
Dim ws As Worksheet
Dim cel As Range
Dim LastRow As Long, I As Long
Dim WrdArray() As String
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row 'last row with data in Column A
For Each cel In .Range("A2:A" & LastRow).Cells 'loop through each cell in Column A
strg = ""
Sal = ""
WrdArray() = Split(cel, ", ")
For I = LBound(WrdArray) To UBound(WrdArray)
Sal = Sal & "; " & Application.WorksheetFunction.VLookup(WrdArray(I), Sheet1.Range("d2:e9"), 2, False)
cel.Offset(0, 1) = Sal
Next I
Next
End With
End Sub
You can do so without iteration, plus you might want to take into consideration removing duplicates. For example:
Sub Test()
Dim lr As Long
Dim arr As Variant, arrA As Variant, arrB As Variant
With ThisWorkbook.Sheets("Sheet1")
'Get last used row and data into memory
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A2:B" & lr).Value
'Join and transpose column A:A and B:B into their own array
With Application
arrA = Split(.Trim(Join(.Transpose(.Index(arr, 0, 1)), ",")), ",")
arrB = Split(.Trim(Replace(Join(.Transpose(.Index(arr, 0, 2)), ";"), Chr(10), "")), ";")
End With
'Write array to sheet
.Range("D2").Resize(UBound(arrA) + 1).Value = Application.Transpose(arrA)
.Range("E2").Resize(UBound(arrB) + 1).Value = Application.Transpose(arrB)
'Remove duplicates from column D:E
.Range("D2:E" & UBound(arrA) + 1).RemoveDuplicates Array(1, 2), xlNo
End With
End Sub

Concatenate the values in one column separated by '/' based on the values assigned to the another column

I have an excel sheet which contains two columns called ProductName and CountryCode.i wanted to concatenate all the CountryCode separated by / based on the corresponding values in the column 'ProductName' and My output would be obtained in a separate column called 'FinalResults'. Please note that I used remove duplicate function to get unique values in Column C from Column A.
I tried the below VBA code with the help of stackoverflow and got the results.
Sub ProductCountry()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet2")
Dim FoundCell As Range, SearchRange As Range, Names As Range, SearchCell As Range
Dim MyString As String, i As Long
Set SearchRange = ws.Range("A2:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
SearchRange.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=ws.Range("C2"), Unique:=True
ws.Range("C2").Delete Shift:=xlShiftUp
Set Names = ws.Range("C2:C" & ws.Range("C" & ws.Rows.Count).End(xlUp).Row)
For Each SearchCell In Names
Set FoundCell = SearchRange.Find(SearchCell)
For i = 1 To Application.WorksheetFunction.CountIf(SearchRange, SearchCell)
MyString = MyString & FoundCell.Offset(, 1) & "/"
Set FoundCell = SearchRange.FindNext(FoundCell)
Next i
SearchCell.Offset(, 1) = Left(MyString, Len(MyString) - 1)
MyString = ""
Next SearchCell
End Sub
Seems it works fine except for the first product PRO1. You could see it didn't concatenate the codes orderly and skipped the country code US and took the country code SG two times instead.
Can anyone help what went wrong in this script and I also got range error sometime if I use this same code for large data.
I rewrote it ...
Public Function ConcatenateCodes(ByVal strProductName As String, ByVal rngCells As Range, Optional ByVal strDelimiter As String = "/") As String
Application.Volatile
Dim objCell As Range, lngRow As Long, lngCol As Long, strThisProductName As String
Dim strCountry As String, lngBlank As Long
For lngRow = 1 To rngCells.Rows.Count
strThisProductName = Trim(rngCells.Cells(lngRow, 1))
strCountry = Trim(rngCells.Cells(lngRow, 2))
If strThisProductName & strCountry = "" Then
lngBlank = lngBlank + 1
Else
lngBlank = 0
If strProductName = strThisProductName Then
ConcatenateCodes = ConcatenateCodes & strDelimiter & strCountry
End If
End If
If lngBlank = 10 Then Exit For
Next
If ConcatenateCodes <> "" Then ConcatenateCodes = Mid(ConcatenateCodes, 2)
End Function
... I'm comfortable with the above but that's just me. It means the data doesn't need to be sorted and it will work.
Add the formula to your cell and watch it go.
If you concern about speed you should use arrays to handle your data:
Option Explicit
Public Sub CollectList()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
'read values into array
Dim InputValues() As Variant
InputValues = ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp).Offset(0, 1)).Value
Dim UniqueList As Object
Set UniqueList = CreateObject("Scripting.Dictionary")
'collect all products in a dictionary
Dim iRow As Long
For iRow = 1 To UBound(InputValues, 1)
If UniqueList.Exists(InputValues(iRow, 1)) Then
UniqueList(InputValues(iRow, 1)) = UniqueList(InputValues(iRow, 1)) & "/" & InputValues(iRow, 2)
Else
UniqueList.Add InputValues(iRow, 1), InputValues(iRow, 2)
End If
Next iRow
'output dictionary into cells
iRow = 2 'start output in row 2
Dim itm As Variant
For Each itm In UniqueList
ws.Cells(iRow, "C").Value = itm
ws.Cells(iRow, "D").Value = UniqueList(itm)
iRow = iRow + 1
Next itm
End Sub
As can be seen by the other responses, there are many ways to accomplish your task.
But read VBA HELP for the Range.Find method
I submit the following to help you understand where you went wrong:
This is your problem line:
Set FoundCell = SearchRange.Find(SearchCell)
You only specify the what argument for the Find. So other arguments default to some uncontrolled value. In general, the after argument will default to the beginning of the range, so the first matching term you will Find for PRO1 will be in A3. Also, the 2nd SG is being picked up because the lookat is defaulting to xlPart and PRO1 is contained within PRO10.
So one way of correcting that portion of your code, would be to be sure to specify all the relevant arguments of the Find. eg:
Set FoundCell = SearchRange.Find(what:=SearchCell, after:=SearchRange.End(xlDown), lookat:=xlWhole)

How to copy only visible/filtered array values to clipboard?

So I have a worksheet of data and I want to copy a comma-delimited array to my clipboard. If I have to paste the value into a cell first, that is fine as well. The worksheet has autofilters on and is filtered. I only want to select the values that are currently visible due to the filtering, not the whole array.
The array is in column P and starts in P2. I have a LastRow set up, and have been able to get the comma-delimited part to work, but am having trouble with the copying to clipboard part and the visible values only part.
The code below creates the comma-delimited list and I can show it in a message box or something, but I'm not sure how to copy it to the clipboard or how to make sure only visible values are being selected.
Dim LastRow As Long
LastRow = Range("P" & Rows.Count).End(xlUp).Row
Dim arr
arr = Join(Application.Transpose(Range("P2:P" & LastRow).Value), ",")
Try this code
Sub Test()
Dim arr, rng As Range, c As Range, n As Long
Set rng = Range("P2:P" & Cells(Rows.Count, "P").End(xlUp).Row).SpecialCells(xlCellTypeVisible)
ReDim a(1 To rng.Cells.Count)
For Each c In rng
n = n + 1: a(n) = c.Value
Next c
arr = Join(a, ",")
End Sub
Range("P2:P" & Cells(Rows.Count, "P").End(xlUp).Row).SpecialCells(xlCellTypeVisible)

Resources