VBA to loop through sheet and copy contents to a new workbook

Mjoza

Board Regular
Joined
Aug 31, 2011
Messages
172
Hi all,

Can someone please help me with a code. I have a workbook with sheet1 which contains data that I want to copy that data to different workbooks (Month tab) based on the values in column a.

The data in Sheet1 is sorted by column a and the workbooks which the data has to be copied to are named based on column a values.

For example the table below will be the values in sheet1[TABLE="width: 500"]
<tbody>[TR]
[TD]Name[/TD]
[TD]YEAR[/TD]
[TD]Month[/TD]
[TD]QTY[/TD]
[TD]Code[/TD]
[TD]CONFRM[/TD]
[/TR]
[TR]
[TD]IND[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]12[/TD]
[TD]2345[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]IND[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]45[/TD]
[TD]2345[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]IRE[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]646[/TD]
[TD]3345[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]IRE[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]35[/TD]
[TD]3345[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]SGP[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]35[/TD]
[TD]4588[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]USA[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]45[/TD]
[TD]1101[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]USA[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]452[/TD]
[TD]1102[/TD]
[TD]Y[/TD]
[/TR]
</tbody>[/TABLE]

The destination workbooks are saved in a different folder (named Completed) on the desktop. In that folder there are excel files with with name that begin with names like in the Name column above ie IND Monthly Data, IRE Monthly Data, USA Monthly Data etc. There are a lot more than whats shown above so I would like to make the code dynamic.

What I would like to do is to have the code to loop through the data in sheet1 and open files saved in the Completed folder and save the data.

So for example the code will copy the information in the sheet1 to 4 different workbooks and save each and close. The data will be pasted into cell B2 because the destination file already has headers.

Example: workbook "IND Monthly Data" and Month tab will have the following
[TABLE="width: 500"]
<tbody>[TR]
[TD]Name[/TD]
[TD]YEAR[/TD]
[TD]Month[/TD]
[TD]QTY[/TD]
[TD]Code[/TD]
[TD]CONFRM[/TD]
[/TR]
[TR]
[TD]IND[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]12[/TD]
[TD]2345[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]IND[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]45[/TD]
[TD]2345[/TD]
[TD]Y[/TD]
[/TR]
</tbody>[/TABLE]

Example: workbook "IRE Monthly Data" and Month tab will have the following
[TABLE="width: 500"]
<tbody>[TR]
[TD]Name[/TD]
[TD]YEAR[/TD]
[TD]Month[/TD]
[TD]QTY[/TD]
[TD]Code[/TD]
[TD]CONFRM[/TD]
[/TR]
[TR]
[TD]IRE[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]646[/TD]
[TD]3345[/TD]
[TD]Y[/TD]
[/TR]
[TR]
[TD]IRE[/TD]
[TD]2015[/TD]
[TD]October[/TD]
[TD]35[/TD]
[TD]3345[/TD]
[TD]Y[/TD]
[/TR]
</tbody>[/TABLE]

Thank you so much in advance.

Mjoza
 
This i great guys. I greatly appreciate you all.

I'm still trying to figure the codes and hopefully one day I can make codes like the ones you just wrote. :)
 
Upvote 0

Excel Facts

Spell Check in Excel
Press F7 to start spell check in Excel. Be careful, by default, Excel does not check Capitalized Werds (whoops)
Glad we could help
Let us know how you get on with the code, and come back if you need more help
Alan
Bayern
Germany
 
Upvote 0
Here The Code:

Rich (BB code):
Sub MjozaWorkbookToWorkbooks() '    http://www.mrexcel.com/forum/excel-questions/899597-visual-basic-applications-loop-through-sheet-copy-contents-new-workbook.html     'http://www.excelforum.com/excel-programming-vba-macros/1101975-copy-the-data-from-master-file-and-paste-into-individual-excel-file-as-per-criteria.html
'ActiveWorkbook.Save
Application.ScreenUpdating = False 'This is not too important but can speed things up a bit by disabling the updating of the screen which otherwise frequently happens
On Error GoTo TheEnd 'In the case of an Error goto a section of code at the end of the program
Rem 1) Initial sheet info, Importantly Copying entire data from Sheet1 to an Array.
                                              'Dim vTemp As Variant 'Temporary variable used in debugging
Dim wsMain As Worksheet, wbMain As Workbook 'Give Abbreviation Methods, Properties of Object Worksheet, Workbook through .dot
Set wbMain = ThisWorkbook: Set wsMain = wbMain.Worksheets("Sheet1") 'This code is in here, This Workbook
Dim rm As Long, cm As Long, lmr As Long, lmc As Long 'Variable for Rows, Columns, last Row last Column of Main sheet. Long is a Big whole Number limit (-2,147,483,648 to 2,147,483,647) If you need some sort of validation the value should only be within the range of a Byte/Integer otherwise there's no point using anything but Long.--upon/after 32-bit, Integers (Short) need converted internally anyways, so a Long is actually faster.
Dim vLkUpc As Long: Let vLkUpc = 1 'set column number 'Column where search criteria for filtering is. '
Let lmc = wsMain.Cells(1, Columns.Count).End(xlToLeft).Column 'For this sheet the Range Object last cell in Header row, has the End porperty (argument XL to the left) applied returning the first Range object ( cell ) seen "looking" to the left. This further has the Column property applied which returns the column indicie for that cell
Let lmr = wsMain.Cells(Rows.Count, vLkUpc).End(xlUp).Row 'The Range Object ( cell ) that is the last cell  in the column of interest has the property .End ( argument Xl up ) applied to it. This returns a new range ( cell ) which is that of the first Range ( cell ) with something in it "looking up" the XL spreadsheet from the last cell. Then the .Row Property is applied to return a long number equal to the row number of that cell. ( +1 would give the next free cell )
Dim rngDataIn As Range: Set rngDataIn = wsMain.Range("A2", wsMain.Cells(lmr, lmc)) 'This is our full Master data range
Dim arrin() As Variant: Let arrin() = rngDataIn.Value 'Allowed VBA One Liner - An Array of variants may be set to a collection of Range values. The Range object works as to return a collection of (Variants initially) of various types. So Initially must see an Array of Variant types for compatability

