Excel VBA merging cells in a function - excel

I wrote a crude function to select and concatenate cells based on a range.
Function GetSkills(CellRef As String, CellRefEnd As String, Delimiter As String)
Dim CellStart As Range
Dim CellEnd As Range
Dim LoopVar As Long
Dim StartRow As Long
Dim EndRow As Long
Dim Concat As String
Dim Col As Long
Set CellStart = Worksheets(1).Cells.Range("B" & CellRef)
Set CellEnd = Worksheets(1).Cells.Range("B" & CellRefEnd)
Col = CellStart.Column
StartRow = CellStart.Row
EndRow = CellEnd.Row
With Range(CellStart, CellEnd)
.Merge
.WrapText = True
End With
Concat = ""
For LoopVar = StartRow To EndRow
Concat = Concat & Cells(LoopVar, Col).Value
If LoopVar <> EndRow Then Concat = Concat & Delimiter & " "
Next LoopVar
GetSkills = Concat
End Function
Within it I'm trying to merge the cells, when I run the function I get a prompt saying:
The selection contains multiple data values. Merging into once cell
will keep the upper-left most data only
I click OK and Excel crashes, restarts, and prompts the dialog again. Is there another way to merge a block of cells using VBA?

Generally merging cells is not a good idea. It is a cosmetic formatting approach that can cause havoc with VBA code.
Disclaimers aside, a few suggestions
use a Sub rather than a Function given you want to work with altering the range
use Application.DisplayAlerts to suppress the merge cells message
you can cut down the code significantly
code
Sub Test()
Call GetSkills(2, 4, ",")
End Sub
Sub GetSkills(CellRef As String, CellRefEnd As String, Delimiter As String)
Dim CellStart As Range
Dim CellEnd As Range
Dim Concat As String
Application.DisplayAlerts = False
Set CellStart = Worksheets(1).Cells.Range("B" & CellRef)
Set CellEnd = Worksheets(1).Cells.Range("B" & CellRefEnd)
Concat = Join(Application.Transpose(Range(CellStart, CellEnd)), Delimiter)
With Range(CellStart, CellEnd)
.Merge
.WrapText = True
.Value = Concat
End With
Application.DisplayAlerts = True
End Sub

Related

Shortening words based on database in Excel VBA

I am currently trying to replace words in a cell with shorter versions in mass. I have a dictionary of words to make shorter and will have a column of cells that need to have one or more of the words shortened.
I am very new to VBA and I'm not sure how I would go about this. I tried searching and found some that would be changing text in a word doc but nothing from Excel to excel, at least with my search terms.
I have added a picture here of the Idea, the Text to be shortened is in column A, the words that can be shortened are in column C and the shortened versions are in column D.
Sample
Here's a full sub version if that works better for you
Sub ReplaceViaList()
Dim ws As Worksheet
Dim repRng As Range
Dim x As Long, lastRow As Long
Dim repCol As Long, oldCol As Long, newCol As Long
Dim oldStr As String, newStr As String
'screenupdating/calc
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
'define worksheet
Set ws = ActiveSheet
'define columns to work with
repCol = 1 'col A
oldCol = 3 'col C
newCol = 4 'col D
'find last row of replacement terms
lastRow = ws.Cells(ws.Rows.Count, repCol).End(xlUp).Row
'set range of items to be replaced
Set repRng = ws.Range( _
ws.Cells(2, repCol), _
ws.Cells(lastRow, repCol) _
)
'loop through cells in replacement terms
For x = 2 To ws.Cells(ws.Rows.Count, oldCol).End(xlUp).Row
'define replacement terms
oldStr = ws.Cells(x, oldCol).Value
newStr = ws.Cells(x, newCol).Value
'replace
repRng.Replace What:=oldStr, Replacement:=newStr
Next x
'screenupdating/calc
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
You can use this UDF.
Function SubstituteMultiple(text As String, old_text As Range, new_text As Range)
Dim i As Single
For i = 1 To old_text.Cells.Count
Result = Replace(LCase(text), LCase(old_text.Cells(i)), LCase(new_text.Cells(i)))
text = Result
Next i
SubstituteMultiple = Result
End Function
Place this code in your regular module. then write this formula =SubstituteMultiple(A2,$C$2:$C$11,$D$2:$D$11) in cell B2 and drag it to the bottom.
Perhaps simple replace in VBA would do it,
Sub test()
Dim searchval As Variant
Dim replaceval As Variant
searchval = Range("C1:C10")
replaceval = Range("D1:D10")
For i = 1 To 10
Columns("A:A").Replace What:=searchval(i, 1), Replacement:=replaceval(i, 1), LookAt:=xlPart
Next i
End Sub

Excel VBA Range for all relevant cells

