Remove last comma from a cell in a dynamic range - excel

I'm trying to delete the last comma from each cell of a dynamic range.
The macro doesn't delete the comma, it just selects the range.
Sub selecting()
Dim sht As Worksheet
Dim LastColumn As Long
Dim StartCell As Range
Set sht = Worksheets("Sheet1")
Set StartCell = Range("D1")
'Find Last Row and Column
LastRow = sht.Cells(sht.Rows.Count, StartCell.Column).End(xlUp).row
LastColumn = sht.Cells(StartCell.row, sht.Columns.Count).End(xlToLeft).Column
'Select Range
sht.Range(StartCell, sht.Cells(LastRow, LastColumn)).Select
With ActiveCell
If Right(.Value, 1) = "," Then .Value = Right(.Value, Len(.Value) - 1)
End With
End Sub
Here is what is returned

Since you want to remove the last character from each cell in column D, try this variation on braX's comment. It loops trough each used cell in column 4 and deletes the last character.
With ThisWorkbook.Sheets("Sheet1")
For Each cel In .Range("D1", .Cells(.Rows.Count, 4).End(xlUp))
cel.Value = Left(cel, Len(cel) - 1)
Next cel
End With

The most conventional way would be to loop over your cells:
Sub Replacing()
Dim lr As Long, lc As Long
Dim rng As Range, cl As Range
With Worksheets("Sheet1")
'Find Last Row and Column
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
lc = .Cells(1, .Columns.Count).End(xlToLeft).Column
'Go through range
Set rng = .Range(.Cells(1, lc), .Cells(lr, lc))
For Each cl In rng
If Right(cl.Value, 1) = "," Then cl.Value = Left(cl.Value, Len(cl.Value) - 1)
Next cl
End With
End Sub
Better would be to go through memory if your range is actually much larger (for performance sake)
Sub Replacing()
Dim lr As Long, lc As Long, x As Long
Dim arr As Variant
With Worksheets("Sheet1")
'Find Last Row and Column
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
lc = .Cells(1, .Columns.Count).End(xlToLeft).Column
'Go through array
arr = .Range(.Cells(1, lc), .Cells(lr, lc)).Value
For x = LBound(arr) To UBound(arr)
If Right(arr(x, 1), 1) = "," Then arr(x, 1) = Left(arr(x, 1), Len(arr(x, 1)) - 1)
Next x
'Write array back to range
.Range(.Cells(1, lc), .Cells(lr, lc)).Value = arr
End With
End Sub
And a more less conventional way (alright for small ranges I guess) would be to evalate a range and avoid an iteration. This however comes at the cost of an array formula:
Sub Replacing()
Dim lr As Long, lc As Long
Dim rng As Range
With Worksheets("Sheet1")
'Find Last Row and Column
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
lc = .Cells(1, .Columns.Count).End(xlToLeft).Column
'Evaluate your range
Set rng = .Range(.Cells(1, lc), .Cells(lr, lc))
rng.Value = .Evaluate("IF(RIGHT(" & rng.Address & ",1)="","",LEFT(" & rng.Address & ",LEN(" & rng.Address & ")-1)," & rng.Address & ")")
End With
End Sub

Use the Replace() function:
For Each Cell in Range.Cells
Cell.Value = Replace(Cell.Text, ",", "")
Next Cell
Edit: After testing, replaced .Text with .Value
Edit 2: I'd also like to add, why are you selecting the range? My supposition is that you are selecting it to enable use of ActiveCell but you can simply manipulate the range without selecting it. Selecting the range is asking to incur errors. My suggestion is:
Dim rplcRng as Range
Set rplcRange = sht.Range(StartCell, sht.Cells(LastRow, LastColumn))
For Each Cell in rplcRng.Cells
Cell.Value = Replace(Cell.Text, ",", "")
Next Cell
Edit 3: added "s"s

Related

How to remove duplicates out from a specific range but leave other data on the rights side and below?