Rem 2) If necessary, a Folder "Completed" is created on the Desktop
Dim strFoldername As String, strDefpath As String 'String variables for the relavent parts of Folder Path
Dim objWSHShell As Object 'I use late binding below for sharing so as not to hve to --Tools --References  --Scroll down and tick Microsoft Scripting Runtime library
Set objWSHShell = CreateObject("WScript.Shell") 'Creat a specific instance of WshShell is  an Object that enables you to query and interact with various aspects of the Windows User Interface.
Dim GetDesktop As String: Let GetDesktop = objWSHShell.SpecialFolders("Desktop") 'This property of the WshShell gives the string path to the desktop
Let strDefpath = GetDesktop & "\" 'Default Path as That of where this code is
Let strFoldername = "Completed"
    If Len(Dir("" & (strDefpath & strFoldername) & "", vbDirectory)) = 0 Then 'If the directory does not exist then...
    MkDir (strDefpath & strFoldername) '...make it
    Else 'The Directory ( Folder )  is presumably already there so do not make it again! Redundant code
    End If

Rem 3) data in Look Up Column is considered and eunique Array ( eunuch() ) produced from that
Dim vLkUp() As Variant 'Dynamic one dimensional array for LookUpColumn
Let vLkUp() = Application.WorksheetFunction.Index(arrin(), 0, vLkUpc) 'Returns format type (1,1) (2,1) (3,1) (4,1)... >> Index Function with second argument ("row" co - ordinate) set to 0 will return the entire "column" given by third argument ( "column" - co ordinate ), applied to the first argument which is the grid, ( Array , Row_Number, Column_Number)  http://www.excelforum.com/excel-new-users-basics/1080634-vba-1-dimensional-horizontal-and-vertical-array-conventions-ha-1-2-3-4-a.html   https://usefulgyaan.wordpress.com/2013/06/12/vba-trick-of-the-week-slicing-an-array-without-loop-application-index/
Dim Eunuch() As String 'Array used for unique LookUpColumn values to be used as both Workbook Names and Search Criteria
ReDim Eunuch(1 To 1) 'We need to size it initially, but it must be a dynamic Array to allow us to increase it's size, hence not done by Dim(1 to 1) which would make it a non dynamic Array
Let Eunuch(1) = "Uniques" 'We do not use the first element of the Array, so just for fun put a Title in it
    For rm = 1 To UBound(vLkUp(), 1) Step 1 'for each "row" in the Look Up Column ( we want to look at the values there )
        If IsError(Application.Match(vLkUp(rm, 1), Eunuch(), 0)) Then 'Match would error if the Unique value was not in the Array for those unique values, in which case ...
        ReDim Preserve Eunuch(1 To UBound(Eunuch()) + 1): Let Eunuch(UBound(Eunuch())) = vLkUp(rm, 1) '.. if not there then put it in!
        Else: End If 'case a unique value already there for this "row"
    Next rm

