Find last column with data and add data after that column - excel

I have some data in my worksheet and what I want to achieve is that I want to add data after 1 column from the last column with data.
I have tried the Range().End property but I think I am not able to implement it correctly.
colnum = Sheet3.Range("A:Z").End(xlToLeft).Column + 2
The above code gives me the column number as '1' and plus 2 means it gives column number '3' which is 'C'.
Data in my worksheet is something like this:
I want a dynamic search and my code should find column 'E' as the last column with data and start adding data from column 'F'. Any suggestions are appreciated.

This function returns an array where the first element is the last row number, and the second element the last column number.
Function LastRowCol(Worksht) As Long()
'Uncomment if on worksheet
'Application.Volatile
Dim WS As Worksheet, R As Range
Dim LastRow As Long, LastCol As Long
Dim L(1) As Long
Select Case TypeName(Worksht)
Case "String"
Set WS = Worksheets(Worksht)
Case "Worksheet"
Set WS = Worksht
End Select
With WS
Set R = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not R Is Nothing Then
LastRow = R.Row
LastCol = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
L(0) = LastRow
L(1) = LastCol
LastRowCol = L
End Function
Sub change_code_name()
'Requires Trust Access to VBA Object Model
Dim wbk As Object, sheet As Object
ActiveWorkbook.VBProject.name = "VBAProject"
Set wbk = ActiveWorkbook.VBProject.VBComponents(ActiveWorkbook.CodeName)
wbk.name = "wbk_code_name"
Set sheet = ActiveWorkbook.VBProject.VBComponents(ActiveWorkbook.Sheets(1).CodeName)
sheet.name = "sheet_code_name"
End Sub
In the case of your code line, assuming your worksheet CodeName is Sheet3, it would be something like:
colnum = LastRowCol(Sheet3)(1) + 1
and would apply to the active workbook.
See also Error in finding last used cell in Excel with VBA for an extensive discussion of the various methods of finding the last row/column and the pitfalls of each.
Edit:
Examining your screenshot, it seems that you might want to exclude Row 1 from the testing. If that is the case, then try this slightly modified code:
Option Explicit
Function LastRowCol(Worksht, Optional excludeRows As Long = 0) As Long()
'Uncomment if on worksheet
'Application.Volatile
Dim WS As Worksheet, R As Range
Dim LastRow As Long, LastCol As Long
Dim L(1) As Long
Dim searchRng As Range
Select Case TypeName(Worksht)
Case "String"
Set WS = Worksheets(Worksht)
Case "Worksheet"
Set WS = Worksht
End Select
If excludeRows > 0 Then
With WS
Set searchRng = Range(.Cells(excludeRows + 1, 1), .Cells(.Rows.Count, .Columns.Count))
End With
Else
Set searchRng = WS.Cells
End If
With searchRng
Set R = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not R Is Nothing Then
LastRow = R.Row
LastCol = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
L(0) = excludeRows + 1
L(1) = LastCol
LastRowCol = L
End Function
and, in your macro:
colnum = LastRowCol(Sheet3,1)(1) + 1

Related

find and select the finding until the next find

