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

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

Related

Need to find an occurrence of a value in any beyond worksheet one and return the value of A:1 for each worksheet

I need to search all worksheets for the values in Worksheet one column A. The behavior should be similar to a CTRL-F find all selection. In A:1 of every worksheet is a name and if the value from column A is in that worksheet then A:1 will be returned. I do not need VLookup or HLookup. It might be doable with index and search combo, but I am not finding a good way to do that. I know I need an array search of some sort since I need to search everywhere. I have a solution that does not scale and is sloppy on the return. This is the formula I am currently using.
Column A is where the search values are pasted. Columns B-Z or however far is needed get the formula pasted in the first 200 rows which is the limit of the allowed search terms.
{=IF(OR($A2<>""),IF(OR($A2=Sheet26!$A$1:SZ$25000),Sheet26!A$1,"Not Found"),"")}
That is the formula for column Z and the sheet numbers are changed for each column that has a sheet. What I need to adjust this to is only having the formula in column B and it returning a concatenated value of all the names it found. There are lots of questions dealing with just one value or one range like this EXCEL: Need to find a value in a range of cells from another worksheet and return value from adjacent cell but nothing that actually answers what I need.
Currently the result I get is something like this.
A B C D E ...
Star Bob Not Found Ann Not Found
Light Bob Jill Not Found Not Found
378 Not Found Jill Not Found Not Found
What I would like to have is this
A B
Star Bob, Ann
Light Bob, Jill
378 Jill
How can I modify my formula to accomplish that?
Thanks
If you get tired of the formula approach, here is a VBA approach that should do what you describe.
It looks at column 1 on sheet1 to get a list of words to search for
read that list into a vba array (for speed)
for each item in the list, search each worksheet to see if the item exists
I added each item to a Dictionary, and then concatenated the results with commas, but you could also construct a string on the fly, to store in the second "column" of the array
After all is done, we write the results back to the worksheet.
It should be able to handle any reasonable number of worksheets and search terms
If necessary, you can limit the range to search on each worksheet; exclude certain worksheets from being searched; look at partial matches in a cell; select a case-sensitive search; etc.
If there are blank entries between the first and last search terms, I have excluded the search.
Option Explicit
Sub FindAllColA()
Dim WB As Workbook, WS As Worksheet
Dim WS1 As Worksheet
Dim D As Object
Dim V
Dim R As Range
Dim FirstRow As Long, LastRow As Long
Dim I As Long
Set D = CreateObject("scripting.dictionary")
Set WB = ThisWorkbook
Set WS1 = WB.Worksheets("Sheet1")
With WS1
If .Cells(1, 1) <> "" Then
FirstRow = 1
Else
FirstRow = .Cells(1, 1).End(xlDown).Row
End If
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
'V will hold both search terms and the results
V = .Range(.Cells(FirstRow, 1), .Cells(LastRow, 1)).Resize(columnsize:=2)
End With
For I = 1 To UBound(V)
If Not V(I, 1) = "" Then
D.RemoveAll
For Each WS In WB.Worksheets
If Not WS.Name = WS1.Name Then
With WS
If Not .Cells.Find(what:=V(I, 1), LookIn:=xlValues, _
lookat:=xlWhole, MatchCase:=False) Is Nothing Then
D.Add .Cells(1, 1).Text, .Cells(1, 1).Text
End If
End With
End If
Next WS
V(I, 2) = Join(D.Keys, ",")
Else
V(I, 2) = ""
End If
Next I
With WS1
Set R = .Range(.Cells(FirstRow, 1), .Cells(LastRow, 2))
R.EntireColumn.Clear
R = V
R.EntireColumn.AutoFit
End With
End Sub
Another way, would be an UDF which can be used in a wider range without any changes like:
Public Function ValString(search_term As String, cell_string As Variant, ParamArray ignored_sheets()) As Variant
Dim x As Variant
If TypeOf cell_string Is Range Then cell_string = cell_string.Address
If Not TypeOf Evaluate(cell_string) Is Range Then
ValString = CVErr(2023)
Exit Function
ElseIf Range(cell_string).Cells.Count > 1 Then
ValString = CVErr(2023)
Exit Function
End If
If IsMissing(ignored_sheets) Then
ignored_sheets = Array(Application.Caller.Parent.Name)
Else
For x = 0 To UBound(ignored_sheets)
If TypeOf ignored_sheets(x) Is Range Then
ignored_sheets(x) = ignored_sheets(x).Parent.Name
ElseIf TypeName(ignored_sheets(x)) = "String" Or IsNumeric(ignored_sheets(x)) Then
ignored_sheets(x) = Format(ignored_sheets(x), "#")
Else
ignored_sheets(x) = ""
End If
Next
End If
For Each x In ThisWorkbook.Worksheets
If IsError(Application.Match(x.Name, Array(ignored_sheets)(0), 0)) Then
If Not x.Cells.Find(search_term, , -4163, 1, , , True) Is Nothing Then
ValString = ValString & ", " & x.Range(cell_string).Value2
End If
End If
Next
If Len(ValString) Then
ValString = Mid(ValString, 3)
Else
ValString = CVErr(2042)
End If
End Function
Put the code in a Module and you can use it like a normal formula in your sheet.
Example:
=ValString(A1,"A1")
Or for your case:
=IFERROR(ValString(A1,"A1"),"Not Found")
Use: ValString([search_term],[cell_string],{[ignored_sheet1],[ignored_sheet2],...})
[search_term]: the string to look for
[cell_string]: the address of a cell as ref or string which you want to output if found
[ignored_sheets]: (optional) the sheet names as strings or a ref to them you want to ignore
If [ignored_sheets] is omitted the sheet you have the formula in will be ignored. To include all sheets in the workbook simply set it to ""
If nothing was found it will return #N/A! (which is good as you can catch this to set whatever output you want without changing the code)
If [cell_string] is not an address-string and/or goes for multiple cells, it will return #REF!
[ignored_sheets] is used as a list like =ValString(A1,"A1",Sheet1!A1,Sheet5!A1) or =ValString(A1,"A1","Sheet3","Sheet4","Sheet7","MyWhateverSheetName"). If used in the ref-way, you can rename the sheets and it will also in the formula. This is good if there is a summary sheet you do not want to check. But keep in mind: if used, the sheet with the formula itself, also needs to be included!
If you still have any questions, just ask ;)
try this UDF
Function findKeywords(findMe As String) As String
findKeywords = ""
Dim sheetToSkip As String
sheetToSkip = "Sheet1"
Dim sht As Worksheet
For Each sht In ActiveWorkbook.Sheets
If sht.Name <> sheetToSkip And Len(findMe) > 0 Then ' do not look for blank cells
' note: LookAt:=xlWhole ... whole word LookAt:=xlPart ... partial
Dim aaa As Range
Set aaa = sht.Cells.Find( _
What:=findMe, _
After:=sht.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not aaa Is Nothing Then
If Len(findKeywords) = 0 Then
findKeywords = sht.Range("a1")
Else
findKeywords = findKeywords & ", " & sht.Range("a1")
End If
End If
End If
Next sht
' If Len(findKeywords) = 0 Then findKeywords = "Not Found" ' uncomment to return "Not Found" if desired
' Debug.Print findKeywords
End Function

Making an Associative Table of Unique Identifiers

I'm trying to create an associative table on a sheet that is pulling in data from a different sheet. By associative I mean, if the data is changed in the source data sheet, it would be reflected on the new sheet. I also want to only have the new sheet's table to be contingent on having a certain unique value. In my case, I want to pull up information related to a part number. The original source data will have many rows that contain the same part number, but I only care to display one of them.
This is what I have so far:
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Dim ref() As Variant
Dim row As Integer
row = 92
Worksheets("Part Tracking Scorecard").Activate
While Cells(row, 6).Value:
If IsInArray(Cells(row, 6).Value, ref) Then
row = row + 1
ElseIf Not IsInArray(Cells(row, 6).Value, ref) Then
ReDim Preserve ref(1 To UBound(ref) + 1) As Variant
ref(UBound(ref)) = Cells(row, 6).Value
Worksheets("Unique Parts").Activate
?????
row = row + 1
To satisfy my condition to only showcase the unique part numbers, I initialized an empty array called "ref". Then, as I iterate through the source sheet, I would check if the part number was in ref with the function "IsInArray". If it was in it, it would move onto the next row, if it wasn't add the part number into the empty array and move to the next row.
The portion with the "????" is where I'm having most of my issue trying to figure out. That part is supposed to be where I make the new table with the date from the unique part number. The very simple and tedious thing I could do is make some loop to run through the columns of the rows and put in a vlookup function. I was wondering if there may be a more robust or more elegant way in doing this.
You've had the right reflex tyring to define an array to stock your values. Here are a few tips of how I would get around to doing it (not perfect, but it should help you out):
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Dim Source as Worksheets
Set Source = Worksheets("Part Tracking Scoreboard")
Dim ref1(), ref2() As Variant
Dim row, index, index2 As Integer
row = 92
ref1 = Source.Range(Worksheets(Source.Cells(row,1), Source.Cells(lastrow, last column))
'Start by placing your ENTIRE source sheet in ref1, if your source sheet is big, this will help you win A LOT of time during the looping phase. Notice how I start from row 92 seeing as this is where you started your loop
'lastrow and lastcolumn represent the position of the last cell in your source file
For index = row to lastrow
If Not IsInArray(ref1(row, 6).Value, ref2) Then
ref2(index) = ref1(index) 'copy the entire row from source to ref2
Next index
Dim NewFile as Worksheet
Set Newfile = Sheets("NewSheetName")
Dim ref2dimension_x, ref2dimension_y as Integer 'find dimensions of ref2 array
ref2dimension_x= UBound(ref2, 1) - LBound(ref2, 1) + 1
ref2dimension_y = UBound(ref2, 2) - LBound(ref2, 2) + 1
For index = 2 to ref2dimension_x 'go through entire new sheet and set values
For index2 = 1 to ref2dimension_y
NewFile.Cells(index, index2).Value = ref2(index - 1, index2)
Next index2
Next index
ref1() = nothing
ref2() = nothing 'free up the space occupied by these arrays
I was not sure about what you were trying to do exactly during the else loop. If you intention is to copy the entire row, this should work. If you want to copy only specific data from the source sheet, you will need to find the indexes of the corresponding columns (hardcode them if they are not going to budge, or use a loop to find them through string comparison otherwise).
This solution combines some macros that I use frequently (so even if you don't use them now, they might be helpful in the future). It won't work if the data in the unique table needs to be "live", but if it'd be sufficient for it to be updated whenever the workbook is opened/closed (or on demand), this is a lot less complicated than the array version.
Basically you just:
Copy the main/unduplicated table to a new sheet
Remove duplicates by part number
Remove unnecessary columns from unduplicated table (if applicable)
I'm assuming that your source data is in a formal Excel Table (ListObject). Just swap out "PartTable" for whatever your actual table is called.
Sub makeUniqueTable()
Application.ScreenUpdating = False
Dim MainWS As Worksheet
Set MainWS = ThisWorkbook.Sheets("Part Tracking Scorecard")
Dim UniqueWS As Worksheet
Set UniqueWS = ThisWorkbook.Sheets("Unique Parts")
UniqueWS.Cells.Clear
Call cloneTable(MainWS.ListObjects("PartTable"), "UniquePartTable", UniqueWS)
Dim UniquePartTable As ListObject
Set UniquePartTable = UniqueWS.ListObjects("UniquePartTable")
Call removeDuplicates(UniquePartTable, "Part Number")
'Optional: remove unnecessary columns by listing columns to be deleted...
'Call deleteColumns(UniquePartTable, Array("Unnecessary Column 1", "Unnecessary Column 2"))
'...or kept:
'Call deleteColumns(UniquePartTable, Array("Part Number", "Manufacturer", "Product Description"), True)
Application.ScreenUpdating = True
End Sub
Sub cloneTable(tbl As ListObject, newName As String, Optional newWS As Worksheet = Nothing)
'Copies a table (tbl) to a new worksheet (newWS) and gives it a name (newName)
'If there is any data in newWS, the new table will be added to the right of the used range
'If newWS is omitted, new table will be added to same worksheet as original table
Dim ws As Worksheet
Dim lastColumn As Long
Dim newRng As Range
Dim newTbl As ListObject
If newWS Is Nothing Then
Set ws = tbl.Parent
lastColumn = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set newRng = ws.Range(ws.Cells(1, lastColumn + 2), ws.Cells(1 + tbl.ListRows.Count, lastColumn + tbl.ListColumns.Count + 1))
Else
Set ws = newWS
If ws.ListObjects.Count > 0 Then
lastColumn = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set newRng = ws.Range(ws.Cells(1, lastColumn + 2), ws.Cells(1 + tbl.ListRows.Count, lastColumn + tbl.ListColumns.Count + 1))
Else
Set newRng = ws.Range(ws.Cells(1, 1), ws.Cells(1 + tbl.ListRows.Count, tbl.ListColumns.Count))
End If
End If
tbl.Range.Copy
newRng.PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
Set newTbl = ws.ListObjects.Add(xlSrcRange, newRng, , xlYes)
newTbl.Name = newName
End Sub
Sub removeDuplicates(tbl As ListObject, Optional colName As Variant = "")
'Removes duplicates from a table (tbl) based on column header names (colName()) provided by user
'If no column names are provided, duplicates will be removed based on all columns in table
Dim i As Long
Dim j As Long
If Not IsArray(colName) Then
If colName = "" Then
ReDim colNumArr(0 To tbl.ListColumns.Count - 1) As Variant
For i = 0 To tbl.ListColumns.Count - 1
colNumArr(i) = tbl.ListColumns(i + 1).Range.Column
Next
Else
ReDim colNumArr(0 To 0) As Variant
colNumArr(0) = tbl.ListColumns(colName).Range.Column
End If
Else
ReDim colNumArr(0 To UBound(colName) - LBound(colName)) As Variant
j = 0
For i = LBound(colName) To UBound(colName)
colNumArr(j) = tbl.ListColumns(colName(i)).Range.Column
j = j + 1
Next
End If
tbl.Range.removeDuplicates Columns:=(colNumArr), Header:=xlYes
End Sub
Sub deleteColumns(tbl As ListObject, ByVal colName As Variant, Optional invert As Boolean = False, Optional sheetCol As Boolean = True)
'Deletes column(s) from sheet based on header names (colName) from a table (tbl)
'Will result in error if provided column contains multiple tables
'colName can be a String or an array of Strings
'Inverted mode deletes all columns *except* those in colName
Dim i As Long
Dim j As Long
Dim x As Boolean
If Not IsArray(colName) Then
tempStr = colName
ReDim colName(1 To 1) As String
colName(1) = tempStr
End If
If invert = False Then
For i = LBound(colName) To UBound(colName)
If sheetCol = True Then
tbl.Parent.Columns(tbl.ListColumns(colName(i)).Range.Column).Delete
Else
tbl.ListColumns(colName(i)).Delete
End If
Next
Else
For i = tbl.ListColumns.Count To 1 Step -1
x = False
For j = LBound(colName) To UBound(colName)
If tbl.HeaderRowRange(i).Value = colName(j) Then
x = True
Exit For
End If
Next
If x = False Then
If sheetCol = True Then
tbl.Parent.Columns(tbl.ListColumns(i).Range.Column).Delete
Else
tbl.ListColumns(i).Delete
End If
End If
Next
End If
End Sub

Extracting values from listbox

I created a UserForm in VBA with 2 ListBoxes.
What I want to do is extract values from the right ListBox (and keep them in temp) and delete every row in another sheet which contains these names.
Writing a code to delete rows is not an issues. I have no clue how to use these selected items in another VBA module. Any ideas?
You can save them in a Collection, and then use that Collection as argument in other Procedure.
Actually I would like my code to look more like this:
For Each c In Range
If c.Value = [any of values from list box] Then
c.EntireRow.Delete
End If
Next c
Update to process rows in reverse (since delete will shift rows up). See comments at top of code.
Option Explicit
Sub cmdDelete_Click()
' This subroutine will allow a user to delete selected rows from an Excel sheet.
' In a multi-select listbox on a user form, select the items that you want to delete.
' Click the 'Delete' button on the form and the following will occur:
' a. Each selected item will be delimited and concatenated into one string.
' (The reason for doing that is to avoid spinning thru each listbox item for
' every row)
' b. Each row in the selected range will have it's value checked within the string.
' c. If found. the row will be deleted.
'
' Notes: - Need to loop thru the rows from bottom upwards to top because
' as each row is deleted, it will shift remainder upwards.
' - You don't really need the delimiters if values can never be confused
Dim sList As String
Dim lItem As Long
Dim r As Range
Dim ws As Worksheet
Dim lFirstRow As Long
Dim lLastRow As Long
Dim lRow As Long
For lItem = 0 To lstCountries.ListCount - 1
If lstCountries.Selected(lItem) = True Then
sList = sList & "<" & Me.lstCountries.column(0, lItem) & ">" ' Adjust to the column YOU want (relative to zero)
End If
Next
Debug.Print "Full List to Delete: " & sList
Set ws = ThisWorkbook.Sheets("Sheet1") ' Change to YOUR worksheet name
' Find the last used row
lLastRow = Cells.Find(What:="*", after:=Range("A1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lFirstRow = 2 ' Set to YOUR first used row
' Spin thru all rows in the range of rows
' Go in reverse order since the Delete will shift the rows UP
For lRow = lLastRow To lFirstRow Step -1
' See if row value exists in the selections made in the listbox
If InStr(1, sList, "<" & ws.Cells(lRow, 1) & ">") > 0 Then
ws.Rows(lRow).Delete ' Delete row if a match is found
End If
Next lRow
End Sub
This code solved my issue
Private Sub delete_button_Click()
On Error Resume Next
Dim custom_range(1 To 5) As Range
Set custom_range(1) = ActiveWorkbook.Sheets("Countries").Columns(5).Cells
Set custom_range(2) = ActiveWorkbook.Sheets("Operations").Columns(2).Cells
Set custom_range(3) = ActiveWorkbook.Sheets("Costs").Columns(2).Cells
Set custom_range(4) = ActiveWorkbook.Sheets("Revenue").Columns(2).Cells
Set custom_range(5) = ActiveWorkbook.Sheets("FS").Columns(2).Cells
For i = 0 To ListBox_selected_countries.ListCount - 1
country_to_delete = ListBox_selected_countries.List(i)
For j = 1 To 5
Set active_range = custom_range(j)
For Each c In active_range
If c.Value = country_to_delete Then
c.EntireRow.delete
End If
Next c
Next j
Next i
End Sub

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

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.

VBA - Search Column by Specific Name without Text box

I am trying to copy specific columns by the column name and copy it to a new worksheet. I found this code online but I would like to have the cloumn names in the vba code instead of having a textbox pop up and me writing each one in at a time.
Sub copycolumns()
Dim strColRng As String
Dim strSheetName As String
Dim intNoofCols As Integer
Dim strColName() As String
Dim strCurSheetName As String
'To get the No. of Columns Available to Search
intRng = 65
'To get the No. of Columns to copy and paste
intNoofCols = 10
'To set size of the Array
ReDim Preserve strColName(intNoofCols)
For i = 0 To intNoofCols - 1
'To Get the Column Name to Search
strColName(i) = Array(Array("POS", "POS"), Array("Product Code", "Product Code"), Array("Product Name", "Product Name"), Array("Currency", "Currency"), Array("Nominal Source", "Nominal Source"), Array("Maturity Date", "Maturity Date"), Array("Nominal USD", "Nominal USD"), Array("BV Source", "BV Source"), Array("ISIN", "ISIN"), Array("Daily NII USD", "Daily NII USD"))
' InputBox("Enter the Column Name to Copy?", "Column Name")
Next
'To get the Sheet Name to paste the content
strSheetName = InputBox("Enter the Sheet Name to Paste?", "Sheet Name")
'To store the Current Sheet Name where to copy
strCurSheetName = ActiveSheet.Name
For j = 0 To intNoofCols - 1 'To get the Column Names from the Array
For i = 1 To intRng
'To Select the Sheet which column to copy
Sheets(strCurSheetName).Select
'Store the Cell Value
strVal = Cells(1, i)
'Check the Value with the User given column name
If UCase(strVal) = UCase(Trim(strColName(j))) Then
'Select and Copy
Cells(1, i).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
'Select and Paste
Sheets(strSheetName).Select
Cells(1, j + 1).Select
Range(Selection, Selection.End(xlDown)).PasteSpecial xlPasteValues
' ActiveSheet.Paste
End If
Next
Next
I appreciate any help. Thanks!
So, if I understand correctly, you want the strColName variable to hold the array you defined, instead of the program looping and asking the user to fill the array? In that case, use:
Dim strColName() As String
strColName = Split("POS,Product Code,Product Name,Currency,Nominal Source,Maturity Date,Nominal USD,BV Source,ISIN,Daily NII USD", ",")
Problem is, you defined the strColName as an array of Strings, and you input arrays. Also, you defined the array inside the loop, so it will execute 10 times. You can delete the redim statement, because you define the number of members of the array when you make the array.
I use this all the time
'1 = DELETE all columns IN list
'2 = DELETE all columns NOT in list
'3 = MOVE all columns IN List to NEW Sheet
'4 = MOVE all columns NOT in List to NEW Sheet
'sSource = Source Sheet for Deleting or Moving To
'tTarget = Target Sheet for Moving Columns To
'n = offset the numer of columns when moving columns n = 0 for no offset
Sub MoveOrDelete()
fDeleteOrMove 3, "MySheetNameSoure", "MySheetNameTarget", 0, Array("ColName1", "ColName2", "ColName3")
End Sub
'THIS IS THE FUNCTION FOR MOVE/DELETE
Sub fDeleteOrMove(cWhat As Integer, sSource As String, tTarget As String, n As Integer, myList As Variant)
Dim wsS As Excel.Worksheet
Dim wsT As Excel.Worksheet
Dim LC As Long
Dim mycol As Long
Dim x
ActiveWorkbook.Worksheets(sSource).Select
Set wsS = ActiveWorkbook.Worksheets(sSource) 'Source Sheet for Deleting or Moving To
Set wsT = ActiveWorkbook.Worksheets(tTarget) 'Target Sheet for Moving Columns To
'Get Last Row of "Source" Sheet
LC = wsS.Cells(1, Columns.Count).End(xlToLeft).Column
For mycol = LC To 1 Step -1
x = ""
On Error Resume Next
x = WorksheetFunction.match(Cells(1, mycol), myList, 0)
Select Case cWhat
Case 1
'Delete all columns IN list
If IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Delete
Case 2
'Delete all columns NOT in list
If Not IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Delete
Case 3
'Move all columns IN List to NEW Sheet
If IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Copy _
Destination:=wsT.Columns(x).Offset(, n)
Case 4
'Move all columns NOT in List to NEW SheeT
If Not IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Copy _
Destination:=wsT.Columns(mycol).Offset(, n)
'Delete the EMPTY columns that were not moved from "Target" sheet
If IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Copy _
Destination:=wsT.Columns(mycol).Offset(, n).Delete
End Select
Next mycol
ActiveWorkbook.Worksheets(tTarget).Select
End Sub