How to dynamically add elements in an array inside a loop - excel

I have a workbook with several sheets and I need to create an array with only the wanted sheets.
So I have this code that skips some hardcoded sheet names, but I don't know how to add the wanted sheets in my array 'sheets_names_array'.
I have this code:
' make an array with the sheet names we want to parse
Dim sheets_names_array() As Variant, sheet As Variant
For Each ws In ThisWorkbook.Worksheets
Select Case ws.Name
Case "Qlik Ingestion"
'Do nothing
Case "Dropdown Values"
'Do nothing
Case "VBmacro"
'Do nothing
Case Else
'MsgBox ws.Name
sheets_names_array.Add (ws.Name)
End Select
Next
But the 'Add' method doesnt work. Do you know how to solve this please?
I have seen documentation that uses ReDim but I am not sure how to loop through the elements of the 'sheets_names_array' table

You can use a collection instead
Dim sheets_names_col As New Collection
and add your items like
sheets_names_col.Add ws.Name
Dim sheets_names_col() As New Collection
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Select Case ws.Name
Case "Qlik Ingestion", "Dropdown Values", "VBmacro"
' do nothing
Case Else
sheets_names_col.Add ws.Name
End Select
Next ws
And you can loop it like
Dim sheet As Variant
For Each sheet In sheets_names_col
Debug.Print sheet
Next sheet

Related

Making sheet name the same as cell value

I am currently working on macro in a workbook with multiple worksheets, that aims to show and hide certain worksheets based on the values in a master worksheet. The worksheet names are also contained in the master worksheet and the main procedure looks at these values when referencing to a worksheet it needs to show or hide. The problem with this method is that, the macro will produce errors if the user changes the worksheet tab names. I was hoping to insert an additional procedure that makes the tab names of each worksheet equal to the values in the respective cell of the master worksheet. I came up with the following:
Sub SheetName()
If Not ActiveWorkbook Is ThisWorkbook Then Exit Sub
Dim DataImport As Worksheet
Set DataImport = ThisWorkbook.Worksheets("Data Import")
DataImport.Range("A13").Value = Sheet1.Name
End Sub
This code works fine but there are about 100+ worksheets in this workbook. I was wondering if there is a more efficient way to do this, as opposed to typing the same procedure 100 times. I've tried storing the worksheet code names in an array and looping the same procedure through the array, for example:
Sub test()
Dim DataImport As Worksheet
Set DataImport = ThisWorkbook.Worksheets("Data Import")
Dim index As Long
Dim ws(0 To 2) As Worksheet
Set ws = Array(Sheet1, Sheet2, Sheet3)
For i = 13 To 14
index = i - 13
DataImport.Cells(i, "A").Value = ws(index).Name
Next i
End Sub
but the error message "Can't Assign to Array" shows up. Sorry in advance if my code looks garbage, I am still new to VBA and I still have quite a lot to learn.
If you list the sheet codenames in ColA of your master sheet, then this code will update columns B and C with the current sheet tab names and indexes respectively:
Sub UpdateIndex()
Dim ws As Worksheet, cn As String, m
For Each ws In ThisWorkbook.Worksheets
cn = ws.CodeName
If cn <> DataImport.CodeName Then
'look for the codename in the Import sheet
m = Application.Match(cn, DataImport.Columns(1), 0)
If Not IsError(m) Then
'got a match - update this row
DataImport.Cells(m, "B").Value = ws.Name 'tab name
DataImport.Cells(m, "C").Value = ws.Index 'sheet index
End If
End If
Next ws
End Sub
Assumes you set the code name for your "Data Import" worksheet to DataImport.
If your code is driven by the sheet codename, it doesn't matter whether the user renames the tabs or changes the sheet order.
For your second attempt, you can use Excel built-in Sheets object and Workbook.Sheets collection:
Sub test()
Dim DataImport As Worksheet
Set DataImport = ThisWorkbook.Worksheets("Data Import")
Dim index As Long
Dim ws As Excel.Sheets
Set ws = ThisWorkbook.Sheets
For i = 13 To 14
index = i - 13
DataImport.Cells(i, "A").Value = ws(index).Name
Next i
End Sub

VBA code to call different macro depending on part of Worksheet name

