Create a Index List of all Sheets with their name in ListObject table column - vba

I want to create a index list of all sheets with their names in a table column.
So far I have written the below code but it gives an error on a quoted line.
Dim ws As Worksheet, tbl As ListObject, i As Integer
Set ws = Sheets("Profile Management")
Set tbl = ws.ListObjects("sheets")
With tbl.ListRows
Do While .Count >= 1
.Item(1).Delete
Loop
End With
For i = 1 To Sheets.Count
"tbl.ListColumns(1).DataBodyRange = Sheets(i).Name"
Next I
Where I am going wrong?

The following is much simpler.
Sub GetWorksheetNames()
Dim i As Long
ThisWorkbook.Worksheets("Profile Management").Cells(1, 1).Value = "Worksheet Inventory"
For i = 1 To ThisWorkbook.Worksheets.Count
ThisWorkbook.Worksheets("Profile Management").Cells(i + 1, 1).Value = ThisWorkbook.Worksheets(i).Name
Next i
End Sub

Working with structured (aka ListObject) tables brings some additional concerns to VBA. You cannot write to the .DataBodyRange property that way and the .DataBodyRane is a member of the ListObject, not the ListObject's ListColumns property.
Option Explicit
Sub wqwe()
Dim tbl As ListObject, i As Long, w As Long
With Worksheets("Profile Management")
With .ListObjects("sheets")
'make sure there is at least 1 row in the databodyrange
If .DataBodyRange Is Nothing Then _
.ListRows.Add
'clear the first column
.DataBodyRange.Columns(1).ClearContents
'insert the worksheet names
For w = 1 To Worksheets.Count
'except "Profile Management"
If Worksheets(w).Name <> .Parent.Name Then
i = i + 1
'expand the table for new worksheets
.DataBodyRange.Cells(i, 1) = Worksheets(w).Name
'optionally insert a hyperlink to each worksheet's A1
.Parent.Hyperlinks.Add Anchor:=.DataBodyRange.Cells(i, 1), _
Address:=vbNullString, SubAddress:=Worksheets(w).Name & "!A1", _
TextToDisplay:=Worksheets(w).Name, ScreenTip:="click to go there"
End If
Next w
'reshape the table if there are blank rows
Do While i < .ListRows.Count
.ListRows(i + 1).Delete
Loop
End With
End With
End Sub
As noted in comments above, I've added the option to hyperlink to each worksheet directly from its listing in the table. If you choose this route, you do not have to put the name into the table cell first.

Related

Return unique values which part match another criteria (Excel VBA)