I copy data from a list into another list in different sheet, whenever I copy data it copy the cells which are in the second list already. I need to delete any duplicates in the sheet number 2, though I always end up deleting everything including rows, gridlines and mostly the data on the right of columns. I only use cells from "A13", "B13" and "C13" down. There are data on the right, specifically formulas which are rather important. How can I only apply cleansing of duplicates on that range?
Sub test()
Dim LastRow As Long, i As Long
Dim rng As Range
Set rng = Worksheets("ABCX Acrylics").Range("A13").CurrentRegion
With Worksheets("Sheet1")
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 1 To LastRow
If .Cells(i, 6) = "Acrylics" Then
With Worksheets("ABCX Acrylics")
.Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0) = _
Worksheets("Sheet1").Cells(i, 1).Value
.Cells(.Rows.Count, "A").End(xlUp).Offset(0, 1) = _
Worksheets("Sheet1").Cells(i, 8).Value
.Cells(.Rows.Count, "A").End(xlUp).Offset(0, 2) = _
Worksheets("Sheet1").Cells(i, 9).Value
End With
End If
Next i
rng.RemoveDuplicates Columns:=1, Header:=xlYes
End With
End Sub
This sets your rng as the first three columns and also the RemoveDuplicates is an array of the first three columns. If you only include column 1, it removes all duplicates that have the first column match only. Also got rid of your nested End With statements to make it easier to follow.
Sub test()
Dim i As Long
Dim rng As Range
Dim ws1LR As Long
Dim ws2LR As Long
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set ws1 = ThisWorkbook.Worksheets("ABCX Acrylics")
Set ws2 = ThisWorkbook.Worksheets("Sheet1")
ws1LR = ws1.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = ws1.Range(ws1.Cells(13, 1), ws1.Cells(ws1LR, 3))
ws2LR = ws2.Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To ws2LR
If ws2.Cells(i, 6) = "Acrylics" Then
ws1.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0) = _
ws2.Cells(i, 1).Value
ws1.Cells(Rows.Count, "A").End(xlUp).Offset(0, 1) = _
ws2.Cells(i, 8).Value
ws1.Cells(Rows.Count, "A").End(xlUp).Offset(0, 2) = _
ws2.Cells(i, 9).Value
End If
Next i
rng.RemoveDuplicates Columns:=Array(1, 2, 3), Header:=xlYes
End With
End Sub
Here we go, this code works for me. However, there is a problem with my gridlines as they are erased. I need to have the same format as it was in "A13" before I applied my code. I think of Scripting.Dictionary to store the format. Any idea? Better approach?
Option Explicit
Sub test()
Dim LastRow As Long, i As Long
Dim rng8 As Range
Set rng8 = Worksheets("ABCX Acrylics").Range("A13:C1370")
With Worksheets("Sheet1")
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 1 To LastRow
If .Cells(i, 6) = "Acrylics" Then
With Worksheets("ABCX Acrylics")
.Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0) = _
Worksheets("Sheet1").Cells(i, 1).Value
.Cells(.Rows.Count, "A").End(xlUp).Offset(0, 1) = _
Worksheets("Sheet1").Cells(i, 8).Value
.Cells(.Rows.Count, "A").End(xlUp).Offset(0, 2) = _
Worksheets("Sheet1").Cells(i, 9).Value
rng8.RemoveDuplicates Columns:=Array(1, 2, 3)
End With
End If
Next i
End With
End Sub

VBA - Vlookup Multiple Columns and Fill to End of Range

