The goal of my macro is from a large set of data, divide the data into separate workbooks for each product. After I paste only the specific data into the file, I want to copy 5 separate worksheets from the workbook with all of the data, each worksheet with a pivot table on them, set the data on the pivot table to the data on the spread sheet that I copied over previously. Then refresh the pivot tables.
My biggest question is how to I name an array of worksheets to a work book
Dim cell As Range, DataRng As Range
Dim curPath As String, curWB As String, newWB
Dim ArrayCM As Variant
Dim InxW As Long
Dim xTable As PivotTable
curPath = ActiveWorkbook.Path & "\"
curWB = ActiveWorkbook.Name
'Array of worksheets to be copied over for Company Manger
Set ArrayCM = curWB.Sheets(Array("CM YTD", "CM MTD", "CM Refurb", "TBM Local", "PSM"))
--This Gives me an error
Because I'm going to creating several workbooks, I want to define my array to copy over from the master file to the new file. Help on this would be greatly appreciated.
You can create a worksheet type var as an array, redim it and assign each element within the array to a Worksheet Object.
Sub manyWSs()
Dim w As Long
Dim cwb As Workbook
Dim wss() As Worksheet
Set cwb = ThisWorkbook
With cwb.Worksheets(Array("Sheet1", "Sheet3", "Sheet5"))
ReDim wss(1 To .Count)
For w = 1 To .Count
Debug.Print .Item(w).Name
Set wss(w) = .Item(w)
Next w
End With
For w = LBound(wss) To UBound(wss)
Debug.Print wss(w).Cells(1, 1).Address(0, 0, external:=True)
Next w
End Sub
The Worksheets(Array("Sheet1", "Sheet3", "Sheet5")) is not a true Worksheets collection so .Item(w) is used to pick out the pieces. Once each piece has been Set, all the properties, methods and members of a single Worksheet Object are available to each piece of the array with full parent Workbook object association.
Related
the VBA code below scans two different datasets/tables in excel against possible matches in Worksheet 2 (aka SecondaryTable) and Worksheet 1 (aka MainTable). Both “Main” and “Secondary” Tables are Table Objects in Excel:
Sub looping()
Dim lRow As Long
Dim lCol As Long
Dim lRow2 As Long
Dim lCol2 As Long
Dim wordsArray() As Variant
wordsArray = Worksheets("SecondaryTable").Range("A2:A" & lRow2).Value
Dim word As Variant
Dim cell As Range
Set sht = Worksheets("MainTable")
Set sht2 = Worksheets("SecondaryTable")
lRow = sht.Range("A1").CurrentRegion.Rows.Count
lCol = sht.Range("A1").CurrentRegion.Columns.Count
lRow2 = sht2.Range("A1").CurrentRegion.Rows.Count
lCol2 = sht2.Range("A1").CurrentRegion.Columns.Count
For Each cell In Worksheets("MainTable").Range("I2:I" & lRow)
For Each word In wordsArray
If InStr(cell.Value, word) > 0 Then
cell.Offset(0, -2).Value = cell.Offset(0, -2).Value & " " & word
End If
Next word
Next cell
End Sub
I wanted to ask if there is any good way (after several failed attempts and errors via VBA in the last couple of days) of doing the following:
Is there any way of offsetting the value identified into a specific Table column instead of counting columns to determine exactly where the data will be populated / should be offset to? I tried replacing cell.Offset(0, -2).Value with a Table reference to the column name such as “Results” however I kept getting errors.
Would there any specific way after the code finds a match from wordsArray = Worksheets("SecondaryTable").Range("A2:A" & lRow2).Value to return a different value from an adjacent cell located in Range("B2:B" & lrow2).Value? The secondary table contains partial keywords in one column via which the loop is executed and a second adjacent column that contains the full name. I tried offsetting the variable word e.g., word.offset(0,1).Value in an effort to pull the name from Column 2 but only got errors.
Secondary Table example
Column A (keywords) Column B(full string)
Dog big dog
Cat small cat
Since you say Tables are Table Objects in Excel: utilise that fact. These are called ListObject's in VBA.
Replace the various NameOf... strings with your actual names
Sub looping()
Dim wordsArray() As Variant
Dim FullWordsArray() As Variant
Dim wb As Workbook
Dim sht As Worksheet
Dim sht2 As Worksheet
Dim loSecondary As ListObject
Dim loMain As ListObject
Set wb = ThisWorkbook ' or specify a workbook
Set sht = wb.Worksheets("MainTable")
Set sht2 = ws.Worksheets("SecondaryTable")
Set loMain = sht.ListObjects(1) ' or by name: Set loMain = sht.ListObjects("YourTableName')
Set loSecondary = sht2.ListObjects(1)
' get two arrays, one for lookup, and the other for replacements
wordsArray = loSecondary.ListColumns("NameOfWordColumn").DataBodyRange.Value2
FullWordsArray = loSecondary.ListColumns("NameOfFullWordColumn").DataBodyRange.Value2
Dim WordIdx As Long
Dim SearchCol As Long
Dim UpdateCol As Long
Dim rw As Long
Dim lr As ListRow
SearchCol = loMain.ListColumns("NameOfColumnToSearch").Index
UpdateCol = loMain.ListColumns("NameOfColumnToUpdate").Index
For Each lr In loMain.ListRows
With lr.Range
For WordIdx = 1 To UBound(wordsArray, 1)
If InStr(.Cells(1, SearchCol).Value2, wordsArray(WordIdx, 1)) > 0 Then
With .Cells(1, UpdateCol)
.Value2 = .Value2 & " " & FullWordsArray(WordIdx, 1)
End With
End If
Next
End With
Next
End Sub
I have a an export "NewExport" that always randomizes the columns of data I receive. I need these columns to align with the order of columns in "TheOrder", so this code will help to re-organize the export to align with the column headers I've already built.
I have 132 columns that need re-alignment, and while I can type it all out, there must be an easier way to align with the column headers I've already created. It should be noted that the below code is shamelessly copy/pasted from another StackOverflow answer.
Sub OrderColumns(ByVal NewExport As Workbook, ByVal TheOrder As Worksheet)
Dim correctOrder() As Variant
Dim lastCol As Long
Dim headerRng As Range, cel As Range
Dim mainWS As Worksheet
Set mainWS = NewExport.Worksheets("Sheet1")
'Need to figure out how to make this an array based on a Range
correctOrder() = Array(TheOrder.Range("A1:A132").Value)
With mainWS
lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set headerRng = .Range(.Cells(1, 1), .Cells(1, lastCol))
End With
Dim newWS As Worksheet
Set newWS = Ninja.Sheets.Add
newWS.Name = "Rearranged Sheet"
Dim col As Long
With newWS
For col = 1 To lastCol
For Each cel In headerRng
If cel.Value = correctOrder(col - 1) Then
mainWS.Columns(cel.Column).Copy .Columns(col)
Exit For
End If
Next cel
Next col
End With
End Sub
While it's not as automated as I would have liked (and requires one piece of hard-coding), I was able to find a solution as such:
Dim correctOrder(132) As Variant
'132 will need to be changed if there's ever any more/less columns added/excluded
For i = 1 To 132
correctOrder(i - 1) = TheOrder.Range("A" & i).Value
Next
This solution gave me the array I was looking for for use later on.
I recently wrote a 'column finder' function for a project of mine.
I've modified it to suit your requirements below.
The function requires you pass the workbook your correct ordered headings are in to capture. You could modify this to require your TargetWorksheet instead so it's a bit more dynamic.
The function returns a single dimension Array.
The function finds the last used Column in the Target Worksheet allowing for changes in the number of column headings (as mentioned in your own answer which has the column number hard coded).
Public Function CorrectOrderHeadingsArrayFunction(ByRef TargetWorkbook As Workbook) As Variant()
With TargetWorkbook.Sheets(1) 'Change this to target your correct sheet
Dim LastColumn As Long
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
CorrectOrderHeadingsArrayFunction= Application.Transpose(Application.Transpose(.Range(.Cells(1, 1), .Cells(1, LastColumn)).Value)) 'This returns the array as single dimension rather than 2D
End With
End Function
As an example, below is some sample 'Test' code to show the concept of using this function .
You could call it like so, and loop through each element perhaps comparing another arrays elements to the correct order elements and do something when the correct order value is found.
Sub TestSub()
Dim CorrectOrderArray As Variant
Dim TargetCorrectOrderElement As Variant
Dim RandomOrderArray As Variant
Dim TargetRandomOrderElement As Variant
CorrectOrderArray = CorrectOrderHeadingsArrayFunction(Workbooks("Export (4).csv")) 'Change this to target your correct workbook
RandomOrderArray = Sheet1.Range("A1:AZ1000") 'Change this to target the correct range for your data.
For Each TargetCorrectOrderElement In CorrectOrderArray
For TargetRandomOrderElement = LBound(RandomOrderArray) To UBound(RandomOrderArray)
If RandomOrderArray(TargetRandomOrderElement) = TargetCorrectorderValue Then
'Do some code to write that column to your worksheet
Exit For 'Leaves the current iteration of the random order array loop to go to the next iteration of the correct order array
End If
Next TargetRandomOrderElement
Next TargetCorrectOrderElement
End Sub
I am trying to populate an array with values from a column in a filtered Excel table.
In this column, the values may appear multiple times, but I need to return unique values, not all occurrences of each value.
Column F
a
a
a
b
c
c
a
b
d
The array would have a variable length and, based on the sample column would have the elements: {a, b, c, d}
The length of the array cannot be fixed because my function works with a filtered table that varies in length. Sometimes there may be only one unique value, other times there might be three.
I need to do this because my array will be used to determine the subject of an e-mail with, "... " & Array.
How do I extract unique values in a column to the array?
Use Scripting.Dictionary
You can select all the data from a column with sht.Cells(sht.Rows.Count, "F").End(xlUp).Row and then use a Scripting.Dictionary to find your unique values. Here's the code:
'Main Routine
Sub MyMacro()
Dim sht As Worksheet
Dim column As Range
Dim LastRow As Long
Dim uniqueValues() As Variant
Set sht = ActiveSheet 'Set your sheet here
LastRow = sht.Cells(sht.Rows.Count, "F").End(xlUp).Row
Set column = Range("F1:F" & LastRow)
uniqueValues = getUniqueValues(column)
'Do what you need to do with your values [...]
End Sub
'Return unique values from a Range
Function getUniqueValues(column As Range)
Dim dict As New Scripting.Dictionary ' requires "Microsoft Scripting Runtime"
Dim cell As Range
For Each cell In column
dict(cell.Value) = "1"
Next
'A double Transpose will put your data in an Array() format
getUniqueValues = Application.Transpose(Application.Transpose(dict.Keys))
End Function
If you don't want to import Microsoft Scripting Runtime, use this code for dict declaration:
'If you don't want to import Scripting Runtime, use this code
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Note: This is tested and works perfectly.
I hope this helps.
you can use Scripting.Dictionary for such task and xlCellTypeVisible, example:
Sub sometest()
Dim x As Long, cl As Range
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
dic.comparemode = vbTextCompare
x = Cells(Rows.Count, "A").End(xlUp).Row
For Each cl In Range(Cells(2, "A"), Cells(x, "A")).SpecialCells(xlCellTypeVisible)
If Not dic.exists(cl.Value) Then
dic.Add cl.Value, Nothing
End If
Next cl
Debug.Print Join(dic.keys, ",")
End Sub
test:
Disclaimer - I used to write macros a lot, but it's a perishable skill. 5 years takes it toll.
The basic concept is this:
I have a template workbook, with up to 30 tabs, that all have indeterminate rows and columns (i.e. It's not always A7:J30 - one tab might have 3 columns, the next 34 columns. Rows are also indeterminate.).
Currently, someone is copy/pasting 30 separate CSVs into this one templated workbook.
This templated workbook is read by another program to populate data. Row 6 of each template sheet is where the other program looks for headers (i.e. I might copy a CSV's data from A2:G1000, but it would need to paste in A7:G1005 of the template target workbook).
All of the CSVs are stored in the same directory. We can copy/paste a Template workbook into that directory, run a macro, and be done.
What I've done so far:
Sub V1BruteForceCopy()
'
'This code lives in ImportTemplate.XLSM, and is run from the same
'
Workbooks.Open (ThisWorkbook.Path & "\deposits.csv") 'Open deposits.CSV in same directory
Range("A2:G1000000").Copy 'Very inflexible copy job - ugly.
Windows("ImportTemplate.xlsm").Activate 'hate to Activate, but can't get it to work without it.
Sheets("depositbatches").Range("A7").Select 'must call each Sheet in the code, instead of declare variable
ActiveSheet.Paste 'don't like Activate, but Sheets("depositbatches").Range("A7").Paste throws an error.
End Sub 'to add a new CSV and a new Sheet to copy to, I have to copy a whole new block of code and then overwrite Sheets("name") and Workbooks.Open(ThisWorkook.Path & "\name.csv") every time.
Other things I've tried:
Sub rangecopy_001()
Dim ImpTemp As Workbook 'Reserved for ImportTemplate
Dim CSVdeposits As Workbook 'Reserved for deposits.CSV
Dim shDeposits As Worksheet 'Deposits worksheet inside ImportTemplate
Dim lRow As Long 'variable for last row
Dim lCol As Long 'variable for last column
Dim test As Range 'variable for copy/paste range
Set ImpTemp = Workbooks.Open(ThisWorkbook.Path & "\ImportTemplate_CSV.xlsm") 'Open DWImportTemplate
Set CSVdeposits = Workbooks.Open(ThisWorkbook.Path & "\deposits.csv") 'Open deposits.CSV
Set shDeposits = ImpTemp.Sheets("depositbatches") 'Declare that shDeposits is a ImportTemplate sheet
With CSVdeposits 'copy out of deposits.CSV and paste into ImportTemplate deposits sheet
'find last row - makes this dynamic
lRow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'find last column - makes this dynamic
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
test = CSVdeposits.Sheet(1).Range("A2:" & Cells(lRow, lCol)) 'error code 438 - Object doesn't support method
DW.shDeposits.Range("A7") = test
End With
End Sub
This makes the copy range dynamic, but I'm still getting an object error when I try to select the range. I got this method from (Copy from one workbook and paste into another) but it's too simple. Plus, if I want to add another 20 tabs, I have to copy/paste this code block another 20 times and change the variables each time.
I found this (Copy multiple rows from multiple workbooks to one master workbook), but Ron DeBruin's thing won't work because we have to move everything down to Row 6, plus we can't count on the headers of the CSVs working properly.
I like the last answer here (Dynamic range of data to paste into another sheet?) but I can't seem to make it work for a single workbook target from other workbooks.
I want to use an array, or set of arrays to declare my worksheets, but I don't know how to iterate over two arrays at one time that are string-based. I'm thinking something like this, but I'm not done:
Sub ArrayCopyV1()
'
'This code lives in Template.XLSM and is run from the same. Copy this book to the directory you wish to copy from.
'
'
Dim ArraySheets As Variant 'an array with all Sheet names. Should have the same number of args as ArrayCSVs array.
Dim ArrayCSVs As Variant 'an array with all CSV names Should have the same number of args as ArraySheets array.
Dim template As Worksheet 'variable for template worksheet inside
Template workbook
Dim CSV As Workbook 'variable for CSV workbook
Dim i As Integer 'variable i to be used in FOR loop counter
Dim lcol as Integer
Dim lrow as Integer
ArraySheets = Array("depositbatches", "otherSheet1", "OtherSheet2")
ArrayCSVs = Array("\deposits.csv", "other1.csv", "Other2.csv")
For i = LBound(ArraySheets) To UBound(ArraySheets)
Set CSV = Workbooks.Open(ThisWorkbook.Path & ArrayCSVs(i))
Set template = Workbooks.Open(ThisWorkbook.Path & ArraySheets(i))
With CSV 'copy out of deposits.CSV and paste into DWImportTemplate deposits sheet
'find last row - makes this dynamic
lRow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'find last column - makes this dynamic
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
test = CSV.Sheet(1).Range("A2:" & Cells(lRow, lCol))
template.Range("A7") = test
End With
Next i
End Sub
For example:
Sub CopyAll()
Dim rw As Range, wb As Workbook
'read over your file<>sheet table
For Each rw In ThisWorkbook.Sheets("Files").Range("A2:B30").Rows
Set wb = Workbooks.Open(ThisWorkbook.Path & "\" & rw.Cells(1).Value) '<< csv file name
With wb.Sheets(1).Range("A1").CurrentRegion
'skip headers (relies on contiguous data)
.Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count).Copy _
ThisWorkbook.Sheets(rw.Cells(2).Value).Range("A7") '<< sheet name to paste into
End With
wb.Close False
Next rw
End Sub
The code below is meant to read columns from an Excel table into arrays, which can then be used to determine whether each "Project" belongs to the Environment "Group", and if so, to add the project number and dollar value to another array. I am having some issues with my code, and have been searching the internet and StackOverflow but have been able to find very little information on dealing with Excel Tables using VBA. I am using Excel 2010.
Sub UpdateProjectsAndCharges()
'Define arrays to be used
Dim projectArray() As Variant
Dim uniqueProjectArray(100) As Variant
Dim dollarValue() As Variant
Dim envProjectArray(100) As Variant
Dim envDollarValue(100) As Double
Dim cumulativeCosts(100) As Double
'Define all tables in this sheet as list objects
Dim UnitsValues As ListObject
Dim ChargingTracking As ListObject
'Define counters to be used
Dim counter As Integer
Dim counter2 As Integer
'Set variables for each table in sheet
Set UnitsValues = Sheets("Cluster Data").ListObjects("UnitsValues")
Set ChargingTracking = Sheets("Cluster Data").ListObjects("ChargingTracking")
'Find last row in table
With Sheets("Cluster Data")
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
End With
'Define variables to be used in loops
Dim userGroup As Variant
Dim project As Variant
Dim Value As Variant
'Set arrays to respective columns from UnitsValues table
userGroups = Range("UnitsValues[Group]")
projectArray = Range("UnitsValues[Project]")
dollarValue = Range("UnitsValues[Dollar Value]")
'Redefine length of arrays to number of rows in table
ReDim Preserve projectArray(lastRow)
ReDim Preserve dollarValue(lastRow)
'Set counter values
counter = 1
counter2 = 1
For Each userGroup In userGroups
project = projectArray(counter)
Value = dollarValue(counter)
If userGroup = "Environment" Then
envProjectArray(counter2) = project
envDollarValue(counter2) = Value
counter2 = counter2 + 1
MsgBox ((envProjectArray(counter2) & " " & envDollarValue(counter2)))
End If
counter = counter + 1
Next userGroup
I was receiving the "Subscript out of range" error with these lines:
project = projectArray(counter)
Value = dollarValue(counter)
I looked up the error and thought that these lines would perhaps fix the problem:
ReDim Preserve projectArray(lastRow)
ReDim Preserve dollarValue(lastRow)
Now, I am receiving the same error on the lines above instead, and have run out of ideas on how to fix the error. I suspect it is happening because I assigned a range into an array, but I'm not certain.
Change:
project = projectArray(counter)
Value = dollarValue(counter)
to
project = projectArray(counter, 1)
Value = dollarValue(counter, 1)
Arrays read from worksheets always are multidimensional even if you just have 1 column.
In this case you are specifying that column to be 1, every time.