I have a sheet of data on sheet1 which contains duplicates. On sheet 2 I have extracted a list of unique values with the Advanced Filter:
lr = Sheets("Data").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Data").Range("F2:F" & lr).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=NewSh2.Range("B4"), Unique:=True
This works fine however I would like it to only return values which partly match another cell (This is a drop down box in K2 -
eg, if AA is selected in the box only values that start with AA are returned.)
I'm new to VBA and I'm not sure of the best way to go about doing this - (I had considered just deleting values which didn't match, which would create blanks, then to remove the blank rows - however I am concerned this would be a bit overkill and process heavy?) - is there a neater way to achieve this?
Thanks in advance!
Edit: Detail added.
So the dropdown in K2 has AA, BB, CC
The list of unique values looks something like:
AA01
AA02
AA03
BB02
BB03
AA05
CC01
CC02
CC03
CC05
BB04
When the drop down has selected AA I would like the list to only return:
AA01
AA02
AA03
AA05
Here's one way, using a dictionary:
Sub tgr()
Dim wb As Workbook
Dim wsData As Worksheet
Dim NewSh2 As Worksheet
Dim aFullList As Variant
Dim hUnqMatches As Object
Dim sMatch As String
Dim i As Long
Set wb = ActiveWorkbook
Set wsData = wb.Sheets("Data")
With wsData.Range("F2:F" & wsData.Cells(wsData.Rows.Count, "F").End(xlUp).Row)
If .Row < 2 Then Exit Sub 'No data
If .Cells.Count = 1 Then
ReDim aFullList(1 To 1, 1 To 1)
aFullList(1, 1) = .Value
Else
aFullList = .Value
End If
End With
sMatch = wsData.Range("K2").Value
Set hUnqMatches = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(aFullList, 1)
If Left(aFullList(i, 1), Len(sMatch)) = sMatch Then
If Not hUnqMatches.Exists(aFullList(i, 1)) Then hUnqMatches.Add aFullList(i, 1), aFullList(i, 1)
End If
Next i
If hUnqMatches.Count > 0 Then
On Error Resume Next
Set NewSh2 = wb.Sheets("Sheet2")
On Error GoTo 0
If NewSh2 Is Nothing Then
Set NewSh2 = wb.Sheets.Add(After:=wb.Sheets(wb.Sheets.Count))
NewSh2.Name = "Sheet2"
End If
NewSh2.Range("B4").Resize(hUnqMatches.Count).Value = Application.Transpose(hUnqMatches.Keys)
End If
End Sub
You can just add your cell K2 from sheet Data as a criteria to your autofilter. Simply add the following piece to your code:
Criteria1:= Sheets("Data").Range("K2").value
This combines with your code to:
lr = Sheets("Data").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Data").Range("F2:F" & lr).AdvancedFilter Action:=xlFilterCopy, Criteria1:= Sheets("Data").Range("K2").value CopyToRange:=NewSh2.Range("B4"), Unique:=True
For some background reading see: https://www.thespreadsheetguru.com/blog/2015/2/16/advanced-filters-with-vba-to-automate-filtering-on-and-out-specific-values

Excel VBA: Cannot delete multi columns by using if statement and for loop

Background & purpose.
I want to delete multi columns in my "sheet1" based on specific column header.
For example: if column name = "First Name", "Surname", "DoB", "Gender", "Year"
Then delete entire of column.
And I use vba to do that.
Button "Execute" in "Execution" sheet
Data is saved in "Sheet1".
Click on "Execute" button, macro will be executed and edit data in "sheet1".
Here is my code
Sub SPO_EditDocument_BUttonClick()
'Declare constant variable
Dim CellValue As String
'Edit SPO Documents.
'Consider worksheet"SPO"
With Worksheets("Sheet1")
'1. Loop and delete unnecessary columns
LastCol = Sheets("Sheet1").Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
For i = 1 To LastCol
On Error Resume Next
CellValue = Sheets("Sheet1").Cells(1, i) 'Get Column header
If CellValue = "Firstname" Then
Sheets("Sheet1").Columns(i).EntireColumn.Delete
ElseIf CellValue = "Surname" Then
Sheets("Sheet1").Columns(i).EntireColumn.Delete
ElseIf CellValue = "DoB" Then
Sheets("Sheet1").Columns(i).EntireColumn.Delete
ElseIf CellValue = "Gender" Then
Sheets("Sheet1").Columns(i).EntireColumn.Delete
ElseIf CellValue = "Year" Then
Sheets("Sheet1").Columns(i).EntireColumn.Delete
End If
Next i
End With 'End to process Worksheets
Application.ScreenUpdating = True
End Sub
Sample data table in sheet1
Problem: When I execute my macro by clicking on button "Execute", just only 1 column is deleted at that time. Next click → next column is deleted (just can delete 1 column by one click).
Question: Why I can not delete all columns by 1 click with my code?
What is the problem in this case?
Any help would be highly appreciated.
Thank you so much for your attentions.
Insert new columns before found cells
Loop through the columns in reverse order. To do that, change your line:
For i = 1 To LastCol
to:
For i = LastCol to 1 Step -1
another fast way
Option Explicit
Sub SPO_EditDocument_BUttonClick()
Dim colNames As Variant, colName As Variant, actualColNames As Variant, foundColName As Variant
Dim colsToDelete As String
colNames = Array("Firstname", "Surname", "DoB", "Gender", "Year") ' build a list of column headers to be deleted
With Worksheets("Sheet1") ' reference data sheet
actualColNames = Application.Index(.Range("A1", .Cells(1, .Columns.count).End(xlToLeft)).Value, 1, 0) ' collect referenced sheet actual column headers
For Each colName In colNames ' loop through your column headers to be deleted list
foundColName = Application.Match(colName, actualColNames, 0) ' try finding current header to be deleted in actual headers list
If Not IsError(foundColName) Then colsToDelete = colsToDelete & Columns(foundColName).Address(False, False) & " " 'if found update columns to be deleted index list
Next
If colsToDelete <> "" Then .Range(Replace(Trim(colsToDelete), " ", ",")).EntireColumn.Delete ' if columns to be deleted index list not empty, then delete thsoe columns
End With
A faster way to delete multiple columns will be to use a Range object, in my code it's DelRng, which merges all Columns that pass your criteria to be deleted - using the Union function. So at the end, you just use the Delete function which takes a long time to process just once.
You can replace your multiple ElseIfs with a Select Case.
All objects which are nested in your With Worksheets("Sheet1") don't need to have With Worksheets("Sheet1") again, just use the . as a prefix to all Cells and Range objects.
You can have the much shorter, and faster code version like the code below:
Modified Code
Sub SPO_EditDocument_BUttonClick()
'Declare constant variable
Dim CellValue As String
Dim DelRng As Range
'Edit SPO Documents.
'Consider worksheet"SPO"
Application.ScreenUpdating = False
With Worksheets("Sheet1")
'1. Loop and delete unnecessary columns
LastCol = .Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
For i = 1 To LastCol
CellValue = .Cells(1, i) 'Get Column header
Select Case CellValue
Case "Firstname", "Surname", "DoB", "Gender", "Year"
If Not DelRng Is Nothing Then
Set DelRng = Application.Union(DelRng, .Columns(i))
Else
Set DelRng = .Columns(i)
End If
Case Else
' Do nothing
End Select
Next i
End With 'End to process Worksheets
' if there's at least 1 column in DelRng >> delete the entire range (all columns) in 1-shot
If Not DelRng Is nothign Then DelRng.Delete
Application.ScreenUpdating = True
End Sub

Excel Macro Copy Range Paste offset based on cell value

I have two sheets "Data" - which has raw data and "Report" - as Report form .
Report sheet first 5 rows has info.
Data Sheet there 6 columns of Data available (SlNo Name Desig Place gender Category)
Report sheet has first 5 columns only (SlNo Name Desig Place gender)
Report sheet range C5 is dropdown box (List from Category column of Data sheet).
So based on this C5 value get details from Data sheet and paste in Report sheet.
I tried the following code but it pastes the whole row when I want to paste only Name,Desig,Place,gender details in offset and loop...
Sub ViewBtn()
Dim SCHL As String
Dim x As Long
x = 2
Do While Cells(x, 1) <> ""
Sheets("Report").Range(Cells(x, 1).Address, Cells(x, 5).Address).ClearContents
x = x + 1
Loop
Dim id As String
id = ActiveSheet.Range("C5").Value
x = 2
Sheets("Data").Select
Category = id
Do While Cells(x, 1) <> ""
If Cells(x, 4) = Category Then
Worksheets("Data").Rows(x).Copy
Worksheets("Report").Activate
erow = Sheets("Report").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
ActiveSheet.Paste Destination:=Worksheets("Report").Rows(erow)
End If
Worksheets("Data").Activate
x = x + 1
Loop
Application.CutCopyMode = False
Worksheets("Report").Activate
End Sub
Here is some sample code to do what I think you are asking for. It is not necessarily the shortest or cleverest way to do it, but everything is done step by step so I hope it is clear enough to follow easily.
Option Explicit
Private Sub viewBtn_Click()
'// Set references to worksheets
Dim wsReport As Worksheet: Set wsReport = Sheets("Report")
Dim wsData As Worksheet: Set wsData = Sheets("Data")
'// Get the category to be reported
Dim sCategory As String
sCategory = wsReport.Range("C5")
'// Reference first line of the report, in row 8
Dim rFirstReportLine As Range
Set rFirstReportLine = wsReport.Range("A8:E8")
'// Reference the line of the report to be written
Dim rReportLine As Range
Set rReportLine = rFirstReportLine
'// Clear the old report area
Do While rReportLine.Cells(1, 1) <> ""
rReportLine.Clear
Set rReportLine = rReportLine.Offset(1, 0)
Loop
'// Reset to first line of the report
Set rReportLine = rFirstReportLine
'// Find the first cell, if any, that matches the category
Dim rMatch As Range
Set rMatch = wsData.Range("F:F").Find(sCategory, , , xlWhole)
'// Get reference to top data row of data sheet, just the cols to be copied
Dim rDataRow As Range: Set rDataRow = wsData.Range("A1:E1")
'// check for at least one match
If Not rMatch Is Nothing Then
'// Save the address of the first match for checking end of loop with FindNext
Dim sFirstMatchAddress As String: sFirstMatchAddress = rMatch.Address
Do
'// 1) .. copy data row to the report line
rDataRow.Offset(rMatch.Row - 1).Copy rReportLine
'// 2) .. move the report line down
Set rReportLine = rReportLine.Offset(1, 0)
'// 3) .. find the next match on category
Set rMatch = wsData.Range("F:F").FindNext(rMatch)
'// 4) .. exit when we have looped around
Loop Until rMatch.Address = sFirstMatchAddress
End If
'// Display the number of entries found at the end of the report
With rReportLine
Dim nEntryCount As Integer: nEntryCount = .Row - rFirstReportLine.Row
.Cells(1, 1) = nEntryCount & IIf(nEntryCount = 1, " Entry", " Entries")
.Font.Italic = True
.Font.Color = vbBlue
End With
'// Make sure the report sheet is displayed
wsReport.Activate
End Sub
With this data
Get this result

Search for multiple phrase; copy to single sheet across multiple sheets

I am using Microsoft Excel to keep track of tasks. I use a different "sheet" for each job. The structure is with regards to columns and data. I have been trying to create a VBA script that would accomplish the following:
Search sheets 1 - X for a value of "Open" or "Past Due" in a row
Copy all rows with those values into a single sheet (such as a ledger) starting at row 3 (so I can add the headers of the template)
Add a column A with the sheet name so that I know what job it came from.
Run this to my hearts obsessive compulsive behavior pleasure to update with new items
I have been using the following posts to help guide me:
Search a specific word and copy line to another Sheet <- which was helpful but not quite right...
Copying rows to another worksheet based on a search on a grid of tags <-- also helpful, but limited to the activesheet and not looping correctly with my modifications...
The last two evenings have been fun, but I feel like I may be making this harder than necessary.
I was able to create a VBA script (edited from another post here) to sweep through all the worksheets, but it was designed to copy all data in a set of columns. I tested that and it worked. I then merged the code base I was using to identify "Open" or "Past Due" in column C (that worked for only the activesheet) into the code. I marked up my edits to share here. At this point it is not functioning, and I have walked myself dizzy. Any tips on where I fubar-ed the code would be appreciated. My code base I working from is:
Sub SweepSheetsCopyAll()
Application.ScreenUpdating = False
'following variables for worksheet loop
Dim W As Worksheet, r As Single, i As Single
'added code below for finding the fixed values on the sheet
Dim lastLine As Long
Dim findWhat As String
Dim findWhat1 As String
Dim findWhat2 As String
Dim toCopy As Boolean
Dim cell As Range
Dim h As Long 'h replaced i variable from other code
Dim j As Long
'replace original findWhat value with new fixed value
findWhat = "Open"
'findWhat2 = "Past Due"
i = 4
For Each W In ThisWorkbook.Worksheets
If W.Name <> "Summary" Then
lastLine = ActiveSheet.UsedRange.Rows.Count 'Need to figure out way to loop all rows in a sheet to find last line
For r = 4 To lastLine 'formerly was "To W.Cells(Rows.Count, 1).End(xlUp).Row"
'insert below row match search copy function
For Each cell In Range("B1:L1").Offset(r - 1, 0)
If InStr(cell.Text, findWhat) <> 0 Then
toCopy = True
End If
Next
If toCopy = True Then
' original code Rows(r).Copy Destination:=Sheets(2).Rows(j)
Range(W.Cells(r, 1), W.Cells(r, 12)).Copy _
ThisWorkbook.Worksheets("Summary").Cells(i, 1)
j = j + 1
End If
toCopy = False
'Next
'end above row match search function
'below original code that copied everything from whole worksheet
' If W.Cells(r, 1) > 0 Then
' Range(W.Cells(r, 1), W.Cells(r, 12)).Copy _
' ThisWorkbook.Worksheets("Summary").Cells(i, 1)
' i = i + 1
' End If
Next r
End If
Next W
End Sub
The working code base to sweep through all the sheets was:
Sub GetParts()
Application.ScreenUpdating = False
Dim W As Worksheet, r As Single, i As Single
i = 4
For Each W In ThisWorkbook.Worksheets
If W.Name <> "Summary" Then
For r = 4 To W.Cells(Rows.Count, 1).End(xlUp).Row
If W.Cells(r, 1) > 0 Then
Range(W.Cells(r, 1), W.Cells(r, 3)).Copy _
ThisWorkbook.Worksheets("Summary").Cells(i, 1)
i = i + 1
End If
Next r
End If
Next W
End Sub
And the copy the matched data from the Activesheet is as follows:
Sub customcopy()
Application.ScreenUpdating = False
Dim lastLine As Long
Dim findWhat As String
Dim findWhat1 As String
Dim findWhat2 As String
Dim toCopy As Boolean
Dim cell As Range
Dim i As Long
Dim j As Long
'replace original findWhat value with new fixed value
findWhat = "Open"
'findWhat2 = "Past Due"
lastLine = ActiveSheet.UsedRange.Rows.Count 'Need to figure out way to loop through all sheets here
'below code does nice job finding all findWhat and copying over to spreadsheet2
j = 1
For i = 1 To lastLine
For Each cell In Range("B1:L1").Offset(i - 1, 0)
If InStr(cell.Text, findWhat) <> 0 Then
toCopy = True
End If
Next
If toCopy = True Then
Rows(i).Copy Destination:=Sheets(2).Rows(j)
j = j + 1
End If
toCopy = False
Next
i = MsgBox(((j - 1) & " row(s) were copied!"), vbOKOnly, "Result")
Application.ScreenUpdating = True
End Sub
You should look into this Vba macro to copy row from table if value in table meets condition
In your case, you would need to create a loop, using this advanced filter to copy the data to your target range or array.
If you need further advice, please post your code, and where you are stuck with it.

Search for two values and copy everything in between in a loop

I have a worksheet which has many terms in Column A.I want to search for two terms for example
term A and term B and copy all rows between the two terms and paste it into a new sheet.These two terms may repeat in the column. The problem which I am basically facing the following problem : whenever I run my code it also copies rows between term B and term A which is unnecessary. Following is the code i am using for two terms term A and term B.
For example my column A is
Institute
Event
Job
Computer
Laptop
Figures
Event
figures
format
computer
and many more terms
I want to copy all the rows between term A: Event and term B: Laptop and paste it into a new sheet. What my code is doing is it is copying the rows between all combinations of Event and computer. Even the rows between computer and event are copied(in this case Figure and laptop).
Sub OpenHTMLpage_SearchIt()
Dim Cell As Range, Keyword$, N%, SearchAgain As VbMsgBoxResult
Dim ass As Variant
Dim Cellev As Range, prakash$, P%, SearchAgaina As VbMsgBoxResult
Dim asa As Variant
StartSearch:
N = 1
Keyword = "Event"
If Keyword = Empty Then GoTo StartSearch
For Each Cell In Range("A1:A500")
If Cell Like "*" & Keyword & "*" Then
ass = Cell.Address
P = 1
prakash = "Computer"
If prakash = Empty Then GoTo StartSearch
For Each Cellev In Range("A1:A500")
If Cellev Like "*" & prakash & "*" Then
asa = Cellev.Address
Range(asa, ass).Select
Selection.Copy
Sheets.Add After:=Sheets(Sheets.Count)
Range("B13").Select
ActiveSheet.Paste
Worksheets("sheet1").Select
P = P + 1
End If
Next Cellev
N = N + 1
End If
Next Cell
End Sub
Edit: code formatting.
The following is the code which is working for me.This copies everything in between Event and laptop and pastes it into a new sheet. Then again it searches for a second time and this time the search will start from the next row to the first search.I hope I am clear with this.
Sub Star123()
Dim rownum As Long
Dim colnum As Long
Dim startrow As Long
Dim endrow As Long
Dim lastrow As Long
rownum = 1
colnum = 1
lastrow = Worksheets("Startsheet").Range("A65536").End(xlUp).Row
With ActiveWorkbook.Worksheets("StartSheet").Range("a1:a" & lastrow)
For rownum = 1 To lastrow
Do
If .Cells(rownum, 1).Value = "Event" Then
startrow = rownum
End If
rownum = rownum + 1
If (rownum > lastrow) Then Exit For
Loop Until .Cells(rownum, 1).Value = "Laptop"
endrow = rownum
rownum = rownum + 1
Worksheets("StartSheet").Range(startrow & ":" & endrow).Copy
Sheets("Result").Select
Range("A1").Select
ActiveSheet.Paste
Next rownum
End With
End Sub
Try this:
Sub DoEeeeeet(sheetName, termA, termB)
Dim foundA As Range, _
foundB As Range
Dim newSht As Worksheet
With Sheets(sheetName).Columns(1)
Set foundA = .Find(termA)
If Not foundA Is Nothing Then
Set foundB = .Find(termB, after:=foundA, searchdirection:=xlPrevious)
End If
End With
If foundA Is Nothing Or foundB Is Nothing Then
MsgBox "Couldn't find " & IIf(foundA Is Nothing, termA, termB)
Else
Range(foundA, foundB).Copy
Set newSht = Sheets.Add
newSht.Range("B13").PasteSpecial
End If
End Sub
You can call it as follows:
DoEeeeeet "Sheet1","Event","Laptop"
It'll find the first instance of "Event" and the last instance of "Laptop" on the sheet named "Sheet1" and copy all of that data to B13 and subsequent cells in a new sheet.
Is that what you want? Or do you want each of the subranges beginning with "Event" and ending with "Laptop"?