Rem 4) Main Outer loop for going through Input Array and copying data to workbooks ( Workbooks are creatde inf they do not exist, based on the unique search criteria
Dim Ceunt As Long: Let Ceunt = 0 'Loop Bound variable Count for "vertical" workbooks
Dim ws As Worksheet 'Temporary Worksheet variable for use when adressing File sheets through an inner loop
Dim rngExData As Range 'Temporary range object variable for use when addressing existing data range in an existing file
Dim clms() As Variant 'Array fo Indicies are obtained for the "columns" required in each "vertical" workbook is done just outside the loop as they are the same for all "Verticals". Variant type required to satisfy the type returned by the Evaluate method below
Let clms() = Evaluate("column(A:" & Left(Replace(Cells(1, UBound(arrin(), 2)).Address, "$", "", , 1), (InStr(Replace(Cells(1, UBound(arrin(), 2)).Address, "$", "", , 1), "$") - 1)) & ")") 'Not as complicated as it looks! - The messy bit just gets the Letter part out of the last cell Address. Then the worksheet Function Columns is used:  Returns a 1 D "quasi" horizontal array of size _: to :_  In that array are the number _:   to :_  .  This "quasi horizontal" Array we require for out "magic" code line
    For Ceunt = 2 To UBound(Eunuch()) 'we consider all th relavent unique "Verticals"==========MAIN LOOP=======
    '4a) Indicies are obtained for the "rows" required in each "vertical" workbook
    Dim rwsT() As Long: ReDim rwsT(1 To 1) 'Temporary Array for the indicies of required "rows"
        For rm = 1 To UBound(vLkUp(), 1) Step 1 'for each "row" in the Look Up Column ( we want to look at the values there )
            If Eunuch(Ceunt) = vLkUp(rm, 1) Then 'this would be the case of a "row" inidie we want
            ReDim Preserve rwsT(1 To UBound(rwsT()) + 1): Let rwsT(UBound(rwsT())) = rm 'required indicie is put in the next "along" space in the Temporary 1 D Array
            Else: End If '
        Next rm
    Dim rws() As Long: ReDim rws(1 To UBound(rwsT()) - 1, 1 To 1) 'Our Indicie Array, rws(), will be 1 less than the rwsT as rwsT(1) was not used and is not needed
        For rm = 2 To UBound(rwsT()) 'for all the indicies in the Tempory Array..
        Let rws((rm - 1), 1) = rwsT(rm) '..put in the indicie
        Next rm

    '4b) An Output Array for the the Workbook of this loop
    Dim arrOut() As Variant: Let arrOut() = Application.Index(arrin(), rws(), clms()) 'This "magic" I still do not understand  http://www.excelforum.com/excel-new-users-basics/1099995-application-index-with-look-up-rows-and-columns-arguments-as-vba-arrays.html

    Rem 5)'Produce Output Filees if necerssary and Paste Data
    'wbMain.Save 'Just in case anything goes wrong after the next line which temporarily sremoves all Input Data
    rngDataIn.ClearContents 'Full data is removed temporarily to prevent copying lots of data unecerssary when usin .Copy "One Liner to produce new "Vertical" File
    Dim strMyDrive As String: Let strMyDrive = Left(strDefpath, 2) ', the drive (  C:   E:   etc   )
    ChDrive (strMyDrive): ChDir (strDefpath & strFoldername) 'Changing the Drive and Directory may be needed for check of Filename to work
    Dim strFilename As String: Let strFilename = Eunuch(Ceunt) & " Monthly Data.xlsx" 'New Montly Filename given as that for the considered Unique Look Up Croteria in this Loop
    Dim strFullPathname As String: Let strFullPathname = strDefpath & strFoldername & Application.PathSeparator & strFilename
        If Dir(strFullPathname) <> "" Then 'case File exists               'Dir(CurDir & Application.PathSeparator & strFoldername) <> "" Then
        Workbooks.Open FileName:=strFullPathname: ActiveWorkbook.Activate 'Open existing File and Activate it                                                                                                               'MsgBox "File is present"
        Set rngExData = ActiveWorkbook.Worksheets("Sheet1").Range("A1").CurrentRegion 'CurrentRegion Property acting on a Range Object returns a new range of that in a "box2 encompassing all cells "connected" to The original range object
            With rngExData
            Set rngExData = .Offset(1, 0).Resize(.Rows.Count - .Row + 1 - 1, .Columns.Count - .Column - 1) 'the Offset Property returned a Range Object shifted 1 row down, and the resize property further retuned a range object of one row smaller, so as not to remove haedings
            rngExData.ClearContents 'Clear data ready for new data' cannot use .Clear here as that will work on the original defined With rngExData
            End With 'rngExData

        Else 'Add Workbook        'Workbooks.Add:ActiveWorkbook.SaveAs Filename:=strFullPathname ', FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        '5b All wanted sheets are copied in one go.
        Dim Flag As Boolean: Let Flag = True 'Flag for first time around, whether to replace or add to selection
            For Each ws In ThisWorkbook.Worksheets 'Going through all worksheets
                If ws.Name = "Sheet1" Or ws.Name = "AnyOtherToCopy" Or ws.Name = "SpareSheet" Then 'Any Sheets to be considered by the following code lines should be written in here
                ws.Select Replace:=Flag: Let Flag = False 'Select with argument False results in last selection remaing as part of the selection, so we effectively select all sheets we want at once.
                Else: End If 'Case sheet not to be copied. Do nothing. redundant Code
                Next ws
            ActiveWindow.SelectedSheets.Copy 'VBA works such that applying .Copy to a sheet or, as here, sheets results in a copy of ttis workbook coming up in a window with the next avauilable default name. Fro now on we can refferenc this as the Active Workbook
            ActiveSheet.Select Replace:=False 'Doesn't appear necerssary, but WTF, probably good practice.
            End If
            '5c)The filtered Data from Sheet1 in the Master File is placed in Sheet1 of Workbook in this file
                With ActiveWorkbook 'The master sheet below must be by its name referrenced and not confused with that in the Master File, wsMain
                    If UBound(rws(), 1) > 1 Then 'Case more than one row with a Unique Look Up criteria relulting in arrOut being 2 Dimensional
                    .Worksheets("Sheet1").Range("A2").Resize(UBound(arrOut(), 1), UBound(arrOut(), 2)).Value = arrOut()  'A nice "One" liner - Resize selected cell to size of output Array and then the allowed VBA assignment of a collection of values to a Spreadsheet range
                    Else 'Case only one Row found , arrOut() becomes a 1 dimensional Array,
                    .Worksheets("Sheet1").Range("A2").Resize(, UBound(arrOut())).Value = arrOut()
                    End If
                Application.DisplayAlerts = False: .SaveAs FileName:=strFullPathname: Application.DisplayAlerts = True 'We turn off being alerted ( in this case that we will lose our macros for a .xlsx save): save the file : Switch the Alert object back on
                .Close (1)
                End With 'ActiveWorkbook

        rngDataIn.Value = arrin() 'Put back in data in Master sheet
   
        Next Ceunt 'Main loop for all Worksheets===============================================END MAIN LOOP======
   
TheEnd:     'Put all things here which shouls be done even in the case of an error.
    Application.ScreenUpdating = True 'If this is not done the screen may be "dead" after the code finishes
    Application.DisplayAlerts = True
    Set objWSHShell = Nothing 'May not be necessary, but good practice
End Sub 'MjozaWorkbookToWorkbooks()

Please let me know how you get on

Alan


Hi guys,

Thank you so much for providing this code. I have learned a lot from from both of you and really appreciate it.

Alan
Can you please help me with the following changes to your code.
-Some of the files in the complete folder will be blank except for the header. Can you please make it so that the data is still pasted even if there is no data in the file.
-After the data is copied, can you please change it to make it so that what was in column A is deleted. So column A in the completed files will be blank.

Thank you again.

Mjo
 
Upvote 0
Hi Mjo,
I am away from y main computer for a while, so am working / guessing “blind a bit”.

.......
-Some of the files in the complete folder will be blank except for the header. Can you please make it so that the data is still pasted even if there is no data in the file.....
_ . I think in this situation my current code would error trying to create a Range to clear the does not exist, hence no Data is copied. I think changing this


Code:
            With rngExData
            Set rngExData = .Offset(1, 0).Resize(.Rows.Count - .Row + 1 - 1, .Columns.Count - .Column - 1) 'the Offset Property returned a Range Object shifted 1 row down, and the resize property further retuned a range object of one row smaller, so as not to remove haedings
            rngExData.ClearContents 'Clear data ready for new data' cannot use .Clear here as that will work on the original defined With rngExData
            End With 'rngExData

To this may help solve that problem