Basically, I'm writing a code that finds text in a Master sheet, I am looking for "Admin" after finding the admin I need to select from this cell unit next find and paste in separate sheets.
I tried different ways but now work, any suggestions?
Example
Sub FindNext_Example()
Dim FindValue As String
FindValue = "Bangalore"
Dim Rng As Range
Set Rng = Range("A2:A11")
Dim FindRng As Range
Set FindRng = Rng.Find(What:=FindValue)
Dim FirstCell As String
FirstCell = FindRng.Address
Do
Range(FristCell).Select
Selection.Copy
Worksheets.Add
ActiveSheet.Paste
Sheets("Sheet0").Select
Set FindRng = Rng.FindNext(FindRng)
Loop While FirstCell <> FindRng.Address
MsgBox "Search is over"
End Sub
Example
Example of finding and select the find row until next find
paste in new sheet
next find
until the end
Try this code:
Sub SubChopList()
'Declarations.
Dim DblColumnOffset As Double
Dim RngSource As Range
Dim RngSearch As Range
Dim RngTop As Range
Dim RngBottom As Range
Dim StrSearch As String
Dim StrDestinationAddress As String
Dim WksSource As Worksheet
'Settings.
Set WksSource = ActiveSheet
Set RngSource = WksSource.Range("A1")
Set RngSource = Range(RngSource, RngSource.End(xlDown).End(xlToRight))
'Setting DblColumnOffset equal to the offset from the first column of RngSource and the column to be searched.
DblColumnOffset = 2
'Setting the column to be searched.
Set RngSearch = RngSource.Columns(1).Offset(0, DblColumnOffset)
'Setting the value to be searched.
StrSearch = "Admin"
'Setting the address of the cell where the data will be pasted in the new sheets.
StrDestinationAddress = "A1"
'Setting RngTop as the first cell that contains StrSearch after the first cell of RngSearch.
Set RngTop = RngSearch.Find(What:=StrSearch, _
After:=RngSearch.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False _
)
'Setting RngBottom as the cell in the row upon the first cell that contains StrSearch after RngTop.
Set RngBottom = RngSearch.Find(What:=StrSearch, _
After:=RngTop, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False _
).Offset(-1, 0)
'Repeating until the last block is reached.
Do
'Creating a new sheet.
Worksheets.Add
'Copy-pasting the block delimited by RngTop and RngBottom in the new sheet at the address specified in StrDestinationAddress.
WksSource.Range(RngTop, RngBottom).Offset(0, -DblColumnOffset).Resize(, RngSource.Columns.Count).Copy Range(StrDestinationAddress)
'Setting RngTop as the first cell that contains StrSearch after RngBottom.
Set RngTop = RngSearch.Find(What:=StrSearch, _
After:=RngBottom, _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False _
)
'Setting RngBottom as the cell in the row upon the first cell that contains StrSearch after RngTop.
Set RngBottom = RngSearch.Find(What:=StrSearch, _
After:=RngTop, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False _
).Offset(-1, 0)
Loop Until RngTop.Row > RngBottom.Row
'Reporting the last block as did for all the previous blocks in the Do Loop cycle.
Set RngBottom = RngSearch.Cells(RngSearch.Rows.Count, 1)
Worksheets.Add
WksSource.Range(RngTop, RngBottom).Offset(0, -DblColumnOffset).Resize(, RngSource.Columns.Count).Copy Range(StrDestinationAddress)
End Sub
Select the sheet with the data you want to chop and run it.
Create Criteria Worksheets
Adjust the values in the constants section.
The Code
Option Explicit
Sub addCriteriaWorksheets()
Const wsName As String = "Sheet1"
Const sCellAddress As String = "A1"
Const Criteria As String = "Admin*"
Const CriteriaColumn As Long = 3
Const dCellAddress As String = "A1"
Dim wb As Workbook: Set wb = ThisWorkbook
Application.ScreenUpdating = False
With wb.Worksheets(wsName).Range(sCellAddress).CurrentRegion
.Worksheet.AutoFilterMode = False
.AutoFilter CriteriaColumn, Criteria
Dim rg As Range
On Error GoTo SpecialCellsError
Set rg = .Columns(CriteriaColumn).Resize(.Rows.Count - 1).Offset(1) _
.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
Dim nCount As Long: nCount = rg.Cells.Count
Dim Coord As Variant: ReDim Coord(1 To nCount, 1 To 3)
Dim arg As Range
Dim cel As Range
Dim n As Long
For Each arg In rg.Areas
For Each cel In arg.Cells
n = n + 1
Coord(n, 1) = cel.Row
If n > 1 Then
Coord(n - 1, 2) = Coord(n, 1) - 1
Coord(n - 1, 3) = Coord(n - 1, 2) - Coord(n - 1, 1) + 2
End If
Next cel
Next arg
n = n + 1
Coord(n - 1, 2) = .Rows.Count
Coord(n - 1, 3) = Coord(n - 1, 2) - Coord(n - 1, 1) + 2
.Worksheet.AutoFilterMode = False
Dim cCount As Long: cCount = .Columns.Count
Dim Data As Variant: Data = .Value
Dim Result As Variant
Dim i As Long, j As Long, k As Long
For n = 1 To nCount
ReDim Result(1 To Coord(n, 3), 1 To cCount)
For j = 1 To cCount
Result(1, j) = Data(1, j)
Next j
k = 1
For i = Coord(n, 1) To Coord(n, 2)
k = k + 1
For j = 1 To cCount
Result(k, j) = Data(i, j)
Next j
Next i
With wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
.Range(dCellAddress).Resize(k, cCount).Value = Result
End With
Next n
.Worksheet.Select
End With
ProcExit:
Application.ScreenUpdating = True
Exit Sub
SpecialCellsError:
Resume ProcExit
End Sub