I am working on a macro that will cycle through all of the sheets in the active workbook and will then clear a certain part of a particular worksheet, based on whether one of the relevant keywords is contained in the worksheet name. In each case the worksheet name will be different, but any I want to clear will contain one of the key words below.
I have set up a separate macro to clear the range of cells in each case. If the Worksheet name does not contain any of the keywords, I want the macro to move onto the next worksheet.
My ultimate aim is to be able to apply this to numerous different workbooks, as the project I am working on is split by region, with a separate Excel file per region.
The code I have been trying is below. There are no errors appearing when I run the code, the code does not seem to run either, in fact nothing at all happens!
Any guidance or advice would be greatly appreciated.
Sub Loop_Customer_Sheets()
Dim ws As Integer
Dim i As Integer
ws = ActiveWorkbook.Worksheets.Count
For i = 1 To ws
If ActiveSheet.Name Like "*ABC*" Then
Call ABCInfoClear
ElseIf ActiveSheet.Name Like "*DEF*" Then
Call DEFInfoClear
ElseIf ActiveSheet.Name Like "*GHI*" Then
Call GHIInfoClear
Else:
End If
Next i
End Sub
"Nothing at all happens" - fixing the issue with your code:
Your issue is that you are looping through the number of sheets, but you are only checking the ActiveSheet, which never changes! Replace your code with
ws = ActiveWorkbook.Worksheets.Count
For i = 1 To ws
With ActiveWorkbook.WorkSheets(i)
If .Name Like "*ABC*" Then
ABCInfoClear
ElseIf .Name Like "*DEF*" Then
DEFInfoClear
ElseIf ActiveSheet.Name Like "*GHI*" Then
GHIInfoClear
End If
End With
Next i
Note: you don't need the Call keyword, you can just call subs as presented above.
Alternative solutions
A better option than having numerous macros might be to create a generic sub like
Sub ClearRangeInSheet(rangeAddress As String, sh As WorkSheet)
Dim myRange As Range
Set myRange = sh.Range(rangeAddress)
myRange.ClearContents
' Any other cell clearing code e.g. for formatting here
End Sub
Then call in the loop
Dim wsCount as Long
wsCount = ActiveWorkbook.WorkSheets.Count
For i = 1 to wsCount
With ActiveWorkbook
If .WorkSheets(i).Name Like "*ABC*" Then
' Always pass ".WorkSheets(i)", but change the range address as needed
ClearRangeInSheet("A1:A20", .WorkSheets(i))
ElseIf ' Other worksheet name conditions ...
End If
End With
Next I
As suggested in the comments, you could ditch indexing the sheets, and just loop through the sheet objects themselves:
Dim wksht as WorkSheet
For Each wksht In ActiveWorkbook.WorkSheets
If wksht.Name Like "*ABC*" Then
' Always pass wksht but change the range address as needed
ClearRangeInSheet("A1:A20", wksht)
ElseIf ' Other worksheet name conditions ...
End If
Next wksht

How to declare a worksheet name as wildcard?

I have a worksheet named "Photo Sheet" that i would like to declare in my codes.
Const myWorksheet = "Photo Sheet"
My question, if i have another sheet called "Photo Sheet (2)" is there a way to declare the variable as wildcard that would take any sheet starting with "Photo Sheet*" ?
Not quite clear what you want to do, but you can iterate over the worksheets, using the Like operator to select the ones which have the appropriate name:
Sub test()
Dim ws As Worksheet
For Each ws In Worksheets
If ws.Name Like "Photo Sheet*" Then Debug.Print ws.Name
Next ws
End Sub
This will print the names of all worksheets that begin "Photo Sheet". Of course, rather than printing their names you could e.g. put these worksheets in a collection for further processing.
There isn't if you use an equity operator for testing it, but you can do something similar with Like:
Const SHEET_NAME = "Photo Sheet*"
Sub WhateverYourThingDoes()
Dim ws As Worksheet
For Each ws in Worksheets
If ws.Name Like SHEET_NAME Then
'Your code here.
End If
Next
End Sub
You can't use a wildcard to declare a worksheet directly, so no set shtPhotos = Sheets(Worksheet & "*"). A declaration like that has to be unambiguous or it would potentially return a collection, which can't be assigned to a non-array variable.
So, no wildcards. What you can do is loop through all your worksheets and check whether the sheet's name contains whatever text you're looking for:
Sub FindPhotos()
Const csSheet As String = "Photo Sheet"
Dim shtPhotos As Worksheet
For Each shtPhotos In ActiveWorkbook.Sheets
If InStr(1, shtPhotos.Name, csSheet) <> 0 Then
'do something
End If
Next shtPhotos
End Sub
This is going to look at every worksheet in the active workbook and see if their name contains the text in the constant. This will work fine if you want to perform the same steps with every worksheet that begins "Photo Sheets"; if you only want it to perform those steps with one such sheet, you'd add an Exit For after performing your steps:
For Each shtPhotos In ActiveWorkbook.Sheets
If InStr(1, shtPhotos.Name, csSheet) <> 0 Then
'do something
Exit For
End If
Next shtPhotos
You could also look for a flag on the sheets that are found, so that only sheets that have, say, today's date in a specified cell would be processed:
For Each shtPhotos In ActiveWorkbook.Sheets
If InStr(1, shtPhotos.Name, csSheet) <> 0 AND _
shtphotos.range("A1").value = Date Then
'do something
End If
Next shtPhotos