Code:
            If rngExData.Rows.Count > 1 Then 'Case there is some data already there
                With rngExData
                Set rngExData = .Offset(1, 0).Resize(.Rows.Count - .Row + 1 - 1, .Columns.Count - .Column - 1) 'the Offset Property returned a Range Object shifted 1 row down, and the resize property further retuned a range object of one row smaller, so as not to remove haedings
                rngExData.ClearContents 'Clear data ready for new data' cannot use .Clear here as that will work on the original defined With rngExData
                End With 'rngExData
            Else 'No Data, just headings, so do nothing. Redundant code
            End If

-After the data is copied, can you please change it to make it so that what was in column A is deleted. So column A in the completed files will be blank.....
This would have easily achieved if you never had a column A. This would mean rewriting the code a bit, which is difficult for me just now. A simple modification for the current code would be adding

Code:
                .Worksheets("Sheet1").Columns(1).Delete

Just before the current workbook is saved

So here is that code line again shown with the next line which saves the current Workbook.

Code:
                .Worksheets("Sheet1").Columns(1).Delete 'Delete first Column in Corrent Workbook
                Application.DisplayAlerts = False: .SaveAs FileName:=strFullPathname: Application.DisplayAlerts = True 'We turn off being alerted ( in this case that we will lose our macros for a .xlsx save): save the file : Switch the Alert object back on
:::....

Here, i think, is the full modified code , ( but i suggest it would be good initially to try to understand and make the changes to the existing code you have )


Code:
Sub MjozaWorkbookToWorkbooks_ErrTestie() '    http://www.mrexcel.com/forum/excel-questions/899597-visual-basic-applications-loop-through-sheet-copy-contents-new-workbook.html     'http://www.excelforum.com/excel-programming-vba-macros/1101975-copy-the-data-from-master-file-and-paste-into-individual-excel-file-as-per-criteria.html
'ActiveWorkbook.Save
Dim strName As String: Let strName = ThisWorkbook.Name
Application.ScreenUpdating = False 'This is not too important but can speed things up a bit by disabling the updating of the screen which otherwise frequently happens
On Error GoTo TheEnd 'In the case of an Error goto a section of code at the end of the program
Rem 1) Initial sheet info, Importantly Copying entire data from Sheet1 to an Array.
                                              'Dim vTemp As Variant 'Temporary variable used in debugging
Dim wsMain As Worksheet, wbMain As Workbook 'Give Abbreviation Methods, Properties of Object Worksheet, Workbook through .dot
'Set wbMain = ThisWorkbook
Set wbMain = Workbooks("ViskasVerticalsMaster dataMjozaWarPig")
'Set wbMain = Workbooks("ViskasVerticalsMaster dataMjozaWarPig.xlsm")
Set wsMain = wbMain.Worksheets.Item(1) 'This code is in here, This Workbook
Let wsMain.Range("A10").Value = "ErrectionTestie"
Dim rm As Long, cm As Long, lmr As Long, lmc As Long 'Variable for Rows, Columns, last Row last Column of Main sheet. Long is a Big whole Number limit (-2,147,483,648 to 2,147,483,647) If you need some sort of validation the value should only be within the range of a Byte/Integer otherwise there's no point using anything but Long.--upon/after 32-bit, Integers (Short) need converted internally anyways, so a Long is actually faster.
Dim vLkUpc As Long: Let vLkUpc = 1 'set column number 'Column where search criteria for filtering is. '
Let lmc = wsMain.Cells(1, Columns.Count).End(xlToLeft).Column 'For this sheet the Range Object last cell in Header row, has the End porperty (argument XL to the left) applied returning the first Range object ( cell ) seen "looking" to the left. This further has the Column property applied which returns the column indicie for that cell
Let lmr = wsMain.Cells(Rows.Count, vLkUpc).End(xlUp).Row 'The Range Object ( cell ) that is the last cell  in the column of interest has the property .End ( argument Xl up ) applied to it. This returns a new range ( cell ) which is that of the first Range ( cell ) with something in it "looking up" the XL spreadsheet from the last cell. Then the .Row Property is applied to return a long number equal to the row number of that cell. ( +1 would give the next free cell )
Dim rngDataIn As Range: Set rngDataIn = wsMain.Range("A2", wsMain.Cells(lmr, lmc)) 'This is our full Master data range
Dim arrin() As Variant: Let arrin() = rngDataIn.Value 'Allowed VBA One Liner - An Array of variants may be set to a collection of Range values. The Range object works as to return a collection of (Variants initially) of various types. So Initially must see an Array of Variant types for compatability

Rem 2) If necessary, a Folder "Completed" is created on the Desktop
Dim strFoldername As String, strDefpath As String 'String variables for the relavent parts of Folder Path
Dim objWSHShell As Object 'I use late binding below for sharing so as not to hve to --Tools --References  --Scroll down and tick Microsoft Scripting Runtime library
Set objWSHShell = CreateObject("WScript.Shell") 'Creat a specific instance of WshShell is  an Object that enables you to query and interact with various aspects of the Windows User Interface.
Dim GetDesktop As String: Let GetDesktop = objWSHShell.SpecialFolders("Desktop") 'This property of the WshShell gives the string path to the desktop
Let strDefpath = GetDesktop & "\" 'Default Path as That of where this code is
Let strFoldername = "Completed"
    If Len(Dir("" & (strDefpath & strFoldername) & "", vbDirectory)) = 0 Then 'If the directory does not exist then...
    MkDir (strDefpath & strFoldername) '...make it
    Else 'The Directory ( Folder )  is presumably already there so do not make it again! Redundant code
    End If