Concatenate string (VBA) for particular columns

Goal: Add the string "Z" to a select few columns for all rows except the header. Concatenate only on select headers i.e. headers defined in the array.
Dim header As Range
ArrayCheck = Array("CarTime", "BusTime", "PlaneTime")
LastRow = desWS1.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lcol = desWS1.Cells(1, Columns.Count).End(xlToLeft).Column
For Each header In desWS1.Range(desWS1.Cells(1, 1), desWS1.Cells(1, lcol))
For i = LBound(ArrayCheck) To UBound(ArrayCheck)
If header = ArrayCheck(i) Then
desWS1.Range(desWS1.Cells(2, header.Column), desWS1.Cells(LastRow, header.Column)) & "Z"
End If
Next i
Next
all entries in these columns are of the form: yyyy-mm-ddThh:mm:ss
#SiddharthRout the current cell is: 2020-09-07T13:08:46, and the output i want is: 2020-09-07T13:08:46Z. So yep, you're right, it's a string. – Jak Carty 2 mins ago
In my below code, I will take a sample of both date and date stored as text. I have commented the code so you should not have a problem understanding it. But if you do then simply post back.
Is this what you are trying?
Code:
WAY 1
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, lCol As Long
Dim ArrayCheck As Variant
Dim i As Long, j As Long
Dim rng As Range
ArrayCheck = Array("CarTime", "BusTime", "PlaneTime")
'~~> Set this to the relevant worksheet
Set ws = Sheet1
With ws
'~~> Find last row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last col
lCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
'~~> Loop though the cell in 1st row
For i = 1 To lCol
'~~> Loop through the array
For j = LBound(ArrayCheck) To UBound(ArrayCheck)
'~~> Check if they match
If .Cells(1, i).Value2 = ArrayCheck(j) Then
'~~> Set your range from cell 2 onwards
Set rng = .Range(.Cells(2, i), .Cells(lRow, i))
'~~> Add "Z" to the entire range in ONE GO i.e without looping
'~~> To understand this visit the url below
'https://stackoverflow.com/questions/19985895/convert-an-entire-range-to-uppercase-without-looping-through-all-the-cells
rng.Value = Evaluate("index(Concatenate(" & rng.Address & ",""Z""" & "),)")
End If
Next j
Next i
End With
End Sub
Note: For the sake of clarity, I am not joining the string ",""Z""" & "),)")
In Action
WAY 2
Introducing a 2nd way
This code writes to array and then works with it.
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, lCol As Long
Dim ArrayCheck As Variant
Dim i As Long, j As Long, k As Long
Dim rng As Range
Dim tmpAr As Variant
ArrayCheck = Array("CarTime", "BusTime", "PlaneTime")
'~~> Set this to the relevant worksheet
Set ws = Sheet1
With ws
'~~> Find last row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last col
lCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
'~~> Loop though the cell in 1st row
For i = 1 To lCol
'~~> Loop through the array
For j = LBound(ArrayCheck) To UBound(ArrayCheck)
'~~> Check if they match
If .Cells(1, i).Value2 = ArrayCheck(j) Then
'~> Set your range
Set rng = .Range(.Cells(2, i), .Cells(lRow, i))
'~~> Store the value in array
tmpAr = rng.Value2
'~~> Work with array
For k = 1 To UBound(tmpAr)
tmpAr(k, 1) = tmpAr(k, 1) & "Z"
Next k
'~~> write the array back to worksheet
rng.Resize(UBound(tmpAr), 1).Value = tmpAr
End If
Next j
Next i
End With
End Sub
In Action