My macro creates a large text file by writing all the data from all sheets in the active workbook.
In each worksheet, it is necessary to determine a certain rectangular range of cells that would be saved in the text file. It's upper left corner would always be A1, but the lower right corner should be chosen so that the range includes all cells with any content (formatting does not matter).
I thought ws.Range("A1").CurrentRegion would do the trick, but it does not work when A1 and the nearby cells are empty. If the only cell with data in the sheet is Q10, then the range should be A1:Q10.
Of course, I could loop over the ws.Cells range to discover the range of interest, but that's quite time consuming, I hope there's more effective way. If I select all cells in a sheet and do a copy-paste to notepad, I do not end up with hundreds of empty columns and thousands of empty rows, only the relevant data are copied. The question is how to replicate that with VBA.
This is my code so far:
Sub CreateTxt()
'This macro copies the contents from all sheets in one text file
'Each sheet contents are prefixed by the sheet name in square brackets
Dim pth As String
Dim fs As Object
Dim rng As Range
pth = ThisWorkbook.Path
Set fs = CreateObject("Scripting.FileSystemObject")
Dim outputFile As Object
Set outputFile = fs.CreateTextFile(pth & "\Output.txt", True)
Dim WS_Count As Integer
Dim ws As Worksheet
Dim I As Integer
WS_Count = ActiveWorkbook.Worksheets.Count
For I = 1 To WS_Count
Set ws = ActiveWorkbook.Worksheets(I)
outputFile.WriteLine ("[" & ws.Name & "]")
Debug.Print ws.Name
Set rng = ws.Range("A1").CurrentRegion
outputFile.WriteLine (GetTextFromRangeText(rng, vbTab, vbCrLf))
Next I
outputFile.Close
End Sub
Function GetTextFromRangeText(ByVal poRange As Range, colSeparator As String, rowSeparator As String) As String
Dim vRange As Variant
Dim sRow As String
Dim sRet As String
Dim I As Integer
Dim j As Integer
If Not poRange Is Nothing Then
vRange = poRange
Debug.Print TypeName(vRange)
For I = LBound(vRange) To UBound(vRange)
sRow = ""
For j = LBound(vRange, 2) To UBound(vRange, 2)
If j > LBound(vRange, 2) Then
sRow = sRow & colSeparator
End If
sRow = sRow & vRange(I, j)
Next j
If sRet <> "" Then
sRet = sRet & rowSeparator
End If
sRet = sRet & sRow
Next I
End If
GetTextFromRangeText = sRet
End Function
if there is anything in A1:B2 cells, this macro works. It breaks when the A1:B2 is empty and the CurrentRegion property returns Empty.
I think you should use these functions to find the last Row/Column
lastRow = Sheets("Sheetname").Cells(Rows.Count, 1).End(xlUp).Row
lastCol = Sheets("Sheetname").Cells(1, Columns.Count).End(xlToLeft).Column
You specify the name of the sheet and the row/columb-number that you want to find the last cell with information, and it return the number of it.
(In the example the last row in first column, and last column in first row are find)
lastCol will give you an Long as an asnwer. If you want to convert this number into the column letter you can use the next function
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
I hope you find this useful
Thanks to user Rosetta, I've come up with this expression for the sought range:
ws.Range("A1:" & ws.Cells.SpecialCells(xlLastCell).Address)

Excel macro to reference cell text

I am putting together a basic macro to format a column to include reference letters. For example, one column has 1,2,3 and there is a cell where the user can input some letters and click a button. ABC for example. This when working shall format 1,2,3 to now be ABC1, ABC2, ABC3 etc.
I have achieved this somewhat but it only works for the letter A. See below:
Sub Macro4()
Range("A3:A60").Select
Selection.NumberFormat = Range("k11").Text & "0" & "0" & "0"
End Sub
Here's my attempt. I'm quite certain there is a better way:
Option Explicit
Sub TestMacro()
Dim MyRange As Range
Dim MyReference As Range
Dim MyArray() As Variant
Dim Counter As Long
Dim wf As WorksheetFunction
Dim Cell As Range
Dim val As Integer
Application.ScreenUpdating = False
Set wf = Application.WorksheetFunction
Set MyRange = Range("A3:A60")
For Each Cell In MyRange
val = Application.Evaluate("=MIN(SEARCH({0,1,2,3,4,5,6,7,8,9}," & Cell.Address & "&" & """0,1,2,3,4,5,6,7,8,9""" & "))")
Cell = CInt(Mid(Cell, val, Len(Cell) - val + 1))
Next Cell
Set MyReference = Range("B3")
MyArray = Application.Transpose(MyRange)
For Counter = LBound(MyArray) To UBound(MyArray)
MyArray(Counter) = MyReference & CStr(MyArray(Counter))
Next Counter
MyRange = Application.Transpose(MyArray)
Application.ScreenUpdating = True
End Sub