Rem 3) data in Look Up Column is considered and eunique Array ( eunuch() ) produced from that
Dim vLkUp() As Variant 'Dynamic one dimensional array for LookUpColumn
Let vLkUp() = Application.WorksheetFunction.Index(arrin(), 0, vLkUpc) 'Returns format type (1,1) (2,1) (3,1) (4,1)... >> Index Function with second argument ("row" co - ordinate) set to 0 will return the entire "column" given by third argument ( "column" - co ordinate ), applied to the first argument which is the grid, ( Array , Row_Number, Column_Number)  http://www.excelforum.com/excel-new-users-basics/1080634-vba-1-dimensional-horizontal-and-vertical-array-conventions-ha-1-2-3-4-a.html   https://usefulgyaan.wordpress.com/2013/06/12/vba-trick-of-the-week-slicing-an-array-without-loop-application-index/
Dim Eunuch() As String 'Array used for unique LookUpColumn values to be used as both Workbook Names and Search Criteria
ReDim Eunuch(1 To 1) 'We need to size it initially, but it must be a dynamic Array to allow us to increase it's size, hence not done by Dim(1 to 1) which would make it a non dynamic Array
Let Eunuch(1) = "Uniques" 'We do not use the first element of the Array, so just for fun put a Title in it
    For rm = 1 To UBound(vLkUp(), 1) Step 1 'for each "row" in the Look Up Column ( we want to look at the values there )
        If IsError(Application.Match(vLkUp(rm, 1), Eunuch(), 0)) Then 'Match would error if the Unique value was not in the Array for those unique values, in which case ...
        ReDim Preserve Eunuch(1 To UBound(Eunuch()) + 1): Let Eunuch(UBound(Eunuch())) = vLkUp(rm, 1) '.. if not there then put it in!
        Else: End If 'case a unique value already there for this "row"
    Next rm

Rem 4) Main Outer loop for going through Input Array and copying data to workbooks ( Workbooks are creatde inf they do not exist, based on the unique search criteria
Dim Ceunt As Long: Let Ceunt = 0 'Loop Bound variable Count for "vertical" workbooks
Dim ws As Worksheet 'Temporary Worksheet variable for use when adressing File sheets through an inner loop
Dim rngExData As Range 'Temporary range object variable for use when addressing existing data range in an existing file
Dim clms() As Variant 'Array fo Indicies are obtained for the "columns" required in each "vertical" workbook is done just outside the loop as they are the same for all "Verticals". Variant type required to satisfy the type returned by the Evaluate method below
Let clms() = Evaluate("column(A:" & Left(Replace(Cells(1, UBound(arrin(), 2)).Address, "$", "", , 1), (InStr(Replace(Cells(1, UBound(arrin(), 2)).Address, "$", "", , 1), "$") - 1)) & ")") 'Not as complicated as it looks! - The messy bit just gets the Letter part out of the last cell Address. Then the worksheet Function Columns is used:  Returns a 1 D "quasi" horizontal array of size _: to :_  In that array are the number _:   to :_  .  This "quasi horizontal" Array we require for out "magic" code line
    For Ceunt = 2 To UBound(Eunuch()) 'we consider all th relavent unique "Verticals"==========MAIN LOOP=======
    '4a) Indicies are obtained for the "rows" required in each "vertical" workbook
    Dim rwsT() As Long: ReDim rwsT(1 To 1) 'Temporary Array for the indicies of required "rows"
        For rm = 1 To UBound(vLkUp(), 1) Step 1 'for each "row" in the Look Up Column ( we want to look at the values there )
            If Eunuch(Ceunt) = vLkUp(rm, 1) Then 'this would be the case of a "row" inidie we want
            ReDim Preserve rwsT(1 To UBound(rwsT()) + 1): Let rwsT(UBound(rwsT())) = rm 'required indicie is put in the next "along" space in the Temporary 1 D Array
            Else: End If '
        Next rm
    Dim rws() As Long: ReDim rws(1 To UBound(rwsT()) - 1, 1 To 1) 'Our Indicie Array, rws(), will be 1 less than the rwsT as rwsT(1) was not used and is not needed
        For rm = 2 To UBound(rwsT()) 'for all the indicies in the Tempory Array..
        Let rws((rm - 1), 1) = rwsT(rm) '..put in the indicie
        Next rm

    '4b) An Output Array for the the Workbook of this loop
    Dim arrOut() As Variant: Let arrOut() = Application.Index(arrin(), rws(), clms()) 'This "magic" I still do not understand  http://www.excelforum.com/excel-new-users-basics/1099995-application-index-with-look-up-rows-and-columns-arguments-as-vba-arrays.html

    Rem 5)'Produce Output Filees if necerssary and Paste Data
    'wbMain.Save 'Just in case anything goes wrong after the next line which temporarily sremoves all Input Data
    rngDataIn.ClearContents 'Full data is removed temporarily to prevent copying lots of data unecerssary when usin .Copy "One Liner to produce new "Vertical" File
    Dim strMyDrive As String: Let strMyDrive = Left(strDefpath, 2) ', the drive (  C:   E:   etc   )
    ChDrive (strMyDrive): ChDir (strDefpath & strFoldername) 'Changing the Drive and Directory may be needed for check of Filename to work
    Dim strFilename As String: Let strFilename = Eunuch(Ceunt) & " Monthly Data.xlsx" 'New Montly Filename given as that for the considered Unique Look Up Croteria in this Loop
    Dim strFullPathname As String: Let strFullPathname = strDefpath & strFoldername & Application.PathSeparator & strFilename
        If Dir(strFullPathname) <> "" Then 'case File exists               'Dir(CurDir & Application.PathSeparator & strFoldername) <> "" Then
        Workbooks.Open FileName:=strFullPathname: ActiveWorkbook.Activate 'Open existing File and Activate it                                                                                                               'MsgBox "File is present"
        Set rngExData = ActiveWorkbook.Worksheets("Sheet1").Range("A1").CurrentRegion 'CurrentRegion Property acting on a Range Object returns a new range of that in a "box2 encompassing all cells "connected" to The original range object
            With rngExData
            Set rngExData = .Offset(1, 0).Resize(.Rows.Count - .Row + 1 - 1, .Columns.Count - .Column - 1) 'the Offset Property returned a Range Object shifted 1 row down, and the resize property further retuned a range object of one row smaller, so as not to remove haedings
            rngExData.ClearContents 'Clear data ready for new data' cannot use .Clear here as that will work on the original defined With rngExData
            End With 'rngExData

        Else 'Add Workbook        'Workbooks.Add:ActiveWorkbook.SaveAs Filename:=strFullPathname ', FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        '5b All wanted sheets are copied in one go.
        Dim Flag As Boolean: Let Flag = True 'Flag for first time around, whether to replace or add to selection
            For Each ws In ThisWorkbook.Worksheets 'Going through all worksheets
                If ws.Name = "Sheet1" Or ws.Name = "AnyOtherToCopy" Or ws.Name = "SpareSheet" Then 'Any Sheets to be considered by the following code lines should be written in here
                ws.Select Replace:=Flag: Let Flag = False 'Select with argument False results in last selection remaing as part of the selection, so we effectively select all sheets we want at once.
                Else: End If 'Case sheet not to be copied. Do nothing. redundant Code
                Next ws
            ActiveWindow.SelectedSheets.Copy 'VBA works such that applying .Copy to a sheet or, as here, sheets results in a copy of ttis workbook coming up in a window with the next avauilable default name. Fro now on we can refferenc this as the Active Workbook
            ActiveSheet.Select Replace:=False 'Doesn't appear necerssary, but WTF, probably good practice.
            End If
            '5c)The filtered Data from Sheet1 in the Master File is placed in Sheet1 of Workbook in this file
                With ActiveWorkbook 'The master sheet below must be by its name referrenced and not confused with that in the Master File, wsMain
                    If UBound(rws(), 1) > 1 Then 'Case more than one row with a Unique Look Up criteria relulting in arrOut being 2 Dimensional
                    .Worksheets("Sheet1").Range("A2").Resize(UBound(arrOut(), 1), UBound(arrOut(), 2)).Value = arrOut()  'A nice "One" liner - Resize selected cell to size of output Array and then the allowed VBA assignment of a collection of values to a Spreadsheet range
                    Else 'Case only one Row found , arrOut() becomes a 1 dimensional Array,
                    .Worksheets("Sheet1").Range("A2").Resize(, UBound(arrOut())).Value = arrOut()
                    End If
                Application.DisplayAlerts = False: .SaveAs FileName:=strFullPathname: Application.DisplayAlerts = True 'We turn off being alerted ( in this case that we will lose our macros for a .xlsx save): save the file : Switch the Alert object back on
                .Close (1)
                End With 'ActiveWorkbook

        rngDataIn.Value = arrin() 'Put back in data in Master sheet
   
        Next Ceunt 'Main loop for all Worksheets===============================================END MAIN LOOP======
   