Obtain displayed order of Excel worksheets

I'd like to find the position of a worksheet as it is displayed in a workbook.
For example, assume I have a workbook starting with Sheet1, Sheet2 and Sheet3 in that order. Then a user drags Sheet2 to left, before Sheet1.
I want Sheet2 to return 1, Sheet1 to return 2 (and Sheet3 still to return 3).
I can't find a way to determine this in VBA.
This should do it:
Worksheets("Sheet1").Index
https://msdn.microsoft.com/en-us/library/office/ff836415.aspx
You can just iterate the Worksheets collection of the Workbook object. You can test yourself by running the following code, switch the order around in the UI, then run it again:
Option Explicit
Sub IterateSheetsByOrder()
Dim intCounter As Integer
Dim wb As Workbook
Set wb = ThisWorkbook
For intCounter = 1 To wb.Worksheets.Count
Debug.Print wb.Worksheets(intCounter).Name
Next intCounter
End Sub
To loop through all worksheets in a workbook use For Each WS in ThisWorkbook.Worksheets where WS is a worksheet object. Hence to obtain order of Excel worksheets as shown, we may also use the following code:
Sub LoopThroughWorksheets()
Dim WS As Worksheet
For Each WS In ThisWorkbook.Worksheets
Debug.Print WS.Name
Next
End Sub
To obtain an output like Worksheets("Sheet1").Index then you may use this code
Sub IndexWorksheet()
Dim WS As Worksheet, n As Long
For Each WS In ThisWorkbook.Worksheets
n = n + 1
If WS.Name = "Sheet1" Then Debug.Print n
Next
End Sub
You can use the Sheets object. In your example, reading Sheets(2).Name should return Sheet1.
Right answer provided by Anastasiya-Romanova, but missing some important details.
There are two methods of doing this. First, with a For Each loop:
Sub ListSheetNames()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print ws.Name
Next ws
End Sub
Second, with a basic For loop:
Sub ListSheetNames
Dim i As Long
For i = 1 to ThisWorkbook.Worksheets.Count
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i
End Sub
You will find the second method will always output the names in the sheet index order, which is generally the order the sheets were created in unless you change the index. Simply rearranging the sheets from the workbook window won't change the index.
Therefore, the first method is the correct way to do this. It will always follow the tab order as you see on your screen.
Below code works even if sheet is renamed or its sequence is changed.
Sub Display_Sheet_Tab_Number()
Dim WorksheetName As String
Dim n As Integer
WorksheetName = Sheet1.Name
MsgBox Worksheetname
n = Sheets(WorksheetName).Index 'n is index number of the sheet
MsgBox "Index No. = " & n
End Sub

With statement for multiple worksheets?

I am repeating a set of actions for certain sheets in a workbook currently by
For Each ws In Worksheets
Select Case ws.Name
Case "2015", "2016", "Rate", "Month"
'code here
End Select
Next ws
which seems awfully inefficient XD
Is it possible to do something like
With ws1, ws2, ws3
'code here
end with
I don't have that many sheets so the loop only takes ~2 seconds but there must be a quicker/elegant way?
Worksheets is a collection that contains all sheets in the current workbook.
To simplify you code you could create your own collection with just the sheets to be processed and use this instead. Then you wouldn't need the select case statement to limit which sheets are processed by the loop.
However, seeing up a collection is probably more work than doing it the way you have done. Although it is the OO way of don't it.
So in a module you might do something like this:
Sub aa()
Dim colSheets As Collection
Set colSheets = New Collection
colSheets.Add Sheet1 ' here I use the object name from the VBA "(Name)" property
colSheets.Add Worksheets("MyWorksheetName")
colSheets.Add Worksheets("SummarySheet")
Dim sht As Worksheet
For Each sht In colSheets
Debug.Print sht.Name
Next sht
Set colSheets = Nothing
End Sub

Resources