Excel Macro: Lose line breaks pasting multiple (non-adjacent) rows into a different workbook

This is odd, because it doesn't always happen as described here.
This Macro allows me to select multiple (non-adjacent) rows in any Workbook or Worksheet, copy them to clipboard and delete the rows.
Sub CopytoClipboardandDelete()
Dim obj As New MSForms.DataObject
Dim X, str As String
Dim count As Integer
count = 0
For Each X In Selection
count = count + 1
If X <> "" Then
If count = 1 Then
str = str & X
Else
str = str & Chr(9) & X
End If
End If
If count = 16384 Then
str = str & Chr(13)
count = 0
End If
Next
obj.SetText str
obj.PutInClipboard
Selection.Delete Shift:=xlUp
End Sub
Now, often, when I get to the Active Workbook or Worksheet to paste the row values the row line breaks are lost and all the data goes into the first single row.
Since this occurs so often, I setup a Macro to easily deal with this.
The problem is that this ONLY works when I happen to paste from the clipboard into a blank Worksheet with all the row data now in Row 1.
If I manually insert 4 rows in the other Worksheet or Workbook at a random point, say into Row 20 to Row 24, since there's 4 rows of data in the clipboard; of course this Macro won't work.
Sub FixAllOnLine1OneRowAtATimeToFirstEmpty()
Application.ScreenUpdating = False
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Set copySheet = ActiveSheet
Set pasteSheet = ActiveSheet
copySheet.Range("Q1:AF1").Copy
pasteSheet.Cells(Rows.count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
Columns("Q:AF").Select
Selection.Delete Shift:=xlToLeft
End Sub
This solution is also close, but again lacks the random flexibility.
Split single row into multiple rows based on cell value in excel
So potentially I'm looking for either solution or both if possible. I am oddly curious why certain times pasting from the clipboard using the Sub CopytoClipboardandDelete the rows preserve their line breaks.
I have a clue to when this occurs, but no idea why. When I use the Sub CopytoClipboardandDelete from the source file that was saved as a text file (.txt or .csv) I rarely lose the row line breaks. But when I use the Sub and paste to a new workbook or worksheet, then use the Sub again from this new dataset and paste it on to another new workbook or worksheet it loses the row line-breaks nearly every time.
UPDATE: When using the Tab delimiter setting, I replace all the preexisting Tabs with 4 spaces.
Copy multiple (non-adjacent) ranges to Clip Board as Comma, Tab Or HTML Delimited Table
Notes:
Areas outside the worksheets UsedRange are cropped from source ange
Each Area in the source range is is broken into rows. Range("C1:D1,F1") will result in 2 rows C1:D1 and F1. 8:8,4:4,6:6 will add 3 rows with the first row being row 8 followed by row 4 and finally row 6.
Sample Data
Option Explicit
Enum ClipTableEnum
eCSV
eHTML
eTab
End Enum
Sub PutRangeIntoClipBoard(rSource As Range, Optional clipEnum As ClipTableEnum = eTab, Optional DebugPrint As Boolean = False)
Dim a, arr
Dim x As Long, rwCount As Long
Dim r As Range, rngRow As Range
Dim s As String
With rSource.Worksheet
Set r = Intersect(rSource, .UsedRange)
If InStr(r.Address(False, False), ",") Then
arr = Split(r.Address(False, False), ",")
Else
ReDim arr(0)
arr(0) = r.Address(False, False)
End If
For Each a In arr
rwCount = .Range(a).Rows.count
For x = 1 To rwCount
Set rngRow = .Range(a).Rows(x)
s = s & get1dRangeToString(rngRow, clipEnum)
Next
Next
End With
If DebugPrint Then Debug.Print vbCrLf & s
PutInClipBoard s
End Sub
Function get1dRangeToString(rSource As Range, Optional clipEnum As ClipTableEnum = eTab) As String
Dim arr
Dim s As String
Dim x As Long
If rSource.Cells.count = 1 Then
ReDim arr(0)
arr(0) = rSource.Value
Else
arr = WorksheetFunction.Transpose(rSource)
arr = WorksheetFunction.Transpose(arr)
End If
Select Case clipEnum
Case ClipTableEnum.eCSV
s = """" & Join(arr, """,""") & """" & vbCrLf
Case ClipTableEnum.eHTML
s = "<TR><TD>" & Join(arr, "</TD><TD>") & "</TD></TR>" & vbCrLf
Case ClipTableEnum.eTab
For x = LBound(arr) To UBound(arr)
arr(x) = Replace(arr(x), vbTab, " ")
Next
s = Join(arr, vbTab)
s = s & vbCrLf
End Select
get1dRangeToString = s
End Function
Sub PutInClipBoard(s As String)
Dim clip As DataObject
Set clip = New DataObject
clip.SetText s
clip.PutInClipBoard
Set clip = Nothing
End Sub
Ok I got it to work, sort-of. Now I can highlight any row that has the multiple rows pasted in; e.g. Highlight Row 10 with Row A10-P10 + Row Q10-AF10 + Row AG10-AV10 etc...and it copies Column Q10-AF10, inserts into Column A11-P11 and deletes Columns("Q:AF").
What I need the Macro to do is loop this process until there's no data outside Column A-P.
Sub FixAllOnLine1OneRowAtATimeInsertToNextRow()
Application.ScreenUpdating = False
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Set copySheet = ActiveSheet
Set pasteSheet = ActiveSheet
copySheet.Range("Q" & ActiveCell.Row & ":AF" & ActiveCell.Row).Copy
Range("Q" & ActiveCell.Row & ":AF" & ActiveCell.Row).Offset(1).Select
pasteSheet.Cells(ActiveCell.Row, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
Columns("Q:AF").Select
Selection.Delete Shift:=xlToLeft
End Sub

Export selected rows and columns to CSV-file

I want to be able to export a selected range of cells to a .csv file using VBA. What I have come up with so far does the job excellently for cohering selections, but fails misearably when multiple columns are selected.
Here is the code I managed to put together from snippets found on the internet: It also fiddles around with some UI and since my Excel speaks German and I need to have "." as decimal separator instead of "," it tweaks that.
Sub Range_Nach_CSV_()
Dim vntFileName As Variant
Dim lngFN As Long
Dim rngRow As Excel.Range
Dim rngCell As Excel.Range
Dim strDelimiter As String
Dim strText As String
Dim strTextCell As String
Dim strTextCelll As String
Dim bolErsteSpalte As Boolean
Dim rngColumn As Excel.Range
Dim wksQuelle As Excel.Worksheet
Dim continue As Boolean
strDelimiter = vbtab
continue = True
Do While continue = True
vntFileName = Application.GetSaveAsFilename("Test.txt", _
FileFilter:="TXT-File (*.TXT),*.txt")
If vntFileName = False Then
Exit Sub
End If
If Len(Dir(vntFileName)) > 0 Then
Dim ans As Integer
ans = MsgBox("Datei existiert bereits. Überschreiben?", vbYesNo)
If ans = vbYes Then
continue = False
ElseIf ans = vbNo Then
continue = True
Else
continue = False
End If
Else
continue = False
End If
Loop
Set wksQuelle = ActiveSheet
lngFN = FreeFile
Open vntFileName For Output As lngFN
For Each rngRow In Selection.Rows
strText = ""
bolErsteSpalte = True
For Each rngCell In rngRow.Columns
strTextCelll = rngCell.Text
strTextCell = Replace(strTextCelll, ",", ".")
If bolErsteSpalte Then
strText = strTextCell
bolErsteSpalte = False
Else
strText = strText & strDelimiter & strTextCell
End If
Next
Print #lngFN, strText
Next
Close lngFN
End Sub
As I already mentioned the sub works well with coherent selections and also with multiple selected lines, but fails when it comes to multiple columns.
The current output of the sub can be seen on this here picture:
multiple columns failed
As one would expect, I want the .csv-file (or respective .txt-file) to look like this:
multiple columns desired output
How can I achieve the desired behaviour for the last case?
And would someone be so kind to include the links as images? If perceived appropriate, of course.
This might seem a little complex, but your use case isn't very simple...
It does assume that each of the selected areas is the same size, and that they all line up (as either rows or columns)
Sub Tester()
Dim s As String, srow As String, sep As String
Dim a1 As Range, rw As Range, c As Range, rCount As Long
Dim areaCount As Long, x As Long
Dim bColumnsSelected As Boolean
Dim sel As Range
bColumnsSelected = False
Set sel = Selection
areaCount = Selection.Areas.Count
Set a1 = Selection.Areas(1)
If areaCount > 1 Then
If a1.Cells(1).Column <> Selection.Areas(2).Cells(1).Column Then
'areas represent different columns (not different rows)
bColumnsSelected = True
Set sel = a1
End If
End If
rCount = 0
For Each rw In sel.Rows
rCount = rCount + 1
srow = ""
sep = ""
For Each c In rw.Cells
srow = srow & sep & Replace(c.Text, ",", ".")
sep = ","
Next c
'if there are multiple areas selected (as columns), then include those
If bColumnsSelected Then
For x = 2 To areaCount
For Each c In Selection.Areas(x).Rows(rCount).Cells
srow = srow & sep & Replace(c.Text, ",", ".")
Next c
Next x
End If
s = s & IIf(Len(s) > 0, vbCrLf, "") & srow
Next rw
Debug.Print s
End Sub

Resources