TheEnd:     'Put all things here which shouls be done even in the case of an error.
rngDataIn.Value = arrin()
Application.ScreenUpdating = True 'If this is not done the screen may be "dead" after the code finishes
Application.DisplayAlerts = True
Set objWSHShell = Nothing 'May not be necerssary, but good practice
End Sub 'MjozaWorkbookToWorkbooks()
'





I hope that helps. Let me know how you get on. It may be difficult for me to help for a while as i am away now for a few weeks

Alan
 
Last edited:
Upvote 0
Hello WarPigl3t

I'd like to reuse your code for a similar task. I have several sheets in my Main workbook, which contains data for several companies. I need to copy the rows for each company from each sheet into a separate workbook (i.e. for Company "Gyp" I need to copy all information from all sheets into a separate workbook, column B contains the company names). I get Runtime error Subscript out of range on line:

ActiveSheet.Paste Destination:=Workbooks(newWB_Name).Sheets("Sheet1").Rows(headerRow)

and I can't figure out why... Any ideas?
 
Upvote 0
@ Mjo,
I just noticed the Full code I posted in Post #24 is the wrong one. So just ignor that and make the changes to the original Code I gave in Post #3
Alan
 
Upvote 0
@ Mjo,
I just noticed the Full code I posted in Post #24 is the wrong one. So just ignor that and make the changes to the original Code I gave in Post #3
Alan

Alan

Thanks for the response. I have been trying to make the changes that I requested and my efforts were fruitless and thought I should ask.

I can wait till you get the time but I will keep trying and if I figure it out I will report back.

Thank you.
 
Upvote 0
Alan

Thanks for the response. I have been trying to make the changes that I requested and my efforts were fruitless and thought I should ask.

I can wait till you get the time but I will keep trying and if I figure it out I will report back.

Thank you.
Hi Alan

i was able to figure it out.

thank you so much.
 
Upvote 0
....
thank you so much.
You's Welcome,
thanks for the feedback
Alan
BTW. here is the correct final code

Rich (BB code):
'
Sub MjozaWorkbookToWorkbooks() '    http://www.mrexcel.com/forum/excel-questions/899597-visual-basic-applications-loop-through-sheet-copy-contents-new-workbook.html     'http://www.excelforum.com/excel-programming-vba-macros/1101975-copy-the-data-from-master-file-and-paste-into-individual-excel-file-as-per-criteria.html
ActiveWorkbook.Save
Application.ScreenUpdating = False 'This is not too important but can speed things up a bit by disabling the updating of the screen which otherwise frequently happens
'On Error GoTo TheEnd 'In the case of an Error goto a section of code at the end of the program
Rem 1) Initial sheet info, Importantly Copying entire data from Sheet1 to an Array.
                                              'Dim vTemp As Variant 'Temporary variable used in debugging
