Excel formula or SQL script that would put bulk text into individual cell - excel

I have a SQL database of illnesses (Records of 9,000) but the illnesses are copy-pasted into a single field and arranged in numbers. From here we always extract to Excel to manipulate the data. My problem now is to put these illnesses into separate cell compartment in excel so that we can filter it properly.
I have only tried the Text-to-Column in Excel but it does not do the job.
Example:
And this is what I'm trying to achieve:
I hope it makes sense.
Thank you.

Comorbidity 2.0
Intro
Wikipedia: In medicine, comorbidity is the presence of one or more additional diseases or disorders co-occurring with (that is, concomitant or concurrent with) a primary disease or disorder; in the countable sense of the term, a comorbidity (plural comorbidities) is each additional disorder or disease.
Since you couldn't get the first script to work I could only conclude that you might have a different line delimiter than the Line Feed at the end of each line in the multi-line cells. So I wrote an improved version of the whole thing and added a function to determine the delimiter for each cell. Now you only have to select a column e.g. A, B, or T (in your sample picture) etc. in the customize section of the code:
'-- CUSTOMIZE BEGIN --------------------
Const cStrColumn As String = "T" '<-- ***COLUMN IN HERE***
Const cStrColumnResult As String = "A" 'Resulting Data Column
Const cLoRow As Long = 0 '0 to use the first row of the initial data range.
'-- CUSTOMIZE END ----------------------
... and the code does the rest itself.
The Code
Option Explicit
'-------------------------------------------------------------------------------
Sub MultilineCellExtractor()
'-------------------------------------------------------------------------------
'Description
'Copies the contents of each cell of a specified COLUMN in a worksheet,
'skipping blank cells and converting multiple lines in cells each to a new
'cell, and returns the result in a COLUMN of a newly created worksheet.
'Arguments as constants
'cStrColumn
'The Column of the Initial Data in ThisWorkbook's ActiveSheet
'cStrColumnResult
'The Column of the Resulting Data in a Newly to be Created Worksheet
'cLoRow
'The First Row of the Resulting Data in the Newly Created Worksheet
'Returns
'A new worksheet with a column of the processed data.
'Usage
'Open the workbook to be processed. Go to VBE and insert a new module. Copy
'this script ('MultilineCellExtractor') and the function 'FirstNonPrintable'
'and paste them into the module. Edit the 'customize section' to fit your
'needs. Exit VBE and start the Run Macro Dialog (Play Button). DoubleClick or
'select 'MultilineCellExtractor' and click Run to execute.
'Remarks
'If there is no data in the column to be processed a message pops up (the only
'error handling done so far). If there are no multiline cells, the data is
'just copied while skipping the blanks.
'There can be no damage done using this script in the previously described way
'because the worksheet is only to be READ from, and the result is always
'pasted into a NEW worksheet.
'-------------------------------------------------------------------------------
'-- CUSTOMIZE BEGIN --------------------
Const cStrColumn As String = "T" 'Initial Data Column
Const cStrColumnResult As String = "A" 'Resulting Data Column
Const cLoRow As Long = 0 '0 to use the first row of the initial data range.
'-- CUSTOMIZE END ----------------------
'-------------------------------------------------------------------------------
Dim oRng As Range 'Initial Colum, Initial Range, Resulting Range
Dim arrRng As Variant 'Array Containing the Initial Data Range (Column)
Dim arrSplit As Variant 'Array Containing the Cell Lines
Dim arrData() As Variant 'Array Containing the Resulting Data Range (Column)
Dim loRow1 As Long 'First Row of the Initial Data Range (Column)
Dim loRow2 As Long 'Last Row of the Initial Data Range (Column)
Dim loRowResult As Long 'First Row of the Resulting Data Range (Column)
Dim loRng As Long 'Initial Array Rows Counter
Dim iSplit As Integer 'Multiline Cell Lines Counter
Dim loData As Long 'Resulting Array(Range) Rows Calculator and Counter
Dim strRng As String 'Initial Data Reader: Shortcut for arrRng(loRng, 1).
Dim str1 As String 'Debug String Writer
Dim lo1 As Long 'Debug String Array Data Counter
'-------------------------------------------------------------------------------
'Column of Initial Data
'Needed to calculate first and last rows of data.
Set oRng = ThisWorkbook.ActiveSheet.Range(cStrColumn & ":" & cStrColumn)
'First Row Containing Data
On Error Resume Next
loRow1 = oRng.Find(What:="*", After:=Cells(Rows.Count, cStrColumn), _
LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext).Row
If Err Then
MsgBox "You have probably selected a column with no data."
GoTo ProcedureExit
End If
'Last Row Containing Data
loRow2 = oRng.Find(What:="*", After:=Cells(1, cStrColumn), _
LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'Calculate Initial Range
Set oRng = ThisWorkbook.ActiveSheet.Range(Cells(loRow1, cStrColumn), _
Cells(loRow2, cStrColumn))
' str1 = "Calculate Initial Range (Results):"
' str1 = str1 & vbCrLf & Space(2) & "loRow1 = " & loRow1
' str1 = str1 & vbCrLf & Space(2) & "loRow2 = " & loRow2
' str1 = str1 & vbCrLf & Space(2) & "oRng.Address: " & oRng.Address
' Debug.Print str1 & vbCrLf
'Paste range into array
arrRng = oRng
Set oRng = Nothing 'Release the variable, initial data is in arrRng.
' str1 = "arrRng Contents:"
' For lo1 = LBound(arrRng) To UBound(arrRng)
' str1 = str1 & vbCrLf & Space(2) & lo1 & ". " & arrRng(lo1, 1)
' Next
' Debug.Print str1 & vbCrLf
'-------------------------------------------------------------------------------
'Now arrays are taking over
'Count data in arrRng to calculate size of arrData.
For loRng = LBound(arrRng) To UBound(arrRng)
strRng = arrRng(loRng, 1)
If strRng <> "" Then 'Not empty cell, continue.
If FirstNonPrintable(strRng) > 0 Then 'Non printable character found.
'Splitting arrSplit by 'FirstNonPrintable'
arrSplit = Split(strRng, Chr(FirstNonPrintable(strRng)))
loData = loData + UBound(arrSplit) + 1 '+ 1 i.e. arrSplit is 0-based.
Else 'Nonprintable character not found.
loData = loData + 1
End If
' Else 'Empty cell, do nothing.
End If
Next
'Redeclare arrData using the result of the counting (loData).
ReDim Preserve arrData(1 To loData, 1 To 1)
'Reset counter for counting.
loData = 0
'Read data from arrRng and write to array.
For loRng = LBound(arrRng) To UBound(arrRng)
strRng = arrRng(loRng, 1)
If strRng <> "" Then 'Not empty cell, continue.
If FirstNonPrintable(strRng) > 0 Then 'Non printable character found.
'Splitting arrSplit by 'FirstNonPrintable'
arrSplit = Split(strRng, Chr(FirstNonPrintable(strRng)))
'
' str1 = "arrSplit Contents:"
' For lo1 = LBound(arrSplit) To UBound(arrSplit)
' str1 = str1 & vbCrLf & Space(2) & lo1 + 1 & ". " & arrSplit(lo1)
' Next
' Debug.Print str1 & vbCrLf
'
'Writing arrSplit data to arrData.
For iSplit = LBound(arrSplit) To UBound(arrSplit)
loData = loData + 1
arrData(loData, 1) = arrSplit(iSplit)
Next
Erase arrSplit 'Is repeatedly newly created to write data to arrData.
Else 'Nonprintable character not found.
loData = loData + 1
arrData(loData, 1) = strRng
End If
' Else 'Empty cell, do nothing.
End If
Next
Erase arrRng 'No longer needed, resulting data is in arrData.
'
' str1 = "arrData Contents:"
' For lo1 = LBound(arrData) To UBound(arrData)
' str1 = str1 & vbCrLf & Space(2) & lo1 & ". " & arrData(lo1, 1)
' Next
' Debug.Print str1
'
'-------------------------------------------------------------------------------
'Return data in new worksheet
'Calculate the first row of data in the resulting worksheet.
If cLoRow > 0 Then
loRowResult = cLoRow 'Row as the constant in the 'customize section'.
Else
loRowResult = loRow1 'Same row as in the initial worksheet.
End If
'Add a new (resulting) worksheet positioned after the initial worksheet.
ThisWorkbook.Worksheets.Add _
After:=ActiveSheet 'The resulting worksheet is active now.
'Calculate the resulting range in the new worksheet.
Set oRng = ActiveSheet.Range(Cells(loRowResult, cStrColumnResult), _
Cells(loRowResult + loData - 1, cStrColumnResult))
'Paste data into the resulting range.
oRng = arrData
Erase arrData 'No longer needed, all data is in oRng.
'-------------------------------------------------------------------------------
ProcedureExit:
Set oRng = Nothing 'Release the variable, all data is in the worksheet.
End Sub
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
Function FirstNonPrintable(StringToClean As String, _
Optional Code0Position1String2 As Integer = 0) As Variant
'-------------------------------------------------------------------------------
'Description
'Finds the first character in a string that is different from the character
'at the same position in the cleaned version of the same string and returns
'its code, position or string.
'Arguments
'StringToClean (String)
'The string to clean.
'Code0Position1String2 (Integer)
'Returns for
'0, the character code (Asc) of the found character to be used with
'the Chr function.
'1, the position of the found character.
'2, the found character.
Dim strCleaned As String
Dim loLen As Long
strCleaned = WorksheetFunction.Clean(StringToClean)
If StringToClean = strCleaned Then Exit Function
For loLen = 1 To Len(StringToClean)
If Mid(StringToClean, loLen, 1) <> Mid(strCleaned, loLen, 1) Then
Select Case Code0Position1String2
Case 0
FirstNonPrintable = Asc(Mid(StringToClean, loLen, 1))
Case 1
FirstNonPrintable = loLen
Case 2
FirstNonPrintable = Mid(StringToClean, loLen, 1)
End Select
Exit Function
End If
Next
End Function
'-------------------------------------------------------------------------------
Some additional info
To put more lines into a cell you have to hold the left ALT key and press enter after each line.
For character codes look here.

This is possible in Get&Transform if you're on a recent version of Excel.
Get Data from range
Right Click the Column>Split Column>By Delimiter
In advanced options you can split by row and get the "new line" character i.e. cr/lf

Comorbidity
Intro
Wikipedia: In medicine, comorbidity is the presence of one or more additional diseases or disorders co-occurring with (that is, concomitant or concurrent with) a primary disease or disorder; in the countable sense of the term, a comorbidity (plural comorbidities) is each additional disorder or disease.
The Code
Beware: There is no error handling, so if something goes wrong just don't save anything. Close without saving and try again.
The code was tested and works fine with Excel 2003 and should work fine with all newer versions, too.
The code blocks starting with ' str1 = are just for debugging, a kind of 'subtotals' and can be deleted if you wish.
Sub Comorbidities()
'Description
'Writes the contents of cell values with several rows (per cell) to a new
'worksheet each row in a seperate row. For this to work the worksheet with
'the 'several row cells values' has to be active (selected).
'-- CUSTOMIZE BEGIN --------------------
Const cStrHeader As String = "Comorbidities" 'Header
Const cLoRow As Long = 2 'Starting row of initial data
Const cStrColumn As String = "T" 'Column of initial data
Const cLoRowResult As Long = 2 'Starting row of resulting data
Const cStrColumnResult As String = "A" 'Column of resulting data
'-- CUSTOMIZE END ----------------------
Dim oRng As Range 'Initial Range
Dim oRngResult As Range 'Resulting Range
Dim arrRng As Variant 'Array containing the initial data pasted from the range
Dim arrSplit As Variant 'Array containing the rows inside a cell
Dim arrData() As Variant 'Array containing the resulting data
Dim loData As Long 'Count of all rows
Dim loRng As Long 'Counter
Dim loArr As Long 'Counter
Dim iSplit As Integer 'Counter
Dim str1 As String 'Debug String
Dim lo1 As Long 'Debug Counter
'Determine the range to be processed
Set oRng = Range(Cells(cLoRow, cStrColumn), _
Cells(Cells(Rows.Count, cStrColumn).End(xlUp).Row, cStrColumn))
'Paste range into array
arrRng = oRng
Set oRng = Nothing 'Release the variable, initial data is in arrRng.
'Now arrays are taking over
' str1 = "arrRng"
' For lo1 = LBound(arrRng) To UBound(arrRng)
' str1 = str1 & vbCrLf & lo1 & ". " & arrRng(lo1, 1)
' Next
' Debug.Print str1
'Counting data - split each cells value and add to sum (loData)
'Reading data from arrRng.
For loRng = LBound(arrRng) To UBound(arrRng)
'Splitting arrSplit by Chr(10)
arrSplit = Split(arrRng(loRng, 1), Chr(10))
loData = loData + UBound(arrSplit) + 1 '+ 1 i.e. arrSplit is 0-based.
Next
'Redeclare arrData using the result of the counting (loData).
ReDim Preserve arrData(1 To loData, 1 To 1)
'Reading data from arrRng.
For loRng = LBound(arrRng) To UBound(arrRng)
'Splitting arrSplit by Chr(10).
arrSplit = Split(arrRng(loRng, 1), Chr(10))
' str1 = "arrSplit"
' For lo1 = LBound(arrSplit) To UBound(arrSplit)
' str1 = str1 & vbCrLf & lo1 + 1 & ". " & arrSplit(lo1)
' Next
' Debug.Print str1
'Writing arrSplit data to arrData.
For iSplit = LBound(arrSplit) To UBound(arrSplit)
loArr = loArr + 1
arrData(loArr, 1) = arrSplit(iSplit)
Next
Erase arrSplit 'Is repeatedly newly created to write data to arrData.
' str1 = "arrData"
' For lo1 = LBound(arrData) To UBound(arrData)
' str1 = str1 & vbCrLf & lo1 & ". " & arrData(lo1, 1)
' Next
' Debug.Print str1
Next
Erase arrRng 'No longer needed, resulting data is in arrData.
' str1 = "arrData"
' For lo1 = LBound(arrData) To UBound(arrData)
' str1 = str1 & vbCrLf & arrData(lo1, 1)
' Next
' Debug.Print str1
'Output to new worksheet
'Add a new worksheet positioned after the initial worksheet.
Worksheets.Add After:=ActiveSheet
'Determine the resulting range in the new worksheet.
Set oRngResult = Range(Cells(1, 1), Cells(loData, 1))
'Paste data into range
oRngResult = arrData
Erase arrData 'No longer needed, all data is in oRngResult.
Set oRngResult = Nothing 'Release the variable, all data is in the worksheet.
'Write Header
Cells(cLoRowResult - 1, cStrColumnResult).Value = cStrHeader
End Sub
Some additional info
How to put more lines into one cell
You have to hold the left ALT key and press enter after each line.
How I got the delimiter
When you go into a cell (click in the formula bar) e.g. cell 'A1' with more lines (bulk data), you select the end of a line expanding over the 'invisible' kind of 'space looking' character right after the 'visible' part and copy it. Then paste it into another cell e.g. A2. Now in e.g. cell A3 write the formula =CODE(A2)and the result will be 10. So in VBA this means you choose this character by using the Chr Function: Chr(10) (in Excel this is =CHAR(10).
BTW the character is called Line Feed (LF) or New Line (NL). For other character codes look here.

Related

VBA Function to Return LOOKUP Values Based on Column Range

I am relatively new to VBA and would need help from the community on the below logic.
I have the following table
My Actual Data Table is as follows
My Expected Output is as follows:
I tried using index value to the cat codes and tried but I am stuck for logic here and not able to proceed. Thanks for your help.
Note: The Actual data need not contain the Catcode, for example value belonging to CatCode A will not always contain A in the value. I would to categorize all the values between two catcodes to the cat code that follows it.
Lookup Based on Column Range
Adjust the values in the constants section (e.g. The sheet names can be all the same, the first rows or columns can be different etc.).
New Version
Option Explicit
Sub LookupBasedOnColumnRange()
Const Head1 As String = "CatCode" ' 1st Column Header
Const Head2 As String = "Values" ' 2nd Column Header
Const cSheet As String = "Sheet1" ' CatCode Sheet Name
Const cFR As Long = 2 ' CatCode First Row Number (no header)
Const cCol As Variant = 1 ' CatCode Column (e.g. 1 or "A")
Const aSheet As String = "Sheet2" ' Actual Sheet Name
Const aFR As Long = 2 ' Actual First Row Number (no header)
Const aCol As Variant = 1 ' Actual Column (e.g. 1 or "A")
Const rSheet As String = "Sheet3" ' Result Sheet Name
Const rCel As String = "A1" ' Result First Cell Range Address
Dim rng As Range ' CatCode Non-Empty 1-Column Range,
' Actual Non-Empty 1-Column Range,
' Result 2-Column Range
Dim CatCode As Variant ' CatCode Array
Dim Actual As Variant ' Actual Array
Dim Result As Variant ' Result Array
Dim i As Long ' CatCode Array Elements Counter
Dim j As Long ' Actual Array Elements Counter,
' Result Array 1st Dimension (Rows) Elements Counter
' Change to "As Long" if only numbers
' or to "As Variant" if there are numbers and strings.
Dim CurC As String ' Current CatCode
Dim CurA As String ' Current Actual
' Write ranges to arrays.
With ThisWorkbook.Worksheets(cSheet)
Set rng = .Columns(cCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
CatCode = .Range(.Cells(cFR, cCol), rng)
End With
With ThisWorkbook.Worksheets(aSheet)
Set rng = .Columns(aCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
Actual = .Range(.Cells(aFR, aCol), rng)
End With
Set rng = Nothing
' Resize Result Array (Same first dimension (rows) as Actual Array).
ReDim Result(1 To UBound(Actual) + 1, 1 To 2) ' '+1' for headers
' Write headers to Result Array.
Result(1, 1) = Head1
Result(1, 2) = Head2
' Calculate and write data to Result Array.
j = 1
On Error GoTo ErrorHandler
For i = 1 To UBound(CatCode)
CurC = CatCode(i, 1)
Do
' If CatCode is missing, Run-time error '9'.
CurA = Actual(j, 1)
Result(j + 1, 1) = CurC
Result(j + 1, 2) = CurA
j = j + 1
Loop Until CurA = CurC Or j = UBound(Result) + 1
' "j = UBound(Result) + 1" prevents infinite loop
' if CatCode missing.
Next i
On Error GoTo 0
' Erase arrays not needed anymore.
Erase CatCode
Erase Actual
With ThisWorkbook.Worksheets(rSheet)
' Clear contents of columns of Result Range.
.Range(rCel).Resize(.Rows.Count - Range(rCel).Row + 1, 2).ClearContents
' Define Result Range.
Set rng = .Range(rCel).Resize(UBound(Result), UBound(Result, 2))
End With
' Copy Result Array to Result Range.
rng = Result
' Inform user.
MsgBox "Transferred Result(" & UBound(Result) & "x" & UBound(Result, 2) _
& ").", vbInformation, "Custom Message"
GoTo exitProcedure
ErrorHandler:
If Err.Number = 9 Then
MsgBox "CatCode '" & CurC & "' missing.", vbCritical, "Custom Message"
Err.Clear: GoTo exitProcedure
End If
If Err.Number > 0 Then
MsgBox "An unexpected error occurred. Error '" _
& Err.Number & "': " & Err.Description, vbCritical, "Custom Message"
Err.Clear: GoTo exitProcedure
End If
exitProcedure:
End Sub
Old Version Improved
Option Explicit
Sub LookupBasedOnColumnRangeFirst()
Const Head1 As String = "CatCode" ' 1st Column Header
Const Head2 As String = "Values" ' 2nd Column Header
Const cSheet As String = "Sheet1" ' CatCode Sheet Name
Const cFR As Long = 2 ' CatCode First Row Number (no header)
Const cCol As Variant = 1 ' CatCode Column (e.g. 1 or "A")
Const aSheet As String = "Sheet2" ' Actual Sheet Name
Const aFR As Long = 2 ' Actual First Row Number (no header)
Const aCol As Variant = 1 ' Actual Column (e.g. 1 or "A")
Const rSheet As String = "Sheet3" ' Result Sheet Name
Const rCel As String = "A1" ' Result First Cell Range Address
Dim rng As Range ' CatCode Non-Empty 1-Column Range,
' Actual Non-Empty 1-Column Range,
' Result 2-Column Range
Dim CatCode As Variant ' CatCode Array
Dim Actual As Variant ' Actual Array
Dim Result As Variant ' Result Array
Dim i As Long ' CatCode Array Elements Counter
Dim j As Long ' Actual Array Elements Counter
Dim k As Long ' Result Array 1st Dimension (Rows) Elements Counter
' Write ranges to arrays.
With ThisWorkbook.Worksheets(cSheet)
Set rng = .Columns(cCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
CatCode = .Range(.Cells(cFR, cCol), rng)
End With
With ThisWorkbook.Worksheets(aSheet)
Set rng = .Columns(aCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
Actual = .Range(.Cells(aFR, aCol), rng)
End With
Set rng = Nothing
' The following line assumes that all 'data is valid'. If not then
' Result Array will have empty elements at the end (probably no harm done,
' but definately 'not correct'.
' Resize Result Array (Same first dimension (rows) as Actual Array).
ReDim Result(1 To UBound(Actual) + 1, 1 To 2) ' '+1' for headers
' Write headers to Result Array.
Result(1, 1) = Head1
Result(1, 2) = Head2
' Calculate and write data to Result Array.
k = 2
For i = 1 To UBound(CatCode)
For j = 1 To UBound(Actual)
If Actual(j, 1) Like CatCode(i, 1) & "*" Then
Result(k, 1) = CatCode(i, 1)
Result(k, 2) = Actual(j, 1)
k = k + 1
End If
Next j
Next i
' Note: The previous For Next Loop always loops through all elements
' of Actual Array allowing it to be unsorted.
' Erase arrays not needed anymore.
Erase CatCode
Erase Actual
With ThisWorkbook.Worksheets(rSheet)
' Clear contents of columns of Result Range.
.Range(rCel).Resize(.Rows.Count - Range(rCel).Row + 1, 2).ClearContents
' Define Result Range.
Set rng = .Range(rCel).Resize(UBound(Result), UBound(Result, 2))
End With
' Copy Result Array to Result Range.
rng = Result
' Inform user.
MsgBox "Transferred Result(" & UBound(Result) & "x" & UBound(Result, 2) _
& ").", vbInformation, "Custom Message"
End Sub

Concatenate two columns and skip blank cells

My current spreadsheet has two columns of data I would like to concatenate. In my provided code, I create a column to the right of the columns I would like to combine and then use a FOR loop to combine each value with a ", " between the values. I would like to adjust the code to skip cells/rows without values because now I end up with a ", " in my combined column if the two initial columns had no values.
Public Sub MergeLatLong()
Dim LastRow As Long
Worksheets("Raw_Data").Activate
Columns("AT:AT").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
LastRow = Range("AR" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
Cells(i, 46) = Cells(i, 44) & ", " & Cells(i, 45)
Next i
End Sub
Do you need to use VBA? I would recommend using a TEXTJOIN formula (if you have Excel 2016). Assuming your cells in columns AR and AS and the formula in AT.
The parameters for the formula are =TEXTJOIN(delimiter,ingnore_blanks,range)
So the below formula in AT1 would return a concatenation of the two columns for each row with a comma as the delimiter if there is contents in both columns.
=TEXTJOIN(“,”,TRUE,AR1:AS1)
If you are using a version less than 2016. You could just use the following
=AR1&IF(ISBLANK(AS1),””,”, AS1”)
Either of these can be dragged down and you wouldn’t have any extra commas in any rows with a blank in column AS.
The code below should do what you intend. It will enter a blank if both values are missing, the first only (without comma) if the second is missing, and the second only (with leading comma) if the first is missing. You might adjust that part to better suit your needs.
Public Sub MergeLatLong()
Dim Ws As Worksheet
Dim LastRow As Long
Dim Combo As String, Tmp As String
Dim R As Long
' No need to Activate or Select anything!
Set Ws = Worksheets("Raw_Data")
With Ws
.Columns(46).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
LastRow = .Cells(Rows.Count, "AR").End(xlUp).Row
For R = 2 To LastRow
' if you mean the 'Value' it's better to specify the 'Value' property
Combo = Trim(.Cells(R, 44).Value) ' treat Space as blank
Tmp = Trim(.Cells(R, 45).Value) ' treat Space as blank
If Len(Tmp) Then Tmp = ", " & Tmp
If Len(Combo) And Len(Tmp) > 0 Then Combo = Combo & Tmp
Cells(R, 46).Value = Combo
Next R
End With
End Sub
As did #Dude Scott, I also felt that a worksheet function might be more suitable. VBA might have some advantage if it's a very frequently recurring task only.
If the number of entries is large, add Application.ScreenUpdating = False before the For .. Next loop and reset ScreenUpdating to True at the end of the procedure. That will make for significantly better speed.
you could loop through column AR not blank cells only and check for column AS ones content to properly add comma
moreover, avoid Activate/Select pattern and use direct and explicit reference to ranges:
Public Sub MergeLatLong()
Dim cell As Range
With Worksheets("Raw_Data") ' reference wanted worksheet
.Columns("AT:AT").Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
For Each cell In .Range("AR2", .Cells(.Rows.Count, "AR").End(xlUp)).SpecialCells(xlCellTypeConstants) ' loop through referenced sheet column AR cells with some "constant" values
If IsEmpty(cell.Offset(, 1)) Then
cell.Offset(, 2) = cell.Value
Else
cell.Offset(, 2) = cell.Value & ", " & cell.Offset(, 1)
End If
Next
End With
End Sub
2 Columns 2 One
Fast Array Version
Sub MergeLatLong() ' Array Version
Dim vnt1 As Variant ' 1st Array
Dim vnt2 As Variant ' 2nd Array
Dim vntR As Variant ' Result Array
Dim NoR As Long ' Number of Rows
Dim i As Long ' Row Counter
Dim str1 As String ' 1st String
Dim str2 As String ' 2nd String
Dim strR As String ' Result String
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Handle possible error.
On Error GoTo ErrorHandler
With ThisWorkbook.Worksheets("Raw_Data")
' Insert column ("AT") to the right of column ("AS").
.Columns("AT").Insert xlToRight, xlFormatFromLeftOrAbove
' Calculate Number of Rows (Last Used Row - First Row + 1).
NoR = .Cells(.Rows.Count, "AR").End(xlUp).Row - 2 + 1
' Copy values of column "AR" to 1st Array.
vnt1 = .Columns("AR").Cells(2).Resize(NoR)
' Copy values of column "AS" to 2nd Array.
vnt2 = .Columns("AS").Cells(2).Resize(NoR)
End With
' Resize Result Array to size of 1st Array (or 2nd Array).
ReDim vntR(1 To UBound(vnt1), 1 To 1) As String
' Remarks: All arrays are of the same size.
' Loop through rows of arrays.
For i = 1 To NoR
' Write current value in 1st array to 1st String.
str1 = vnt1(i, 1)
' Write current value in 2nd array to 2nd String.
str2 = vnt2(i, 1)
' Check if 1st String is not empty ("").
If str1 <> "" Then ' 1st String is not empty.
' Check if 2nd String is not empty ("").
If str2 <> "" Then ' 2nd String is not empty.
' Concatenate.
strR = str1 & ", " & str2
Else ' 2nd String is empty.
strR = str1
End If
Else ' 1st String is empty.
If str2 <> "" Then ' 2nd String is not empty.
strR = str2
Else ' 2nd String is empty.
strR = ""
End If
End If
' Write Result String to current row of Result Array.
vntR(i, 1) = strR
Next
With ThisWorkbook.Worksheets("Raw_Data").Columns("AT")
' Copy Result Array to Result Range.
.Cells(2).Resize(NoR) = vntR
' Adjust the width of Result Column.
.AutoFit
' ' Apply some additional formatting to Result Range.
' With .Cells(2).Resize(NoR)
' ' e.g.
' .Font.Bold = True
' End With
End With
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ErrorHandler:
MsgBox "An unexpected error has occurred. Error '" & Err.Number & "': " _
& Err.Description, vbInformation, "Error"
GoTo ProcedureExit
End Sub
Slow Range Version
Sub MergeLatLongRange() ' Range Version
Dim LastRow As Long ' Last Row Number
Dim i As Long ' Row Counter
Dim str1 As String ' 1st String
Dim str2 As String ' 2nd String
Dim strR As String ' Result String
' Speed up.
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Handle possible error.
On Error GoTo ErrorHandler
With ThisWorkbook.Worksheets("Raw_Data")
' Insert column ("AT") to the right of column ("AS").
.Columns("AT").Insert xlToRight, xlFormatFromLeftOrAbove
' Calculate Last Used Row using 1st column "AR".
LastRow = .Cells(.Rows.Count, "AR").End(xlUp).Row
' Loop through rows in columns.
For i = 2 To LastRow
' Write value of cell at current row in column "AR" to 1st String.
str1 = .Cells(i, "AR")
' Write value of cell at current row in column "AS" to 2nd String.
str2 = .Cells(i, "AS")
' Check if 1st String is not empty ("").
If str1 <> "" Then ' 1st String is not empty.
' Check if 2nd String is not empty ("").
If str2 <> "" Then ' 2nd String is not empty.
' Concatenate.
strR = str1 & ", " & str2
Else ' 2nd String is empty.
strR = str1
End If
Else ' 1st String is empty.
If str2 <> "" Then ' 2nd String is not empty.
strR = str2
Else ' 2nd String is empty.
strR = ""
End If
End If
' Write Result String to cell at current row in column "AT".
Cells(i, "AT") = strR
Next
' Adjust the width of column "AT".
.Columns("AT").AutoFit
End With
ProcedureExit:
' Speed down.
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ErrorHandler:
MsgBox "An unexpected error has occurred. Error '" & Err.Number & "': " _
& Err.Description, vbInformation, "Error"
GoTo ProcedureExit
End Sub
Here is the code I ended up using, a blend of the responses above. I create some additional code to find the columns with latitude and longitude, that way if the columns were to somehow be rearranged, the program would still be looking at the correct columns for values.
Sub concatenateLatLong()
Dim WS As Worksheet
Dim lastRow As Long
Dim longName As String
Dim longColumn As Long
Dim latName As String
Dim latColumn As Long
Dim latValue As String
Dim longValue As String
Dim i As Long
Set WS = Worksheets("Data")
With WS
lastRow = .Cells.Find(What:="*", After:=.Range("A1"), LookAt:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, MatchCase:=False).Row
'MsgBox "The last row with entered data is " & lastRow
'Find Longitude column
longName = "LONGITUDE"
longColumn = .Rows(1).Find(What:=longName, LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False).Column
'MsgBox "The " & longName & " header is found in column " & longColumn
'Insert a row to the right of the longitude column
.Columns(longColumn + 1).Insert Shift:=xlToLeft, CopyOrigin:=xlFormatFromLeft
'Give new column header "LAT, LONG"
.Cells(1, longColumn + 1).Value = "LAT, LONG"
'Find Latitude column
latName = "LATITUDE"
latColumn = .Rows(1).Find(What:=latName, LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False).Column
'MsgBox "The " & latName & " header is found in column " & latColumn
'Combine latitude and longitude
For i = 2 To lastRow
latValue = Trim(.Cells(i, latColumn).Value)
longValue = Trim(.Cells(i, longColumn).Value)
If Len(longValue) Then longValue = ", " & longValue
If Len(latValue) And Len(longValue) > 0 Then latValue = latValue & longValue
.Cells(i, longColumn + 1).Value = latValue
Next i
End With
End Sub

Find range which contains first cell only of last line?

I need to calculate a range which contains just a single cell. This cell is from the last line and first column. If the sheet is empty the range is A1:A1.
I know there are plenty of ways to calculate the last line, but I'm looking for an elegant way to get the first cell of the last line. Maybe some examples explain better.
Example #1
A B C D
1
2 X
3 X
4 X
Result #1
Range = A4:A4
Example #2
A B C D
1
Result #2
Range = A1:A1
How to do this?
If I have understood correctly, you want find the last row across some range (or bunch of columns).
One way to achieve this might be to loop over each column within the range, find what row the last cell (in that particular column), and check if it exceeds whatever the greatest last row has been thus far in the loop.
In the code below, if you change "Sheet1" to whatever your sheet is called, and change the range from "A4:Z5" to something like "A:Z" or "A1:D4" (or whatever it is in your case), it should then display the address of the cell you're after.
Option Explicit
Private Sub ShowLastCell()
' Change this to what your sheet is called.
With ThisWorkbook.Worksheets("Sheet1")
' Change this to the range you need to check.
With .Range("A4:Z5")
Dim firstColumnToCheck As Long
firstColumnToCheck = .Columns(1).Column
Dim lastColumnToCheck As Long
lastColumnToCheck = .Columns(.Columns.Count).Column
End With
Dim maxLastRow As Long
Dim columnIndex As Long
For columnIndex = firstColumnToCheck To lastColumnToCheck
maxLastRow = Application.Max(maxLastRow, .Cells(.Rows.Count, columnIndex).End(xlUp).Row)
Next columnIndex
MsgBox ("I think the cell you want is " & .Cells(maxLastRow, "A").Address & ":" & .Cells(maxLastRow, "A").Address)
End With
End Sub
GetFirstCellInLastLine will return the first cell in the last line of the referenced worksheet as a Range object. Then you can do what you want with it. For example, printing to Immediate Window for the active sheet:
Debug.Print GetFirstCellInLastLine(ActiveSheet).Address
It is setup to return Noting if the worksheet is blank, but you can modify this according to your needs:
'''
''' Returns the first used cell in the last line of the worksheet.
''' Returns "Nothing" if the worksheet is blank.
'''
Public Function GetFirstCellInLastLine(ws As Excel.Worksheet) As Excel.Range
Dim rng As Excel.Range
Set rng = ws.UsedRange.Cells(ws.UsedRange.Rows.Count, 1)
If ((ws.UsedRange.Columns.Count > 1) And ws.Range(rng, rng.End(xlToRight)).Columns.Count <= ws.UsedRange.Columns.Count) Then
Set rng = ws.Range(rng, rng.End(xlToRight))
If VBA.IsEmpty(rng.Cells(1, 1)) Then
Set rng = rng.Cells(1, rng.Columns.Count)
Else
Set rng = rng.Cells(1, 1)
End If
ElseIf (ws.UsedRange.Columns.Count = 1) And VBA.IsEmpty(rng.Cells(1, 1)) Then
Set rng = Nothing
End If
Set GetFirstCellInLastLine = rng
End Function
Last Used Row & Specified Column Intersection feat. UsedRange
One of the elegant ways would be to use the UsedRange property.
Advanced Version
'*******************************************************************************
' Purpose: Using the UsedRange Property, creates a reference to the cell *
' range at the intersection of the last used row and a specified *
' column in a worksheet and prints its address and the address *
' of the UsedRange to the Immediate Window. *
'*******************************************************************************
Sub LastUR_Column_UsedRange()
Const cVntCol As Variant = "A" ' Column
Dim objRngT As Range ' Target Range
With ThisWorkbook.Worksheets("Sheet1")
If .Cells(.UsedRange.Rows.Count + .UsedRange.Row - 1, cVntCol).Row = 1 _
And .Cells(1, Columns.Count).End(xlToLeft).Column = 1 _
And IsEmpty(.Cells(1, 1)) Then
Debug.Print "objRngT = Nothing (Empty Worksheet)"
Else
Set objRngT = .Cells(.UsedRange.Rows.Count + .UsedRange.Row - 1, cVntCol)
Debug.Print "objRngT = " & objRngT.Address & " calculated from the " _
& "used range (" & .UsedRange.Address & ")."
Set objRngT = Nothing
End If
End With
End Sub
'*******************************************************************************
Lesson Version
'*******************************************************************************
' Purpose: Using the UsedRange Property, creates a reference to the cell *
' range at the intersection of the last used row and a specified *
' column in a worksheet and prints subresults and its address *
' to the Immediate Window. *
'*******************************************************************************
Sub LastUR_Column_UsedRange_Lesson()
' When you declare the column as variant you can use
' column letter or number e.g. "A" or 1, "D" or 4 ...
Const cVntCol As Variant = "A" ' Column
Dim objRngT As Range ' Target Range
Dim lngLast As Long ' Last Row
Dim lngRows As Long ' Number of Rows
Dim lngFirst As Long ' First Row
With ThisWorkbook.Worksheets("Sheet1")
' Finding first row and number of rows is easy.
lngFirst = .UsedRange.Row
Debug.Print "lngFirst = " & lngFirst
lngRows = .UsedRange.Rows.Count
Debug.Print "lngRows = " & lngRows
' Note1: Only when there is data in the first row, the number of rows
' is equal to the last row.
' Therefore we have to calculate the last row.
lngLast = lngRows + lngFirst - 1
Debug.Print "lngLast = " & lngLast
' Now imagine you have the first data in row 2, and you have 3 rows
' which would mean the last data is in row 4 (rows 2, 3, 4). So when you add
' 2 + 3 = 5, you have to subtract 1 row, because you counted row 2 twice.
' Note2: If there is data in the first row then lngFirst = 1.
' So the formula will calculate:
' lnglast = lngRows + 1 - 1
' lngLast = lngRows + 0
' which proves the statement in Note1.
' The previous three lines could have been written in one line:
lngLast = .UsedRange.Rows.Count + .UsedRange.Row - 1
Debug.Print "lngLast = " & lngLast & " (One Row Version)"
' Now we have all the ingredients for the Target Range.
Set objRngT = .Cells(lngLast, cVntCol)
Debug.Print "objRngT = " & objRngT.Address _
& " (Before Check if Empty)"
' And again all this could have been written in one line:
Set objRngT = .Cells(.UsedRange.Rows.Count + .UsedRange.Row - 1, cVntCol)
Debug.Print "objRngT = " & objRngT.Address & " (One Row Version)" _
& " (Before Check if Empty)"
' then you wouldn't need variables lngLast, lngFirst and lngRows. On the
' other hand you wouldn't have learned how this big formula was created.
' Now the problem is that if the worksheet is empty, UsedRange will show
' the cell in the first row as the used range. So we have to address this
' issue by checking if all of the following three conditions are true.
' - Check if the resulting cell range is in the first row (1).
' - Check if from the end of the first row to the beginning the result
' is the first cell (1) (all other cells are empty).
' - Check if the cell ("A1") is empty.
If objRngT.Row = 1 And _
.Cells(1, Columns.Count).End(xlToLeft).Column = 1 And _
IsEmpty(.Cells(1, 1)) Then
Debug.Print "objRngT = Nothing (Empty Worksheet)"
Else
Debug.Print "objRngT = " & objRngT.Address
End If
' Although this is a working code, we can conclude that we should have done
' this checking at the beginning which will be done in the advanced version.
End With
Set objRngT = Nothing
End Sub
'*******************************************************************************
Last Used Row & Specified Column Intersection feat.
The Find Method
I would call this the safest and most elegant way: using the Find method.
'*******************************************************************************
' Purpose: Using the Find method, creates a reference to the cell range at *
' the intersection of the last used row and a specified column *
' in a worksheet and prints its address to the Immediate window. *
'*******************************************************************************
Sub LastUR_Column_Find()
Const cVntCol As Variant = "A" ' Column Letter or Number ("A" or 1)
Dim objRngT As Range ' Target Range
With ThisWorkbook.Worksheets("Sheet1")
If Not .Cells.Find("*", .Cells(.Rows.Count, .Columns.Count), -4123, , 1) _
Is Nothing Then
Set objRngT = .Cells(.Cells.Find("*", , , , , 2).Row, cVntCol)
Debug.Print "objRngT = " & objRngT.Address
Set objRngT = Nothing
Else
Debug.Print "objRngT = Nothing (Empty Worksheet)"
End If
End With
End Sub
'*******************************************************************************
' Remarks: If you carefully study the "Find method as it applies to *
' the Range object." from "Visual Basic Help", you will realize *
' why exactly those four arguments and their parameters in *
' the If statement must be included and why three of them can *
' be omitted, but a new one has to be added in the Else clause. *
'*******************************************************************************

How to put into a cell a product of another cell with a variable?

I'm new to vba and I've been trying to make the following code work:
convert = WorksheetFunction.SumIfs(Sheets("Convert").Range("C:C"), _
Sheets("Convert").Range("A:A"), Sheets("Vista").Range("L8"), _
Sheets("Convert").Range("D:D"), Sheets("Vista").Range("C2"), _
Sheets("Convert").Range("E:E"), Sheets("Vista").Range("AC4"))
Sheets("series").Range("L2").FormulaR1C1 = _
"=RC[-8]*"&convert&"
What I'm trying to do, is to put into a variable the result of a SUMIF formula, and use that same value to multiply it with the value of another cell.
It gives me an error of "Application-defined or object-defined error".
Thank you
Arrays Again
The Eliminator
Sub Eliminator()
Dim convert As Long
'Convert = WorksheetFunction.SumIfs(Sheets("Convert").Range("C:C"), _
Sheets("Convert").Range("A:A"), Sheets("Vista").Range("L8"), _
Sheets("Convert").Range("D:D"), Sheets("Vista").Range("C2"), _
Sheets("Convert").Range("E:E"), Sheets("Vista").Range("AC4"))
'e.g.
convert = 1000
Sheets("series").Range("L2").FormulaR1C1 = "=RC[-8]*" & convert
End Sub
Blah, Blah...
Now that we have concluded that the 'Convert' line is causing the error...
Since I use Excel 2003 and you have written the formula correctly, I can only guess that since SumIfs is something like an array formula it can't always be used successfully in VBA, or maybe never!? if you have error values in cells, there might be the solution, because VBA treats them as 'VBA Errors'.
The 'SumIfsless' Solution
So I provided another solution without using SumIfs. You can run it from VBA or any other worksheet. The 'str1' commented lines are for debugging purposes. You can uncomment them and see some 'subtotals' in the Immediate window.
Sub SumIfsArray()
'Variables
'Objects
Dim oRng As Range 'Range of the Sum Column (To Calculate First and Last Row)
'Arrays
Dim arrRngAddress As Variant 'Compare Addresses
Dim arrWs As Variant 'Worksheet Names
Dim arrCol As Variant 'Three Lookup Columns and the Sum Column
Dim arrRng As Variant 'Values of the Compare Addresses
Dim arrRanges As Variant 'The Ranges of the Four Columns
Dim arrArrays As Variant 'The Values of the Four Columns
'Other
Dim iCol As Integer 'Columns Counter
Dim lngFirst As Long 'First Usable Row of Data
Dim lngLast As Long 'Last Usable Row of Data
Dim lngRows As Long 'Number of Rows of Usable Data
Dim lngRow As Long 'Rows Counter
Dim lngSum As Long 'Sum of Values
Dim blnArr As Boolean 'True if all three conditions are met.
' 'Debug Variables
' Const c1 As String = "," 'Debug String Column Separator
' Const r1 As String = vbCr 'Debug String Row Separator
' Dim i1 As Integer 'Debug String Column Counter
' Dim lo1 As Long 'Debug String Rows Counter
' Dim str1 As String 'Debug String Concatenator
'Initialize
arrRngAddress = Array("L8", "C2", "AC4")
arrWs = Array("Convert", "Vista", "series")
arrCol = Array("A:A", "D:D", "E:E", "C:C")
'Program
ReDim arrRng(1 To 3)
With Worksheets(arrWs(1)) 'Worksheet "Vista"
For iCol = 1 To 3
arrRng(iCol) = .Range(arrRngAddress(iCol - 1)).Value
Next
End With
' str1 = "The Values"
' For i1 = 1 To 3: str1 = str1 & r1 & Space(1) & arrRng(i1)
' Next: Debug.Print str1
With Worksheets(arrWs(0)) 'Worksheet "Convert"
'Number of 'usable' rows of data
Set oRng = .Range(arrCol(3))
With oRng
If .Cells(1, 1) <> "" Then
lngFirst = 1
Else
lngFirst = .Cells(1, 1).End(xlDown).Row
End If
lngLast = .Cells(.Rows.Count, .Column).End(xlUp).Row
End With
Set oRng = Nothing
lngRows = lngLast - lngFirst + 1
'Array of Ranges
ReDim arrRanges(1 To 4)
For iCol = 1 To 4
arrRanges(iCol) = Range(Cells(lngFirst, Range(arrCol(iCol - 1)).Column), _
Cells(lngLast, Range(arrCol(iCol - 1)).Column)).Address
Next
' str1 = "The Ranges"
' For i1 = 1 To 4: str1 = str1 & r1 & Space(1) & arrRanges(i1)
' Next: Debug.Print str1
'Array of Arrays
ReDim arrArrays(1 To 4)
For iCol = 1 To 4
arrArrays(iCol) = .Range(arrRanges(iCol)).Value
Next
End With
' str1 = "Values of Ranges" & r1 & Space(1) & "A,D,E,C"
' For lo1 = 1 To lngRows: str1 = str1 & r1 & Space(1): For i1 = 1 To 4
' If i1 <> 1 Then
' str1 = str1 & c1 & arrArrays(i1)(lo1, 1)
' Else: str1 = str1 & arrArrays(i1)(lo1, 1)
' End If: Next: Next: Debug.Print str1
'Sum of Values
For lngRow = 1 To lngRows
For iCol = 1 To 3
If arrArrays(iCol)(lngRow, 1) = arrRng(iCol) Then
blnArr = True
Else
blnArr = False
Exit For
End If
Next
If blnArr = True Then
lngSum = lngSum + arrArrays(4)(lngRow, 1)
End If
Next
' str1 = "The Sum": str1 = str1 & r1 & Space(1) & lngSum
'Output
'Worksheet "series"
Worksheets(arrWs(2)).Range("L2").FormulaR1C1 = "=RC[-8]*" & lngSum
End Sub
P.S. I never ever use variable names with the same name as a worksheet name in the same workbook.

Trim, Copy (insert), Concatenate on selected range

I have a one dimensional column of cells containing text.
I would like to:
strip ".jpg" extension
duplicate each line and insert a copy of the duplicated line beneath it
for each duplicated line (every second line), add a suffix "-Alpha"
apply ".tif" extension to all of the cells
Data looks like this:
0120-052.jpg
0120-053.jpg
0120-054.jpg
0120-055.jpg
0120-056.jpg
I would like to select that range and it appear like so:
0120-052.tif
0120-052-Alpha.tif
0120-053.tif
0120-053-Alpha.tif
0120-054.tif
0120-054-Alpha.tif
0120-055.tif
0120-055-Alpha.tif
0120-056.tif
0120-056-Alpha.tif
I found out how to insert entire rows between the existing data, but I have other data to the left of this data and don't want to have blank rows running across my entire spreadsheet. I did find a way to insert blanks between the existing data but I could not figure out how to instead paste the data when inserting. I fudged something together, but it tried to paste infinitely.
I think I need to put it all into an array and iterate on a step by step basis, but I was unable to figure out how to do that based off of an arbitrary selection.
Sub PasteInsertRowsAfter()
Dim MyCell As Range
For Each MyCell In Selection
If MyCell.Value <> "" Then
MyCell.Copy
MyCell.Offset(1, 0).Insert shift:=xlDown
MyCell.Offset(2, 0).Select
End If
Next MyCell
End Sub
Does this work for you?
Sub PasteInsertRowsAfter()
Dim i As Long
Dim MyCell As Range
Dim Rng As Range
Set Rng = Selection
For i = Rng.Cells.Count To 1 Step -1
Set MyCell = Rng.Cells(i)
MyCell.Copy
MyCell.Offset(1, 0).Insert shift:=xlDown
MyCell.Value = Replace(MyCell.Value, ".jpg", ".tif")
MyCell.Offset(1, 0).Value = Replace(MyCell.Offset(1, 0), ".jpg", "-Alpha.tif")
Next i
End Sub
This sounds like bad data structure to me (inserting rows) so this solution will be based on a column structured table. However, I don't know much else about the data so this could be a wrong assumption on my end.
You could store your values in columns instead like so | Original String | .jpg | -Alpha.tif |
Where Original String is the header for Column A and so on. Your data will be better organized this way since all modifications of original string will be stored on a single row. This structure will allow you to add other info that may be relevant at some point in time (source, date, etc.). You can create pivots with this format and monitor for duplicates easier. You can even store the original string.
Input/Output of macro are below.
This sub is a simple loop that does not take a Slection range.
Sub Alternative()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim MyRange As Range: Set MyRange = ws.Range("A2:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
Dim MyCell As Range
Application.ScreenUpdating = False
For Each MyCell In MyRange
MyCell.Offset(, 1) = Replace(MyCell, "jpg", "tif")
MyCell.Offset(, 2) = Replace(MyCell, ".jpg", "-Alpha.tif")
Next MyCell
Application.ScreenUpdating = True
End Sub
Here is an option that allows the user to select a range once the macro is launched. Just as the above solution, the macro will output the data in the 2 columns to left of selected range.
Sub Alternative()
Dim MyRange As Range, MyCell As Range
On Error Resume Next 'Allow for Cancel Button
Set MyRange = Application.InputBox("Select Range", Type:=8)
On Error GoTo 0
If Not MyRange Is Nothing Then
Application.ScreenUpdating = False
For Each MyCell In MyRange
MyCell.Offset(, 1) = Replace(MyCell, "jpg", "tif")
MyCell.Offset(, 2) = Replace(MyCell, ".jpg", "-Alpha.tif")
Next MyCell
Application.ScreenUpdating = True
End If
End Sub
Trim160ConcatArrayPaste
Option Explicit
'With Sub ======================================================================
' .Title: Trim160ConcatArrayPaste
' .Author: YMG
'-------------------------------------------------------------------------------
Sub Trim160ConcatArrayPaste()
'Description
' Manipulates data in a selected worksheet range and pastes the result into
' another range (overwriting the former range and more).
'Parameters
' None
'Returns
' Manipulated data in a range.
'
'-- Customize BEGIN --------------------
Const cStr1 As String = ".jpg"
Const cStr2 As String = ".tif"
Const cStr3 As String = "-Alpha.tif"
'If the result should be pasted into another row. Probably useless.
Const loROff As Long = 0 'Row Offset for Array Data
''''''''''''''''''''''''''''''''''''''''
'If the result should be pasted into another column
Const iCOff As Integer = 0 'Column Offset for Array Data
'Remarks:
' I strongly urge you to consider pasting the data into another column e.g.
' the column adjacent to the right of the starting column (Set iCoff = 1).
' If something goes wrong while pasting you will overwrite your initial data
' and you might lose a lot of time getting it back.
' Creating a log file might be considered.
''''''''''''''''''''''''''''''''''''''''
'
'-- Customize END ----------------------
'
Dim oXL As Application 'Exel Application Object
Dim oWb As Workbook 'Workbook Object - ActiveWorkbook
Dim oWs As Worksheet 'Worksheet Object - ActiveSheet
Dim oRng As Range 'Range Object - Range to read from, Range to write to
Dim oCell As Range 'Cell - Range Object - All cells of oRng
Dim arrTCC() As String
Dim lo1 As Long 'Data Entries Counter, Array Entries Counter
Dim strCell As String
Dim strArrRng As String
'
'-------------------------------------------------------------------------------
'Assumptions
' There is a contiguous range (oRng) in the ActiveSheet (oWs) of the
' ActiveWorkbook (oWb) that contains a list of entries in its cells
' (oRng.Cells) to be processed. ('Data' for 'list of entries' in further text)
' The actual range of the Data is selected.
'-------------------------------------------------------------------------------
'
Set oXL = Application
Set oWb = ActiveWorkbook
Set oWs = oWb.ActiveSheet
Set oRng = oXL.Selection
'
'Remarks:
' The Selection Property is a property of the Application object and the
' Window object. Visual Basic doesn't allow ActiveWorkbook.Selection or
' ActiveSheet.Selection.
'
''''''''''''''''''''''''''''''''''''''''
'Task:
' Count the number of Data entries.
'
lo1 = 0 'Data Entries Counter
For Each oCell In oRng.Cells
lo1 = lo1 + 1
Next
'
'Status:
' 'lo1' is the number of Data entries which will be used to determine the
' size of an array in the following code.
'
''''''''''''''''''''''''''''''''''''''''
'Task: Populate an array with the desired results.
'
ReDim arrTCC(1 To lo1 * 2, 1 To 1)
'Explaination:
'"lo1" - Number of list entries.
'" * 2" - Making 2 entries out of each entry.
lo1 = 0 'Array Entries Counter (This is a 1-based array.)
For Each oCell In oRng.Cells
'Clean the text of the Data entries.
strCell = Trim(oCell.Text)
'Remarks:
'Chr(160) which is a non-breaking space (HTML Name: ) is at
'the end of the Data entries. The Trim function doen't clean
'non-breaking spaces.
strCell = Replace(strCell, Chr(160), "")
'Check the last part of the string
If Right(strCell, Len(cStr1)) = cStr1 Then
'Populate array.
lo1 = lo1 + 1
arrTCC(lo1, 1) = Replace(strCell, cStr1, cStr2)
lo1 = lo1 + 1
arrTCC(lo1, 1) = Replace(strCell, cStr1, cStr3)
'If the cell doesn't end with cStr1:
Else 'This should never happen, remember: COUNTIGUOUS.
'An Idea
' lo1 = lo1 + 1
' arrTCC(lo1, 1) = ""
' lo1 = lo1 + 1
' arrTCC(lo1, 1) = ""
MsgBox "You might have selected a wrong range.", vbCritical
Exit Sub
End If
Next
'
' For lo1 = LBound(arrTCC) To UBound(arrTCC)
' Debug.Print arrTCC(lo1, 1)
' Next
' Debug.Print LBound(arrTCC)
' Debug.Print UBound(arrTCC)
'
'Status: The array 'arrTCC' is populated
'
''''''''''''''''''''''''''''''''''''''''
'Task:
' Determine the range where to paste the data from array and paste the
' array into the range.
'
'Calculate the 'Start' Cell Address
strArrRng = oRng.Cells(1 + loROff, 1 + iCOff).Address
'
' Debug.Print strArrRng
'
'Add the ":" (Address Separator) and the Calculated 'End' Cell Address
strArrRng = strArrRng & ":" & _
oRng.Cells(UBound(arrTCC) + loROff, 1 + iCOff).Address
'Paste the Array to the Worksheet
Set oRng = oWs.Range(strArrRng)
'
' Debug.Print strArrRng
' Debug.Print oRng.Address
'
oRng = arrTCC
'
'Status: Done
'
'Remarks:
'Testing the program was done with iCoff = 1 i.e. pasting the array data
'into the column adjacent to the right of the starting column. Since it uses
'overwriting the Data, the Data would always need to be written back for
'further testing.
'Some debugging code has deliberately been commented and left inside the
'program to remind amateurs like myself of debugging importance.
'Some other aspects of this program could be considered like the column
'of the data could be known or unknown so a range, a column or the
'ActiveCell would have or don't have to be selected etc.
'
End Sub
'-------------------------------------------------------------------------------
'With Source Idea --------------------------------------------------------------
' .Title: Excel VBA seemingly simple problem: Trim, Copy (insert), Concat on selected range
' .TitleLink: https://stackoverflow.com/questions/52548294/excel-vba-seemingly-simple-problem-trim-copy-insert-concat-on-selected-rang
' .Author: NewbieStackOr
' .AuthorLink: https://stackoverflow.com/users/10427336/newbiestackor
'End With ----------------------------------------------------------------------
'End With ======================================================================

Resources