I need to do a Vlookup of an ID on the source sheet to a table in the data sheet. When the Vlookup is done, it needs to return the cell values from 6 different columns.
Here I have a function to get the range:
Function find_Col(header As String) As Range
Dim aCell As Range, rng As Range, def_Header As Range
Dim col As Long, lRow As Long, defCol As Long
Dim colName As String, defColName As String
Dim y As Workbook
Dim ws1 As Worksheet
Set y = Workbooks("Template.xlsm")
Set ws1 = y.Sheets("Results")
With ws1
Set def_Header = Cells.Find(what:="ID", LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, SearchFormat:=False)
Set aCell = .Range("B2:Z2").Find(what:=header, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
defCol = def_Header.Column
defColName = Split(.Cells(, defCol).Address, "$")(1)
col = aCell.Column
colName = Split(.Cells(, col).Address, "$")(1)
lRow = Range(defColName & .Rows.count).End(xlUp).Row - 1
Set myCol = Range(colName & "2")
'This is your range
Set find_Col = Range(myCol.Address & ":" & colName & lRow).Offset(1, 0)
'If not found
Else
MsgBox "Column Not Found"
End If
End With
End Function
Then in my sub, I select the range and do a Vlookup which fills this range:
Selection.FormulaR1C1 = "=VLOOKUP(RC[-4],myTable,2,FALSE)"
And this works great.
Then I needed to return more than just one column, so I ended up with the formula:
Selection.FormulaArray = "=VLOOKUP($C3,myTable,{2,3,4,5,6},FALSE)"
Source Sheet:
Data Sheet:
So, my function returns only the range for one column, which I think I can use in terms of getting a row count then using something like this:
Set myRng = find_Col("Product")
For currentRow = myRng.Rows.count To 1 Step -1
Selection.FormulaArray = "=VLOOKUP($C3,myTable,{2,3,4,5,6},FALSE)"
Next currentRow
Then perhaps instead of C3 it could look something like this:
C & currentRow --> Selection.FormulaArray = "=VLOOKUP($C & currentRow,myTable,{2,3,4,5,6},FALSE)"
But then I have the issue that only one cell is selected (G3) and from H-L is not. And I have no idea whether this is even a plausible effort.
Ideally of course, I would have cells G3:L3 selected and fill the formula down to the last row.
My brain is just fried from all the thinking and attempts.
So this should do the trick... I've explained every instance but if you need help understanding just ask:
Option Explicit
Sub FillData1()
Dim ws As Worksheet, wsData As Worksheet, arr As Variant, arrData As Variant
Dim DictHeaders As Scripting.Dictionary, DictIds As Scripting.Dictionary, DictDataHeaders As Scripting.Dictionary, _
DictDataIds As Scripting.Dictionary
Dim LastRow As Long, LastCol As Integer, i As Long, j As Integer
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
With ThisWorkbook
Set ws = .Sheets("Results")
Set wsData = .Sheets("List")
End With
'Lets suppose your data always starts on row 2 in both sheets and column B will always have the max amount of rows filled
With ws 'filling the first array
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
arr = .Range("B2", .Cells(LastRow, LastCol)).Value
End With
With wsData 'filling the data array
LastRow = .Cells(.Rows.Count, 4).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
arrData = .Range("A2", .Cells(LastRow, LastCol)).Value
End With
'Now lets put everything into Dictionaries so if the data moves columns or rows won't matter
Set DictHeaders = New Scripting.Dictionary
Set DictIds = New Scripting.Dictionary
For i = 1 To UBound(arr, 2) 'this will fill the headers positions on the main sheet
If Not DictHeaders.Exists(arr(1, i)) Then DictHeaders.Add arr(1, i), i
Next i
For i = 2 To UBound(arr, 1) 'this will fill the IDs positions on the main sheet
If Not DictIds.Exists(arr(i, DictHeaders("KW ID"))) Then DictIds.Add arr(i, 1), i
Next i
Set DictDataHeaders = New Scripting.Dictionary
Set DictDataIds = New Scripting.Dictionary
For i = 1 To UBound(arrData, 2) 'this will fill the headers positions on the data sheet
If Not DictDataHeaders.Exists(arrData(1, i)) Then DictDataHeaders.Add arrData(1, i), i
Next i
For i = 2 To UBound(arrData, 1) 'this will fill the IDs positions on the data sheet
If Not DictDataIds.Exists(arrData(i, DictDataHeaders("KW ID"))) Then DictDataIds.Add arrData(i, DictDataHeaders("KW ID")), i
Next i
'Finally will loop through the main array to fill it with the data from the data array
On Error Resume Next
For i = 2 To UBound(arr)
For j = 6 To UBound(arr, 2) 'I'm assuming you want to avoid the first columns which are hidden
arr(i, j) = arrData(DictDataIds(arr(i, 1)), DictDataHeaders(arr(1, j)))
Next j
Next i
On Error GoTo 0
With ws 'filling the first array
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
.Range("B2", .Cells(LastRow, LastCol)).Value = arr
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
I don't know if I got the true issue of your goal. However, since your Selection parts in your code should be avoid, why don't make something like the following?
Set myRng = find_Col("Product")
For currentRow = myRng.Rows.count To 1 Step -1
Range(Cells(currentRow, 5), Cells(currentRow, 9)).FormulaArray = "=VLOOKUP(RC3,myTable,{2,3,4,5,6},FALSE)"
Next currentRow

How to hide row based on cell value in last column of worksheet?

I'm trying to create a macro where the last column of a worksheet is searched for a value, and if that value is found in a cell that cell's entire row is hidden. I'm also trying to use a dynamic range for this as the last column will change.
With ws1
LastColumn = .Cells(1, Columns.Count).End(xlToLeft).Column
LastRow = .Cells(.Rows.Count, LastColumn).End(xlUp).Row
Set rDataRange = .Range(.Cells(2, LastColumn), .Cells(LastRow, LastColumn))
For Each rCell in RDataRange
If rCell.Value = "Yes" Then
rCell.EntireRow.Hidden = True
End If
Next rCell
End With
Anyone have an idea as to why this might not be working?
Try:
Option Explicit
Sub test()
Dim LastColumn As Long, Lastrow As Long, i As Long
Dim str As String
With ThisWorkbook.Worksheets("Sheet1")
str = "Test"
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
Lastrow = .Cells(.Rows.Count, LastColumn).End(xlUp).Row
For i = 1 To Lastrow
If .Cells(i, LastColumn).Value = str Then
.Rows(i).EntireRow.Hidden = True
End If
Next i
End With
End Sub

VBA to Vlookup inside a Loop that compares data between 2 sheets and pastes de repeated values in a 3rd sheet

My objective is to compare data in a range in Ws1 with data in a range in ws2, and copy those values that repeat in ws3.
Ideally I would like to copy the value found and the rest of the information to the right of that cell (from ws2), but for now I am happy just copying the value found.
I decided to use a loop for this but I was getting an infinite looping, and now that I re-summarize; I am getting:
range of object _ global failed" error and it points to: "With
Range(ws3.cells(i, 1))
Sub quicktests()
Dim ws1, ws2, ws3 As Worksheet
Dim ColNum, ColNum2 As Long
Dim rng, Range2 As Range
Dim lRow1, lRow2, lCol2 As Integer
ColNum = 9
ColNum2 = 14
lRow1 = 347
Set ws2 = Sheets("Filled today")
With ws2
lCol2 = .cells(1, .Columns.Count).End(xlToLeft).Column
'MsgBox lCol2
lRow2 = .cells(.Rows.Count, 1).End(xlUp).Row
'MsgBox lRow2
Set Range2 = .Range(.cells(1, ColNum2), .cells(lRow2, lCol2))
End With
Set ws3 = Sheets("Duplicates filled and hiring")
Set ws1 = Sheets("Reconciliated Recruiment Plan")
For i = 1 To lRow1
With ws1
Set rng = .cells(i, ColNum)
End With
With Range(ws3.cells(i, 1))
.Formula = "=VLookup(rng, Range2, ColNum, False)"
.Value = .Value
End With
Next i
End Sub
Looking at just the part with the VLOOKUP:
You can't used range with one cells() it needs a begining and an end, remove the Range wrapper.
The Vlookup; You need to remove the vba part from the string and concatenate it.
With ws3.cells(i, 1)
.Formula = "=VLookup(" & rng.Address(0,0) & "," & Range2.Address(0,0) & "," & ColNum & ", False)"
.Value = .Value
End With

VBA Collection issue

I an trying towrite a simple VBA code where some cell values are combined.
Problem with code bellow is that Cell Object in the loop keeps selecting whole row, not just one cell in Row Collection
Dim Cell As Range
Dim Row As Range
Set Row = Rows(ActiveCell.Row)
Set Cell = ActiveCell
For Each Cell In Row
With Cell
If IsNumeric(InStr(1, Right(.Value, 1), "/")) Then
.Value = .Value & .Offset(0, 1).Value
.Offset(0, 1).Delete (xlShiftToLeft)
End If
End With
Next Cell
Try this. For this example, assumptions made about data being on Sheet1 and the start row (stRow) and start col (testCol) of the data. Amend these to suit your conditions.
Option Explicit
Sub combine()
Dim ws As Worksheet
Dim stRow As Long, endRow As Long, testCol As Long, endCol As Long
Dim rnum As Long, cnum As Long
Dim cl As Range
Set ws = Sheets("Sheet1")
stRow = 1
testCol = 1
With ws
endRow = .Cells(Rows.Count, testCol).End(xlUp).Row
For rnum = stRow To endRow
endCol = .Cells(rnum, Columns.Count).End(xlToLeft).Column
For cnum = testCol To endCol - 1
Set cl = .Cells(rnum, cnum)
If Right(cl, 1) = "/" And Right(cl.Offset(0, 1), 1) <> "/" Then
If IsNumeric(Left(cl.Value, Len(cl.Value) - 1)) Then
cl.Value = cl.Value & cl.Offset(0, 1).Value
cl.Offset(0, 1).Delete (xlShiftToLeft)
End If
End If
Next cnum
Next rnum
End With
End Sub
Although not specified by you, this code does not combine an adjacent cell which also has a trailing "/". This on the basis that we shouldn't 'remove' a 'test' value. If this condition not required it is easily changed.

Resources