Dim wsMain As Worksheet, wbMain As Workbook 'Give Abbreviation Methods, Properties of Object Worksheet, Workbook through .dot
Set wbMain = ThisWorkbook: Set wsMain = wbMain.Worksheets("Sheet1") 'This code is in here, This Workbook
Dim rm As Long, cm As Long, lmr As Long, lmc As Long 'Variable for Rows, Columns, last Row last Column of Main sheet. Long is a Big whole Number limit (-2,147,483,648 to 2,147,483,647) If you need some sort of validation the value should only be within the range of a Byte/Integer otherwise there's no point using anything but Long.--upon/after 32-bit, Integers (Short) need converted internally anyways, so a Long is actually faster.
Dim vLkUpc As Long: Let vLkUpc = 1 'set column number 'Column where search criteria for filtering is. '
Let lmc = wsMain.Cells(1, Columns.Count).End(xlToLeft).Column 'For this sheet the Range Object last cell in Header row, has the End porperty (argument XL to the left) applied returning the first Range object ( cell ) seen "looking" to the left. This further has the Column property applied which returns the column indicie for that cell
Let lmr = wsMain.Cells(Rows.Count, vLkUpc).End(xlUp).Row 'The Range Object ( cell ) that is the last cell  in the column of interest has the property .End ( argument Xl up ) applied to it. This returns a new range ( cell ) which is that of the first Range ( cell ) with something in it "looking up" the XL spreadsheet from the last cell. Then the .Row Property is applied to return a long number equal to the row number of that cell. ( +1 would give the next free cell )
Dim rngDataIn As Range: Set rngDataIn = wsMain.Range("A2", wsMain.Cells(lmr, lmc)) 'This is our full Master data range
Dim arrin() As Variant: Let arrin() = rngDataIn.Value 'Allowed VBA One Liner - An Array of variants may be set to a collection of Range values. The Range object works as to return a collection of (Variants initially) of various types. So Initially must see an Array of Variant types for compatability

Rem 2) If necessary, a Folder "Completed" is created on the Desktop
Dim strFoldername As String, strDefpath As String 'String variables for the relavent parts of Folder Path
Dim objWSHShell As Object 'I use late binding below for sharing so as not to hve to --Tools --References  --Scroll down and tick Microsoft Scripting Runtime library
Set objWSHShell = CreateObject("WScript.Shell") 'Creat a specific instance of WshShell is  an Object that enables you to query and interact with various aspects of the Windows User Interface.
Dim GetDesktop As String: Let GetDesktop = objWSHShell.SpecialFolders("Desktop") 'This property of the WshShell gives the string path to the desktop
Let strDefpath = GetDesktop & "\" 'Default Path as That of where this code is
Let strFoldername = "Completed"
    If Len(Dir("" & (strDefpath & strFoldername) & "", vbDirectory)) = 0 Then 'If the directory does not exist then...
    MkDir (strDefpath & strFoldername) '...make it
    Else 'The Directory ( Folder )  is presumably already there so do not make it again! Redundant code
    End If

Rem 3) data in Look Up Column is considered and eunique Array ( eunuch() ) produced from that
Dim vLkUp() As Variant 'Dynamic one dimensional array for LookUpColumn
Let vLkUp() = Application.WorksheetFunction.Index(arrin(), 0, vLkUpc) 'Returns format type (1,1) (2,1) (3,1) (4,1)... >> Index Function with second argument ("row" co - ordinate) set to 0 will return the entire "column" given by third argument ( "column" - co ordinate ), applied to the first argument which is the grid, ( Array , Row_Number, Column_Number)  http://www.excelforum.com/excel-new-users-basics/1080634-vba-1-dimensional-horizontal-and-vertical-array-conventions-ha-1-2-3-4-a.html   https://usefulgyaan.wordpress.com/2013/06/12/vba-trick-of-the-week-slicing-an-array-without-loop-application-index/
Dim Eunuch() As String 'Array used for unique LookUpColumn values to be used as both Workbook Names and Search Criteria
ReDim Eunuch(1 To 1) 'We need to size it initially, but it must be a dynamic Array to allow us to increase it's size, hence not done by Dim(1 to 1) which would make it a non dynamic Array
Let Eunuch(1) = "Uniques" 'We do not use the first element of the Array, so just for fun put a Title in it
    For rm = 1 To UBound(vLkUp(), 1) Step 1 'for each "row" in the Look Up Column ( we want to look at the values there )
        If IsError(Application.Match(vLkUp(rm, 1), Eunuch(), 0)) Then 'Match would error if the Unique value was not in the Array for those unique values, in which case ...
        ReDim Preserve Eunuch(1 To UBound(Eunuch()) + 1): Let Eunuch(UBound(Eunuch())) = vLkUp(rm, 1) '.. if not there then put it in!
        Else: End If 'case a unique value already there for this "row"
    Next rm

Rem 4) Main Outer loop for going through Input Array and copying data to workbooks ( Workbooks are creatde inf they do not exist, based on the unique search criteria
Dim Ceunt As Long: Let Ceunt = 0 'Loop Bound variable Count for "vertical" workbooks
Dim ws As Worksheet 'Temporary Worksheet variable for use when adressing File sheets through an inner loop
Dim rngExData As Range 'Temporary range object variable for use when addressing existing data range in an existing file
Dim clms() As Variant 'Array fo Indicies are obtained for the "columns" required in each "vertical" workbook is done just outside the loop as they are the same for all "Verticals". Variant type required to satisfy the type returned by the Evaluate method below
Let clms() = Evaluate("column(A:" & Left(Replace(Cells(1, UBound(arrin(), 2)).Address, "$", "", , 1), (InStr(Replace(Cells(1, UBound(arrin(), 2)).Address, "$", "", , 1), "$") - 1)) & ")") 'Not as complicated as it looks! - The messy bit just gets the Letter part out of the last cell Address. Then the worksheet Function Columns is used:  Returns a 1 D "quasi" horizontal array of size _: to :_  In that array are the number _:   to :_  .  This "quasi horizontal" Array we require for out "magic" code line
    For Ceunt = 2 To UBound(Eunuch()) 'we consider all th relavent unique "Verticals"==========MAIN LOOP=======
    '4a) Indicies are obtained for the "rows" required in each "vertical" workbook
    Dim rwsT() As Long: ReDim rwsT(1 To 1) 'Temporary Array for the indicies of required "rows"
        For rm = 1 To UBound(vLkUp(), 1) Step 1 'for each "row" in the Look Up Column ( we want to look at the values there )
            If Eunuch(Ceunt) = vLkUp(rm, 1) Then 'this would be the case of a "row" inidie we want
            ReDim Preserve rwsT(1 To UBound(rwsT()) + 1): Let rwsT(UBound(rwsT())) = rm 'required indicie is put in the next "along" space in the Temporary 1 D Array
            Else: End If '
        Next rm
    Dim rws() As Long: ReDim rws(1 To UBound(rwsT()) - 1, 1 To 1) 'Our Indicie Array, rws(), will be 1 less than the rwsT as rwsT(1) was not used and is not needed
        For rm = 2 To UBound(rwsT()) 'for all the indicies in the Tempory Array..
        Let rws((rm - 1), 1) = rwsT(rm) '..put in the indicie
        Next rm

    '4b) An Output Array for the the Workbook of this loop
    Dim arrOut() As Variant: Let arrOut() = Application.Index(arrin(), rws(), clms()) 'This "magic" I still do not understand  http://www.excelforum.com/excel-new-users-basics/1099995-application-index-with-look-up-rows-and-columns-arguments-as-vba-arrays.html

    Rem 5)'Produce Output Filees if necerssary and Paste Data
    'wbMain.Save 'Just in case anything goes wrong after the next line which temporarily sremoves all Input Data
