Excel VBA - String Conactenation with delimiter and single quotes - vba

I have a column of cells. They need to be concatenated into a single string with multiple delimiters in between.
I need something like the result in column 2 from the values in column 1
Column 1 | column 2
a1 | 'a1'
a2 | 'a1';'a2'
a3 | 'a1';'a2';'a3'
a4 | 'a1';'a2';'a3';'a4'
a5 | 'a1';'a2';'a3';'a4';'a5'
Currently I use the following formula
Column 1 | Column 2
a1 | ="'"&a1&"'"&";"
a2 | =b1&"'"&a2&"'"&";"
a3 |
and copy it down the column B2.
Is there a VBA code that could help me do that. I did find some that would add a single delimiter ' between the cells but I could not modify it to add multiple delimiters.
It would be very helpful could share a VBA code for the same.

You can use the Join method in VBA with a given delimeter:
e.g.
someArray = Array("some", "words", "here")
Debug.Print "'" & Join(someArray, "';'") & "'"
'// will print:
'// 'some';'words';'here'

I know you asked for VBA, but you can do this with a formula:
In B2:
=IF(ROW()=2,TEXTJOIN(";",TRUE,"'"&B1,"'"&A2&"'"),TEXTJOIN(";",TRUE,B1,"'"&A2&"'"))
and drag down:
Or
In B1, use ="'"&A1&"'", then in B2 (and drag down):
=SUBSTITUTE(TEXTJOIN(";",TRUE,"'"&B1,"'"&A2&"'"),"'","",1)

You can achieve desired output using this formula:
=CONCATENATE(B2, " ; '", A3, "'")
First put value from cell A2 to B2 manually (using ="'" & A2 & "'" formula) and then paste this formula in cell B3 and drag it down.
Here is the result with updated formula:
UPDATE (Thanks to BruceWayne)
Enter the value in cell B2 using:
="'" & A2 & "'"
So it will take the first '

I would just do a simple loop like so.
Sub combineRows()
'start and end rows, assuming column A
Dim startRow, endRow As Integer
Dim myString, myAdd As String
startRow = 2
endRow = 6
For i = startRow To endRow
myAdd = "'" & Range("A" & i) & "'" & ";"
myString = myString + myAdd
Range("B" & i) = myString
Next i
End Sub

Here's my JoinRange function. It has a few more options than you're looking for.
Public Function JoinRange(rInput As Range, _
Optional sDelim As String = vbNullString, _
Optional sLineStart As String = vbNullString, _
Optional sLineEnd As String = vbNullString, _
Optional sBlank As String = vbNullString, _
Optional sQuotes As String = vbNullString, _
Optional IgnoreBlanks As Boolean = True) As String
Dim vaCells As Variant
Dim i As Long, j As Long
Dim lCnt As Long
Dim aReturn() As String
If rInput.Cells.Count = 1 Then
ReDim aReturn(1 To 1)
aReturn(1) = sQuotes & rInput.Value & sQuotes
Else
vaCells = rInput.Value
ReDim aReturn(1 To rInput.Cells.Count)
For i = LBound(vaCells, 1) To UBound(vaCells, 1)
For j = LBound(vaCells, 2) To UBound(vaCells, 2)
If Len(vaCells(i, j)) = 0 Then
If Not IgnoreBlanks Then
lCnt = lCnt + 1
aReturn(lCnt) = sQuotes & sBlank & sQuotes
End If
Else
lCnt = lCnt + 1
aReturn(lCnt) = sQuotes & vaCells(i, j) & sQuotes
End If
Next j
Next i
ReDim Preserve aReturn(1 To lCnt)
End If
JoinRange = sLineStart & Join(aReturn, sDelim) & sLineEnd
End Function
use it in B1 like
=JoinRange($A$1:A1,";")
and fill down.