How can I use advanced filter if I have the criteria in a cell?

The cell is going to change the value and it is in different locations of the worksheet. The point is that I haven't been able that my macro understand that criteria that is located in the last cell of a column, the second in this case
This is the code that I am using
Public Sub Excel()
Dim T#, crit As Range, Data As Range, ws As Worksheet
Dim r&, fc As Range, lc As Range, fr1 As Range, fr2 As Range
Dim year%
year = 2019
Set fc = ActiveSheet.UsedRange.Item(1)
Set lc = GetMaxCell
Set Data = ActiveSheet.Range(fc, lc)
Set ws = Sheets.Add
With Data
Set fr1 = Data.Worksheet.Range(fc, fc.Offset(, lc.Column))
Set fr2 = ws.Range(ws.Cells(fc.Row, fc.Column), ws.Cells(fc.Row, lc.Column))
With fr2
fr1.Copy
.PasteSpecial xlPasteColumnWidths: .PasteSpecial xlPasteAll
.Item(1).Select
End With
Set crit = .Resize(2, 1).Offset(, lc.Column + 1)
'This is the part that I need to understand
crit = [{"AÑO";<>lRow)}]
.AdvancedFilter xlFilterCopy, crit, fr2
.Worksheet.Delete
End With
End Sub
Public Function GetMaxCell(Optional ByRef rng As Range = Nothing) As Range
'Returns the last cell containing a value, or A1 if Worksheet is empty
Const NONEMPTY As String = "*"
Dim lRow As Range, lCol As Range
If rng Is Nothing Then Set rng = Application.ActiveWorkbook.ActiveSheet.UsedRange
If WorksheetFunction.CountA(rng) = 0 Then
Set GetMaxCell = rng.Parent.Cells(1, 1)
Else
With rng
Set lRow = .Cells.Find(What:=NONEMPTY, LookIn:=xlFormulas, _
After:=.Cells(1, 1), _
SearchDirection:=xlPrevious, _
SearchOrder:=xlByRows)
If Not lRow Is Nothing Then
Set lCol = .Cells.Find(What:=NONEMPTY, LookIn:=xlFormulas, _
After:=.Cells(1, 1), _
SearchDirection:=xlPrevious, _
SearchOrder:=xlByColumns)
Set GetMaxCell = .Parent.Cells(lRow.Row, lCol.Column)
End If
End With
End If
End Function
Thanks for your help

File size of Excel file doesn't change after deleting several worksheets via VBA