'    rngDataIn.ClearContents 'Full data is removed temporarily to prevent copying lots of data unecerssary when using .Copy "One Liner to produce new "Vertical" File
    Dim strMyDrive As String: Let strMyDrive = Left(strDefpath, 2) ', the drive (  C:   E:   etc   )
    ChDrive (strMyDrive): ChDir (strDefpath & strFoldername) 'Changing the Drive and Directory may be needed for check of Filename to work
    Dim strFilename As String: Let strFilename = Eunuch(Ceunt) & " Monthly Data.xlsx" 'New Montly Filename given as that for the considered Unique Look Up Croteria in this Loop
    Dim strFullPathname As String: Let strFullPathname = strDefpath & strFoldername & Application.PathSeparator & strFilename
        If Dir(strFullPathname) <> "" Then 'case File exists               'Dir(CurDir & Application.PathSeparator & strFoldername) <> "" Then
        Workbooks.Open FileName:=strFullPathname: ActiveWorkbook.Activate 'Open existing File and Activate it                                                                                                               'MsgBox "File is present"
        Set rngExData = ActiveWorkbook.Worksheets("Sheet1").Range("A1").CurrentRegion 'CurrentRegion Property acting on a Range Object returns a new range of that in a "box" encompassing all cells "connected" to The original range object
            If rngExData.Rows.Count > 1 Then 'Case there is some data already there
                With rngExData
                Set rngExData = .Offset(1, 0).Resize(.Rows.Count - .Row + 1 - 1, .Columns.Count - .Column - 1) 'the Offset Property returned a Range Object shifted 1 row down, and the resize property further retuned a range object of one row smaller, so as not to remove haedings
                rngExData.ClearContents 'Clear data ready for new data' cannot use .Clear here as that will work on the original defined With rngExData
                End With 'rngExData
            Else 'No Data, just headings, so do nothing. Redundant code
            End If

        Else 'Add Workbook        'Workbooks.Add:ActiveWorkbook.SaveAs Filename:=strFullPathname ', FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        '5b All wanted sheets are copied in one go.
        Dim Flag As Boolean: Let Flag = True 'Flag for first time around, whether to replace or add to selection
            For Each ws In ThisWorkbook.Worksheets 'Going through all worksheets
                If ws.Name = "Sheet1" Or ws.Name = "AnyOtherToCopy" Or ws.Name = "SpareSheet" Then 'Any Sheets to be considered by the following code lines should be written in here
                ws.Select Replace:=Flag: Let Flag = False 'Select with argument False results in last selection remaing as part of the selection, so we effectively select all sheets we want at once.
                Else: End If 'Case sheet not to be copied. Do nothing. redundant Code
                Next ws
            ActiveWindow.SelectedSheets.Copy 'VBA works such that applying .Copy to a sheet or, as here, sheets results in a copy of ttis workbook coming up in a window with the next avauilable default name. Fro now on we can refferenc this as the Active Workbook
            ActiveSheet.Select Replace:=False 'Doesn't appear necerssary, but WTF, probably good practice.
            End If
            '5c)The filtered Data from Sheet1 in the Master File is placed in Sheet1 of Workbook in this file
                With ActiveWorkbook 'The master sheet below must be by its name referrenced and not confused with that in the Master File, wsMain
                    If UBound(rws(), 1) > 1 Then 'Case more than one row with a Unique Look Up criteria relulting in arrOut being 2 Dimensional
                    .Worksheets("Sheet1").Range("A2").Resize(UBound(arrOut(), 1), UBound(arrOut(), 2)).Value = arrOut()  'A nice "One" liner - Resize selected cell to size of output Array and then the allowed VBA assignment of a collection of values to a Spreadsheet range
                    Else 'Case only one Row found , arrOut() becomes a 1 dimensional Array,
                    .Worksheets("Sheet1").Range("A2").Resize(, UBound(arrOut())).Value = arrOut()
                    End If
                .Worksheets("Sheet1").Columns(1).Delete 'Delete first Column in Corrent Workbook
                Application.DisplayAlerts = False: .SaveAs FileName:=strFullPathname: Application.DisplayAlerts = True 'We turn off being alerted ( in this case that we will lose our macros for a .xlsx save): save the file : Switch the Alert object back on
                .Close (1)
                End With 'ActiveWorkbook

        rngDataIn.Value = arrin() 'Put back in data in Master sheet
   
        Next Ceunt 'Main loop for all Worksheets===============================================END MAIN LOOP======
   
TheEnd:     'Put all things here which shouls be done even in the case of an error.
    Application.ScreenUpdating = True 'If this is not done the screen may be "dead" after the code finishes
    Application.DisplayAlerts = True
    Set objWSHShell = Nothing 'May not be necessary, but good practice
End Sub 'MjozaWorkbookToWorkbooks()
 
Upvote 0

Forum statistics

Threads
1,223,277
Messages
6,171,147
Members
452,382
Latest member
RonChand

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top