For the sake of avoiding visual confusion, I will offer CHAR(39)&CHAR(59)&CHAR(39) in place of "';'".
In B1, use this:
=CHAR(39)&TEXTJOIN(CHAR(39)&CHAR(59)&CHAR(39), TRUE, A$1:A1)&CHAR(39)
Fill down.
... or if the end result is the only important thing,
=CHAR(39)&TEXTJOIN(CHAR(39)&CHAR(59)&CHAR(39), TRUE, A1:A5)&CHAR(39)
A single tick (aka single quote or ') is ASCII character 39 and a semi-colon is ASCII character 59.

Related

How to replace string with value contained in cells?

I have a column containing formulas as "strings", i.e. "=+I11+I192+I245+I280"
I need to replace the cells (I11, I192,I245andI280`) ID with the content (strings) contained in the cells themselves.
Example:
Cell X --> "=+I11+I192+I245+I280"
Cell I11 = 'A'
Cell I192 = 'B'
Cell I245 = 'C'
Cell I280 = 'D'
The formula should generate "=+A+B+C+D".
This?
="=+" & I11 &"+" & I192 &"+" & I245 & "+" & I280
Well, how about :
=I11 & I192 & I245 & I280
Or you can include spaces
=I11 & " " & I192
But straight quotes - my phone is being funny...
The formula should generate --> "=+A+B+C+D"
Try,
="=+"&textjoin("+", true, I11, I192, I245, I280)
Don't know what you will be doing with empty cells so here is draft
Public Sub test()
[I11] = "A": [I192] = "B": [I245] = "C": [I280] = "D"
Debug.Print ConvertedString("=+I11+I192+I245+I280")
End Sub
Public Function ConvertedString(ByVal inputString As String) As Variant
Dim arr() As String, i As Long
On Error GoTo errHand
If Not InStr(inputString, Chr$(43)) > 0 Then
ConvertedString = CVErr(xlErrNA)
Exit Function
End If
arr = Split(inputString, Chr$(43))
For i = 1 To UBound(arr)
arr(i) = Range(arr(i))
Next i
ConvertedString = Join(arr, Chr$(43))
Exit Function
errHand:
ConvertedString = CVErr(xlErrNA)
End Function
I think you mean something like
=INDIRECT(I11,TRUE)+INDIRECT(I192,TRUE)+INDIRECT(I245,TRUE)+INDIRECT(I280,TRUE)
but please note that Indirect is a volatile function, and can slow your calculations down if used extensively.
Using VBA (with only single delimiter):
Function ReplaceAddr(sInput As String, Optional sDelimiter As String = "+") As String
Dim sArr
Dim i As Long
sArr = Split(sInput, sDelimiter)
For i = 1 To UBound(sArr)
sArr = Range(sArr(i))
Next i
ReplaceAddr = Join(sArr, sDelimiter)
End Function
From OP's comment:
The problem is that formulas changes, so I can't only change manually. The one I gave you is only an example, but I have so many different ones with all math operators.
You can try finding cell addresses with regular expression and replace with cell's value:
Function ReplaceAddr2(sInput As String) As String
Dim oRegEx As Object
Dim oMatches As Object
Dim i As Long, lStart As Long, lLength As Long
Set oRegEx = CreateObject("vbscript.regexp")
oRegEx.Pattern = "[A-Za-z]{1,3}\d{1,7}"
oRegEx.Global = True
oRegEx.MultiLine = True
Set oMatches = oRegEx.Execute(sInput)
lStart = 0
For i = 0 To oMatches.Count - 1
lLength = oMatches(i).FirstIndex - lStart
ReplaceAddr2 = ReplaceAddr2 & Mid$(sInput, lStart + 1, lLength) & Range(oMatches(i).Value)
lStart = lStart + lLength + oMatches(i).length
Next
ReplaceAddr2 = ReplaceAddr2 & Mid(sInput, lStart + 1, Len(sInput) - lStart)
End Function
Pattern is 1-3 letters followed by 1-7 digits.
Both functions are not volatile - will be recalculated only when input string changes, but not when cells addressed there change. Adding this line:
Application.Volatile True
will make it recalculate on every change, but it may affect performance.

Split two columns by delimiter and merge together taking a step from each (EXCEL 2016)

Ok so I have two columns of data as follows
Personalisation Max Char | Personaisation Field
1x15x25 | Initial, Name, Date
Previously I was using the following vba function (As excel16 has no TEXTJOIN)
Function TEXTJOIN(delim As String, skipblank As Boolean, arr)
Dim d As Long
Dim c As Long
Dim arr2()
Dim t As Long, y As Long
t = -1
y = -1
If TypeName(arr) = "Range" Then
arr2 = arr.Value
Else
arr2 = arr
End If
On Error Resume Next
t = UBound(arr2, 2)
y = UBound(arr2, 1)
On Error GoTo 0
If t >= 0 And y >= 0 Then
For c = LBound(arr2, 1) To UBound(arr2, 1)
For d = LBound(arr2, 1) To UBound(arr2, 2)
If arr2(c, d) <> "" Or Not skipblank Then
TEXTJOIN = TEXTJOIN & arr2(c, d) & delim
End If
Next d
Next c
Else
For c = LBound(arr2) To UBound(arr2)
If arr2(c) <> "" Or Not skipblank Then
TEXTJOIN = TEXTJOIN & arr2(c) & delim
End If
Next c
End If
TEXTJOIN = Left(TEXTJOIN, Len(TEXTJOIN) - Len(delim))
End Function
This would change 1x15x25 into 1-1, 2-15, 3-25using the following formula
{=TEXTJOIN(", ",TRUE,ROW(INDIRECT("1:" & LEN(A1)-LEN(SUBSTITUTE(A1,"x",""))+1)) & " - " & TRIM(MID(SUBSTITUTE(A1,"x",REPT(" ",999)),(ROW(INDIRECT("1:" & LEN(A1)-LEN(SUBSTITUTE(A1,"x",""))+1)) -1)*999+1,999)))}
Due to the fact, my original method was not specific enough I've been forced to go back to the drawing board.
From the Above, I am wanting to produce the following.
1-2-Initial, 2-15-Name, 3-25-Date
I am a developer but not in visual basic and the worst part Is I know what I would do with a database and PHP just don't have enough knowledge to transfer that to excel.
So I need to either by formula or function
Take 2 Columns and split by a delimiter
Then count the entries on each (Maybe only one)
Then for each in the range create a new string adding the count-col1-col2
I cannot change the data as its given by the supplier
I have a basic understanding of VBA so explain don't belittle
UPDATED (DATA SNAPSHOTS)
This Example uses the formula above a little-jazzed up.
As you can see each row starts the count again Ignore the Personalization/Message line parts I can add these again later
I am in a mega rush so only whipped this up with one row of values (in A1 and B1)
I hope you can step through to understand it, wrap it in another loop to go through your 6000 rows, and change the msgbox to whatever output area you need... 6000 rows should be super quick:
Sub go()
Dim a() As String
Dim b() As String
Dim i As Long
Dim str As String
' split A1 and B1 based on their delimiter, into an array a() and b()
a() = Split(Range("A1").Value2, "x")
b() = Split(Range("B1").Value2, ",")
' quick check to make sure arrays are same size!
If UBound(a) <> UBound(b) Then Exit Sub
' this bit will need amended to fit your needs but I'm using & concatenate to just make a string with the outputs
For i = LBound(a) To UBound(b)
str = str & i + 1 & "-" & a(i) & "-" & b(i) & vbNewLine
Next i
' proof in the pudding
MsgBox str
End Sub
Sub test()
Dim rngDB As Range
Dim vR() As Variant
Dim i As Long
Set rngDB = Range("a2", Range("a" & Rows.Count).End(xlUp)) '<~~personaliation Max Char data range
ReDim vR(1 To rngDB.Count, 1 To 1)
For i = 1 To rngDB.Count
vR(i, 1) = textjoin(rngDB(i), rngDB(i, 2))
Next i
Range("c2").Resize(rngDB.Count) = vR '<~ result wil be recorded in Column C
End Sub
Function textjoin(rng1 As Range, rng2 As Range)
Dim vS1, vS2
Dim vR()
Dim i As Integer
vS1 = Split(rng1, "x")
vS2 = Split(rng2, ",")
ReDim vR(UBound(vS1))
For i = LBound(vS1) To UBound(vS1)
vR(i) = i + 1 & "-" & Trim(vS1(i)) & "-" & Trim(vS2(i))
Next i
textjoin = Join(vR, ",")
End Function
THANK YOU FOR ALL OF THE HELP
I went back to the drawing board having seen the above.
I learnt
That my original use of array formula and TEXTJOIN where over the top and hardly simplistic
That I can use VBA just like any other programming code :)
My Solution simplified from Dy.Lee
Function SPLITANDMERGE(arr1 As String, arr2 As String, Optional del1 As String = "x", Optional del2 As String = ",")
'Arr1 Split'
Dim aS1
'Arr2 Split'
Dim aS2
'Value Array'
Dim r()
'Value Count'
Dim v As Integer
'Split The Values'
aS1 = Split(arr1, del1)
aS2 = Split(arr2, del2)
'Count The Values'
ReDim r(UBound(aS1))
'For All The Values'
For v = LBound(aS1) To UBound(aS2)
'Create The String'
r(v) = "Personalisation_Line " & v + 1 & " - " & Trim(aS1(v)) & " Characters - [" & Trim(aS2(v)) & "]"
Next v
'Join & Return'
SPLITANDMERGE = Join(r, ", ")
End Function
I'm still working on it but I now get the following result.
Will Be Adding:
Value Count Comparison (If we have 4 and 5 Values return "-" to be picked up by conditional formatting)
Conditional plural values (If value 2 in the string is 0 then character instead of characters
If there are any pitfalls or errors anyone can see please do enlighten me. Im here to learn.

Excel 2010 - VBA code to Write Formatted Numbers to CSV

I'm working on a 5 sheet workbook, where a button named ExportCSV on sheet 5 exports data on sheet 3. More specifically, the button runs a VBA code that goes row by row and checks the first 3 cells for data. If any of the first three cells have data, then the whole row is selected. After all rows with data are selected, the data is written row by row to a CSV file (the file itself is semicolon-delimited, however).
The problem that I'm having is that some cell formatting is being copied over, but some is not. For example, values in cells formatted for Accounting with a $ are formatted correctly, meaning "$12,345,678.90" shows up as "$12,345,678.90." However, values in cells formatted as Accounting but without $ are not being written to the csv correctly, meaning "12,345,678.90" is being written as "12345678.9."
Below is the Macro in question.
Dim planSheet As Worksheet
Dim temSheet As Worksheet
Private Sub ExportCSV_Click()
Dim i As Integer
Dim j As Integer
Dim lColumn As Long
Dim intResult As Integer
Dim strPath As String
On Error GoTo Errhandler
Set temSheet = Worksheets(3)
i = 2
Do While i < 1001
j = 1
Do While j < 4
If Not IsEmpty(temSheet.Cells(i, j)) Then
temSheet.Select
lColumn = temSheet.Cells(2, Columns.Count).End(xlToLeft).Column
temSheet.Range(temSheet.Cells(2, 1), temSheet.Cells(i, lColumn)).Select
End If
j = j + 1
Loop
i = i + 1
Loop
Application.FileDialog(msoFileDialogFolderPicker).InitialFileName = Application.ActiveWorkbook.Path
Application.FileDialog(msoFileDialogFolderPicker).Title = "Select a Path"
Application.FileDialog(msoFileDialogFolderPicker).ButtonName = "Select Path"
intResult = Application.FileDialog(msoFileDialogFolderPicker).Show
If intResult <> 0 Then
'dispaly message box
strPath = Application.FileDialog(msoFileDialogFolderPicker).SelectedItems(1)
End If
Dim X As Long, FF As Long, S() As String
ReDim S(1 To Selection.Rows.Count)
For X = 1 To Selection.Rows.Count
S(X) = Join(WorksheetFunction.Transpose(WorksheetFunction.Transpose(Selection.Rows(X).Value)), ";")
Next
FF = FreeFile
FilePath = strPath & "\Data" & Format(Now(), "yyyyMMddhhmmss") & ".csv"
Open FilePath For Output As #FF
Print #FF, Join(S, vbNewLine)
Close #FF
Errhandler:
...Error Handling Code omitted
End Sub
I need to be able to copy over the exact formatting of the cells. Converting the no-$ cells to $ cells won't work because the values without $ are being used for a calculation later on in the process that can handle the commas, but not a $, and I can't change the code for the later calculation (proprietary plug-in doing the calculation.) Also, the rows have mixed content, meaning some values in the row are text instead of numbers.
I ended up following David Zemens' advice and overhauled the section that was For X = 1 to Selection.Rows.Count See below.
For X = 1 To Selection.Rows.Count
For Y = 1 To Selection.Columns.Count
If Y <> Selection.Columns.Count Then
If IsNumeric(temSheet.Cells(X + 1, Y).Value) Then
If temSheet.Cells(X + 1, Y).Value = 0 Then
S(X) = S(X) & ";"
Else
S(X) = S(X) & Replace(temSheet.Cells(X + 1, Y).Text, " ", "") & ";"
End If
Else
S(X) = S(X) & Trim(temSheet.Cells(X + 1, Y).Text) & ";"
End If
Else
If IsNumeric(temSheet.Cells(X + 1, Y).Value) Then
If temSheet.Cells(X + 1, Y).Value <> 0 Then
S(X) = S(X) & Replace(temSheet.Cells(X + 1, Y).Text, " ", "")
End If
Else
S(X) = S(X) & Trim(temSheet.Cells(X + 1, Y).Text)
End If
End If
Next
Next
Some more formatting was necessary. It goes cell by cell, purposefully skipping the first row of the sheet. The .Text property of some of the cells returned empty space before the value or between the $ and value, so it had to be removed. Trim removes leading and ending spaces while Replace replaces all spaces in the export.

How to get range of cells in one cell separated by two blank spaces

I need a vba code to concat range of cell values separated by two spaces into another cell.
For eg.
a1 a
a2 b
a3 c
a4 blank
a5 blank
a6 d
a7 e
a8 f
then value of cell B will be
b1 a,b,c
b2 d,e,f
This code concats single range and also concat space. I want space to be a delimiter and concat the range after space and loop through it.
Sub demo()
'i = 2
'if cells(i,2).value
Dim lastRow As Long
lastRow = ActiveSheet.Range("a" & Rows.Count).End(xlUp).Row
MsgBox (lastRow)
For i = 2 To lastRow
'Do Until Cells(i, 2).End(xlDown).Row = ""
If Cells(i, 3).Value = "" Then
Sheet2.Cells(i, 1).Value = Cells(i, 1) & " " & Cells(i, 2)
Else
Sheet2.Cells(i, 1).Value = Cells(i, 1) & " " & _
Cells(i, 2) & "(" & Cells(i, 3) & ")"
End If
' i = i + 1
Next i
End Sub
You could try this. This solves your first problem. I don't understand if you are presenting another problem in your second part, or trying to explain the first. Anyway, the sub treats a single blank as part of the range to process. Example output in the picture.
Just change the input range ("A1:A30" below). It puts the results in the column adjacent to the input data.
Public Sub spaced_out()
Dim rr, Ain As Range
Dim AB As Object
Dim rr_out_count As Integer
Set Ain = ActiveSheet.Range("A1:A30")
Set AB = CreateObject("scripting.dictionary")
For Each rr In Ain
If (rr.Value <> "") Then
AB.Add rr.Row(), rr.Value
ElseIf (rr.Value = "" And rr.Offset(1, 0).Value = "") Then
rr_out_count = rr_out_count + 1
Ain.Offset(0, 1).Cells(rr_out_count, 1).Value = Join(AB.items(), ",")
AB.RemoveAll
End If
Next
End Sub

Excel Formatting with VBA

Where I work we keep a list of vehicles that we find with damages. These damage codes come in a few variations, and I would like to setup a VBA script in excel to auto change the contents of a cell with the correct formatting, but I don’t really use VBA scripting and the Excel data objects confuse me
Here are a few examples of what I would like
06071 – VBA Function – 06.07.1
031211 – VBA Function- 03.12.1(1)
0409237-VBA Function – 04.09.2(3,7)
040912 030713 –VBA Function – 04.09.1(2) 03.07.1(3) (some vehicles have multiple damages)
Basically any number past length 5 would put any numbers in the 6th position onward into the parentheses, separated by commas.
I could do this in just about any other language, it’s just with all the random Excel stuff I am having issue after issue.
It doesn’t seem to matter what I try, my code bugs out before I can make any progress past
Dim test
test = Worksheets(“Sheet1”).Range(“A:A”).Value
Worksheets(“Sheet2”).Range(“B:B”).Value=test
I tried to make a function which ended up not working no matter how I called it. If I could just basic formatting of these numbers, I could more than likely figure it out from there.
Thanks for any help you guys can give me
You can do this with a UDF (user defined function): Place the following code in a new module in VBA:
Function ConvertIt(rng As Range) As String
Dim varStr As Variant
Dim strSource As String, strResult As String
Dim i As Integer
For Each varStr In Split(Trim(rng.Value), " ")
strSource = CStr(varStr)
strResult = strResult & _
Mid(strSource, 1, 2) & "." & _
Mid(strSource, 3, 2) & "." & _
Mid(strSource, 5, 1)
If Len(strSource) > 5 Then
strResult = strResult & "("
For i = 6 To Len(strSource)
strResult = strResult & Mid(strSource, i, 1) & ","
Next i
strResult = Left(strResult, Len(strResult) - 1) & ")"
End If
strResult = strResult & " "
Next
ConvertIt = Left(strResult, Len(strResult) - 1)
End Function
Assuming that your data is in column A of your worksheet, place this formula in B2: =ConvertIt(A2) and copy it down. Done!
If you want to convert the cells in one rush and replace the source, use this code:
Sub ConvertAll()
Dim rng As Range
For Each rng In Range("A1:A100")
rng.Value = ConvertIt(rng)
Next
End Sub
Lightly-tested:
Function FormatStuff(v)
Dim i As Long, c As String, v2 As String, num As String
Dim num2 As String, x As Long
v2 = v
v = v & " "
For i = 1 To Len(v)
c = Mid(v, i, 1)
If c Like "#" Then
num = num & c
Else
If num <> "" And Len(num) >= 5 Then
num2 = Left(num, 2) & "." & Mid(num, 3, 2) & _
"." & Mid(num, 5,1)
If Len(num) > 5 Then
num2 = num2 & "("
For x = 6 To Len(num)
num2 = num2 & IIf(x > 6, ",", "") & Mid(num, x, 1)
Next x
num2 = num2 & ")"
End If
v2 = Replace(v2, num, num2)
End If
num = ""
End If
Next i
FormatStuff = v2
End Function
To answer your unasked question:
There are two reasons the code you supplied does not work.
Range("A:A") and Range("B:B") both select entire rows, but the
test variable can only hold content for one cell value at a time.
If you restrict your code to just one cell, using
Range("A1").value, for example, the code you have written will
work.
It seems you used different quotation marks than the
standard, which confuses the compiler into thinking "Sheet1", "A:A". etc. are variables.
With the range defined as one cell, and the quotation marks replaced, your code moves the value of cell A1 on Sheet1 to cell B1 on Sheet2:
Sub testThis()
Dim Test
Test = Worksheets("Sheet1").Range("A1").value
Worksheets("Sheet2").Range("B1").value = Test
End Sub
If you wanted to work down the entire column A on Sheet1 and put those values into the column B on Sheet2 you could use a loop, which just repeats an action over a range of values. To do this I've defined two ranges. One to track the cells on Sheet1 column A, the other to track the cells on Sheet2 column B. I've assumed there is no break in your data in column A:
Sub testThat()
Dim CellinColumnA As Range
Set CellinColumnA = Worksheets("Sheet1").Range("A1")
Dim CellinColumnB As Range
Set CellinColumnB = Worksheets("Sheet2").Range("B1")
Do While CellinColumnA.value <> ""
CellinColumnB.value = CellinColumnA.value
Set CellinColumnA = CellinColumnA.Offset(1, 0)
Set CellinColumnB = CellinColumnB.Offset(1, 0)
Loop
End Sub