I ran a simulation in excel with VBA that gave me back about 200 worksheets and a summary of the simulation data. Now, I recognize that the speed of Excel became slower. Thus, I deletes the bulk of the worksheets that only the worksheet with the summary remains to reduce the file size (which is currently about 140mb). Unfortunately, the file size did not change significantly. How can I solve the problem?
When I run my similar scenario - I am unable to duplicate your issue. How are you deleting the sheets? This is what I use to delete the extra sheets and the file size is modified correctly when I save.
Sub DeleteSheets1()
'This macro will delete all sheets except 'sheet1'
Dim xWs As Worksheet
Application.ScreenUpdating = False
Application.DisplayAlerts = False
For Each xWs In Application.ActiveWorkbook.Worksheets
If xWs.Name <> "Sheet1" Then
xWs.Delete
End If
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Hit Ctrl+Shift+End and confirm the selected area. Is it what you expect or does the range extend far beyond what you expect? Select all columns to the right of what you don't want/need, and delete this range. Select all rows down from the spot you need to preserve, and delete this range. Save your file. Check the size. Is this what you expect to see?
Also, use this VBA script below to recalculate the used range for each sheet.
Sub ExcelDiet()
Dim j As Long
Dim k As Long
Dim LastRow As Long
Dim LastCol As Long
Dim ColFormula As Range
Dim RowFormula As Range
Dim ColValue As Range
Dim RowValue As Range
Dim Shp As Shape
Dim ws As Worksheet
Application.ScreenUpdating = False
Application.DisplayAlerts = False
On Error Resume Next
For Each ws In Worksheets
With ws
'Find the last used cell with a formula and value
'Search by Columns and Rows
On Error Resume Next
Set ColFormula = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious)
Set ColValue = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious)
Set RowFormula = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
Set RowValue = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
On Error GoTo 0
'Determine the last column
If ColFormula Is Nothing Then
LastCol = 0
Else
LastCol = ColFormula.Column
End If
If Not ColValue Is Nothing Then
LastCol = Application.WorksheetFunction.Max(LastCol, ColValue.Column)
End If
'Determine the last row
If RowFormula Is Nothing Then
LastRow = 0
Else
LastRow = RowFormula.Row
End If
If Not RowValue Is Nothing Then
LastRow = Application.WorksheetFunction.Max(LastRow, RowValue.Row)
End If
'Determine if any shapes are beyond the last row and last column
For Each Shp In .Shapes
j = 0
k = 0
On Error Resume Next
j = Shp.TopLeftCell.Row
k = Shp.TopLeftCell.Column
On Error GoTo 0
If j > 0 And k > 0 Then
Do Until .Cells(j, k).Top > Shp.Top + Shp.Height
j = j + 1
Loop
If j > LastRow Then
LastRow = j
End If
Do Until .Cells(j, k).Left > Shp.Left + Shp.Width
k = k + 1
Loop
If k > LastCol Then
LastCol = k
End If
End If
Next
.Range(.Cells(1, LastCol + 1), .Cells(.Rows.Count, .Columns.Count)).EntireColumn.Delete
.Range("A" & LastRow + 1 & ":A" & .Rows.Count).EntireRow.Delete
End With
Next
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub

Shift All Rows To First Common Row

I have 100K Excel file that has many employee info, I want to shift all existence data to the first row for this employee, the picture below will be louder than my words, can a VBA code do this? or there is a trick in excel that I am not aware of
Try following code.
Sub Demo()
Dim ws As Worksheet
Dim cel As Range, rng As Range
Dim lastRow As Long, lastCol As Long, i As Long
Dim fOccur As Long, lOccur As Long, colIndex As Long
Dim dict As Object, c1
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your data range
Set dict = CreateObject("Scripting.Dictionary")
With ws
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row 'last row with data in Column A
lastCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column 'last column with data in Sheet1
Set rng = .Range("A1:A" & lastRow) 'set range in Column A
c1 = .Range("A2:A" & lastRow)
For i = 1 To UBound(c1, 1) 'using dictionary to get uniques values from Column A
dict(c1(i, 1)) = 1
Next i
colIndex = 16 'colIndex+1 is column number where data will be displayed from
For Each k In dict.keys 'loopthrough all unique values in Column A
fOccur = Application.WorksheetFunction.Match(k, rng, 0) 'get row no. of first occurrence
lOccur = Application.WorksheetFunction.CountIf(rng, k) 'get row no. of last occurrence
lOccur = lOccur + fOccur - 1
'copy range from left to right
.Range(.Cells(fOccur, 1 + colIndex), .Cells(lOccur, lastCol + colIndex)).Value = .Range(.Cells(fOccur, 1), .Cells(lOccur, lastCol)).Value
'delete blanks in range at right
.Range(.Cells(fOccur, 1 + colIndex), .Cells(lOccur, lastCol + colIndex)).SpecialCells(xlCellTypeBlanks).Delete Shift:=xlUp 'delte blank rows
Next k
End With
Application.ScreenUpdating = True
End Sub
Try the below. You can amend the below code to match where you want to move the range:
Dim oW As Worksheet: Set oW = ThisWorkbook.Worksheets("Sheet8")
With oW.UsedRange
.Cut .Offset(0, .Columns.Count + 2)
End With

Resources