My current macro takes my data row by row from workbook A or worksheet A and splits it into different sheets based on matching headers. I am having trouble taking it a step further and splitting the string fields among these sheets.
For example, my data in workbook A, column B, contains 10 unique strings, how can I sort string x to one sheet only, and strings the rest of them to other sheets. So the row that contains sheet x will go to a certain sheet and strings abc will act as normal.
Here is my code so far, specifically call out the workbook and sheet names so it is not dynamic:
Option Explicit
Sub main()
Dim dsRng As Range
Dim sht As Worksheet
Dim AShtColsList As String, BShtColsList As String
Set dsRng = Workbooks("Workbook A").Worksheets("Sample Extract").Range("A1").CurrentRegion '<--| set your entire data set range in workbook "A" worksheet "ShtA" (change "A" and "ShtA" to your actual names)
dsRng.Sort key1:=dsRng.Range("A1"), order1:=xlAscending, Header:=xlYes '<--| sort data set range on its 1st column (which is "A", beginning it from column "A")
With Workbooks("Workbook B") '<--| refer "B" workbook
For Each sht In .Worksheets(Array("Stack", "Documentation", "Users")) '<--| loop through its worksheets
GetCorrespondingColumns dsRng, sht, AShtColsList, BShtColsList '<--| build lists of corresponding columns indexes in both workbooks
CopyColumns dsRng, sht, AShtColsList, BShtColsList '<--| copy listed columns between workbooks
Next sht
End With
End Sub
Sub GetCorrespondingColumns(dsRng As Range, sht As Worksheet, AShtColsList As String, BShtColsList As String)
Dim f As Range, c As Range
Dim iElem As Long
AShtColsList = "" '<--| initialize workbook "A" columns indexes list
BShtColsList = "" '<--| initialize workbook "B" current sheet columns indexes list
For Each c In sht.Rows(2).SpecialCells(xlCellTypeConstants, xlTextValues) '<--| loop through workbook "B" current sheet headers in row 2 *******
Set f = dsRng.Rows(1).Find(what:=c.Value, lookat:=xlWhole, LookIn:=xlValues) '<--| look up data set headers row for workbook "B" current sheet current column header
If Not f Is Nothing Then '<--| if it's been found ...
BShtColsList = BShtColsList & c.Column & "," '<--| ...update workbook "B" current sheet columns list with current header column index
AShtColsList = AShtColsList & f.Column & "," '<--| ...update workbook "A" columns list with corresponding found header column index
End If
Next c
End Sub
Sub CopyColumns(dsRng As Range, sht As Worksheet, AShtColsList As String, BShtColsList As String)
Dim iElem As Long
Dim AShtColsArr As Variant, BShtColsArr As Variant
If AShtColsList <> "" Then '<--| if any workbook "B" current sheet header has been found in workbook "A" data set headers
BShtColsArr = Split(Left(BShtColsList, Len(BShtColsList) - 1), ",") '<--| build an array out of workbook "B" current sheet columns indexes list
AShtColsArr = Split(Left(AShtColsList, Len(AShtColsList) - 1), ",") '<--| build an array out of workbook "A" corresponding columns indexes list
For iElem = 0 To UBound(AShtColsArr) '<--| loop through workbook "A" columns indexes array (you could have used workbook "A" corresponding columns indexes list as well)
Intersect(dsRng, dsRng.Columns(CLng(AShtColsArr(iElem)))).Copy sht.Cells(2, CLng(BShtColsArr(iElem))) '<--| copy data set current column into workbook "B" current sheet corresponding column starting from row 2 *******
Next iElem
End If
End Sub
Thanks.
EDIT
Complete extract. Call this Sample extract in workbook B.
'Users' Sheet. My Macro already does this.
'Documentation' Sheet, my macro already does this too
'Stack' Sheet. my macro does not do this. It filtered the record stackoverflow and its pertaining columns.
Hopefully this helps.
get your data saved in sheet named "data". and below code will generate separate sheets for every unique value in column B with data of corresponding value.
Dim data, sht As Worksheet
Dim rng As Range
Dim counter As Long
Set data = ThisWorkbook.Sheets("data")
data.Activate
Range("B:B").Copy
Range("H:H").PasteSpecial xlPasteValues
Range("H:H").RemoveDuplicates Columns:=1, Header:=xlYes
Set rng = data.Range("H2")
Do While rng.Value <> ""
Set sht = ThisWorkbook.Worksheets.Add
sht.Name = rng.Value
data.Activate
ActiveSheet.AutoFilterMode = False
Range("A1").AutoFilter field:=2, Criteria1:=rng.Value
Range("A1:C1").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.SpecialCells(xlVisible).Copy
sht.Activate
Range("A1").PasteSpecial xlPasteValues
Range("A1").Activate
Set rng = rng.Offset(1, 0)
Loop
It will create sheets in same workbook.
Related
Just started learning VBA today to try to make life a bit easier at my new job. I'm essentially trying to look for every instance where column E has the letter "a" copy and paste it into a newly created worksheet called "Aton" then delete the original rows with the "a"s.
I tried to modify the solution found here: VBA: Copy and paste entire row based on if then statement / loop and push to 3 new sheets
When I changed the above solution to make this line "If wsSrc.Cells(i, "E").Value = "a" Then" that's when I run into problems.
Sub Macro3()
'Need "Dim"
'Recommend "Long" rather than "Integer" for referring to rows and columns
'i As Integer
Dim i As Long
'Declare "Number"
Dim Number As Long
'Declare a variable to refer to the sheet you are going to copy from
Dim wsSrc As Worksheet
Set wsSrc = ActiveSheet
'Declare a variable to refer to the sheet you are going to copy to
Dim wsDest As Worksheet
'Declare three other worksheet variables for the three potential destinations
Dim wsEqualA As Worksheet
'Create the three sheets - do this once rather than in the loop
Set wsEqualA = Worksheets.Add(After:=Worksheets(Worksheets.Count))
'Assign the worksheet names
wsEqualA.Name = "Aton"
'Determine last row in source sheet
Number = wsSrc.Cells(wsSrc.Rows.Count, "C").End(xlUp).Row
For i = 1 To Number
'Determine which destination sheet to use
If wsSrc.Cells(i, "E").Value = "a" Then
Set wsDest = wsEqualA
Else
End If
'Copy the current row from the source sheet to the next available row on the
'destination sheet
With wsDest
wsSrc.Rows(i).Copy .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0)
End With
'Delete row if column E has an a
If wsSrc.Cells(i, "E").Value = "a" Then
Selection.EntireRow.Delete
Else
End If
Next i
End Sub
Sticking to your code, you have three issues
when deleting rows you have to loop backwards and avoid skipping rows
you’re copying and (trying to) deleting rows outside the ‘If wsSrc.Cells(i, "E").Value = "a"‘ block, hence regardless of current row “i” column E value
you don’t want to delete currently selected range rows, but currently loop “i” row
Putting it all together here’s the correct relevant snippet;
Set wsDest = wsEqualA 'set target sheet once and for all outside the loop
For i = Number To 1 Step -1 'Loop backwards
If wsSrc.Cells(i, "E").Value = "a" Then
'Copy the current row from the source sheet to the next available row on the destination sheet
With wsDest
wsSrc.Rows(i).Copy .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0) 'Copy wsSrc current “i” row and paste it to wsDest
wsSrc.Rows(i).Delete 'Delete wsSrc current “i” row
End With
End If
Next
As a possible enhancement, you could swap the sheets references in the “With...End With” block, since it’s more effective to reference the mostly “used” one:
With wsSrc
.Rows(i).Copy wsDest.Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0) 'Copy wsSrc current “i” row and paste it to wsDest
.Rows(i).Delete 'Delete wsSrc current “i” row
End With
You need to qualify which sheet the original values are on. Change Sheet on the line Set ws = ThisWorkbook.Sheets("Sheet1") to your sheet name.
Create new sheet and set objects
Create range to loop through, LoopRange (E2 down to last row in column)
Loop through LoopRange. If criteria is met, add the cell, MyCell, to a collection of cells (TargetRange)
If the TargetRange is not empty (meaning your criteria was met at least once) then copy header from ws to ns
Copy TargetRange from ws to ns
Delete TargetRange from ws
The benifit if using Union to collect cells is that you avoid many iterations of copy/paste/delete. If you have 50 cells in your range that meet your criteria, you will have 50 instance each for copy/paste/delete for a grand total of 150 actions.
Using the Union method, you will just have 1 instance for each action for a grand total of 3 actions which will boost run time.
Option Explicit
Sub Learning()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim ns As Worksheet: Set ns = Worksheets.Add(After:=(ThisWorkbook.Sheets.Count)) 'ns = new sheet
ns.Name = "Aton"
Dim LoopRange As Range, MyCell As Range, TargetRange As Range
Set LoopRange = ws.Range("E2:E" & ws.Range("E" & ws.Rows.Count).End(xlUp).Row)
For Each MyCell In LoopRange 'Loop through column E
If MyCell = "a" Then
If TargetRange Is Nothing Then 'If no range has been set yet
Set TargetRange = MyCell
Else 'If a range has already been set
Set TargetRange = Union(TargetRange, MyCell)
End If
End If
Next MyCell
Application.ScreenUpdating = False
If Not TargetRange Is Nothing Then 'Make sure you don't try to copy a empty range
ws.Range("A1").EntireRow.Copy ns.Range("A1") 'copy header from original sheet
TargetRange.EntireRow.Copy ns.Range("A2")
TargetRange.EntireRow.Delete
Else
MsgBox "No cells were found in Column E with value of 'a'"
End If
Application.ScreenUpdating = True
End Sub
First, don't use ActiveSheet, it can cause multiple problems. If sheet1 is not your source worksheet then change it to meet your needs. I prefer using a filter, as urdearboy suggested, which dosn't require a loop and is faster. I always try to keep the code simple, so try this...
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Aton"
With Sheet1.UsedRange
.AutoFilter Field:=5, Criteria1:="a", Operator:=xlFilterValues
.Offset(1).SpecialCells(xlCellTypeVisible).Copy Sheets("Aton").Range("A1")
.Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).EntireRow.Delete
.AutoFilter
End With
I'm currently developing a monitoring Tool in Excel using VBA and encountered some difficulties when copying data.
Current Code:
Sub CopyID()
'Copies entire Row of IDs from "Sheet 2" to main Sheet "Main Sheet"
Dim lastCell As Long
LastCell = Cells(Rows.Count,'Sheet 2':M).End(xlUp).Row
'Missing here: Copy to Column 1 at Row 3!
Sheets("Sheet 2").Columns(M).Copy Destination:=Sheets("Main Sheet").Columns(1)
End Sub
What it is supposed to do:
Copy the Data of Sheet2_Column M starting at Row 2
to
Main Sheet Colum A sarting at Row 3
Also, I don't know if this is possible yet, use a specific formula for the destination (Formular is: =LEFT(Data,10))
I am glad for any response to this as I'd like to learn how these "Copy Methods" work in detail and am happy for any tipps and tricks regarding these methods.
Edit//
The Copy Part should work like this
Sheet 2 Contains a Colum that has a headercell and X cells with a value that has a similar format.
Example of the Sheet 2 Contents
This is a row in Sheet 2. I only need the first 10 digits of the content of the cells. Is it possible to include that as a formula similar to
=Left(Sheet 2:M2,10)
so it works like this:
"sheet 2" cell content: "1234567891_1_123X" copy to "main sheet" as "1234567891"
Define your source and destination worksheet. And range/column names bust be submitted as strings like "M".
Sub CopyID()
'Copies entire Row of IDs from "Sheet 2" to main Sheet "Main Sheet"
Dim WsSource As Worksheet
Set WsSource = ThisWorkbook.Worksheets("Sheet 2")
Dim WsDestination As Worksheet
Set WsDestination = ThisWorkbook.Worksheets("Main Sheet")
Dim lastRow As Long
lastRow = WsSource.Cells(WsSource.Rows.Count, "M").End(xlUp).Row
'Missing here: Copy to Column 1 at Row 3!
WsSource.Range("M2:M" & lastRow).Copy Destination:=WsDestination.Range("A3")
End Sub
Edit:
To copy only the first 10 characters of each cell would need a process for each value:
Option Explicit
Public Sub CopyID()
'Copies entire Row of IDs from "Sheet 2" to main Sheet "Main Sheet"
Dim WsSource As Worksheet
Set WsSource = ThisWorkbook.Worksheets("Sheet 2")
Dim WsDestination As Worksheet
Set WsDestination = ThisWorkbook.Worksheets("Main Sheet")
Dim lastRow As Long
lastRow = WsSource.Cells(WsSource.Rows.Count, "M").End(xlUp).Row 'Find last row in column M
Dim ArrSource As Variant
ArrSource = WsSource.Range("M2:M" & lastRow).Value 'read column m values into array
Dim i As Long
For i = 1 To UBound(ArrSource) 'process each value in the array
ArrSource(i, 1) = Left$(ArrSource(i, 1), 10) 'keep only left 10 characters
Next i
WsDestination.Range("A3").Resize(UBound(ArrSource), 1).Value = ArrSource 'write array into destination
End Sub
Note .Resize(UBound(ArrSource), 1) defines the destination the same size as the array is that we want to insert.
I have workbook A with many columns and headers, I would like to separate this data and populate into workbook B based on header name(workbook B has 4 sheets of different pre populated column headers)
1) Workbook A (many columns), filter for all its unique values in col 'AN' (ie. col AN has 20 unique values but ~3000 rows each for each unique set).
2) There is workbook B, with pre populated columns in 4 sheets, not all are the same headers as in workbook A. Here is where the unique values from col AN from workbook A with their respective records will be populated, one after the other.
The goal here is to populate these 4 sheets with data from Workbook A, sorting by each unique column AN value, with its records into the prepopulated workbook B.
This code so far just filters my main 'AN' column uniquely and just gets unique values, I need unique values along with records.
Sub Sort()
Dim wb As Workbook, fileNames As Object, errCheck As Boolean
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
Dim lngLastNode As Long, lngLastScen As Long
' Finds column AN , header named 'first name'
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("First name", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColScenario), .Cells(lr, intColScenario)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
'I need to take the rest of the records with this though.
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.Visible = True
End With
End Sub
Adding sample pictures
Workbook A sample, I want to unique filter the 'job column' to get all like records together:
Workbook sample B,
Sheet 1 (note there will be multiple sheets).
As you can see workbook A has been sorted by the 'job' column.
you could use the following code:
edited to account for workbook "B" worksheets headers in row 2 (instead of row 1 as per OP example)
Option Explicit
Sub main()
Dim dsRng As Range
Dim sht As Worksheet
Dim AShtColsList As String, BShtColsList As String
Set dsRng = Workbooks("A").Worksheets("ShtA").Range("A1").CurrentRegion '<--| set your entire data set range in workbook "A" worksheet "ShtA" (change "A" and "ShtA" to your actual names)
dsRng.Sort key1:=dsRng.Range("AN1"), order1:=xlAscending, Header:=xlYes '<--| sort data set range on its 40th column (which is "AN", beginning it from column "A")
With Workbooks("B") '<--| refer "B" workbook
For Each sht In .Worksheets '<--| loop through its worksheets
GetCorrespondingColumns dsRng, sht, AShtColsList, BShtColsList '<--| build lists of corresponding columns indexes in both workbooks
CopyColumns dsRng, sht, AShtColsList, BShtColsList '<--| copy listed columns between workbooks
Next sht
End With
End Sub
Sub GetCorrespondingColumns(dsRng As Range, sht As Worksheet, AShtColsList As String, BShtColsList As String)
Dim f As Range, c As Range
Dim iElem As Long
AShtColsList = "" '<--| initialize workbook "A" columns indexes list
BShtColsList = "" '<--| initialize workbook "B" current sheet columns indexes list
For Each c In Sht.Rows(2).SpecialCells(xlCellTypeConstants, xlTextValues) '<--| loop through workbook "B" current sheet headers in row 2 *******
Set f = dsRng.Rows(1).Find(what:=c.value, lookat:=xlWhole, LookIn:=xlValues) '<--| look up data set headers row for workbook "B" current sheet current column header
If Not f Is Nothing Then '<--| if it's been found ...
BShtColsList = BShtColsList & c.Column & "," '<--| ...update workbook "B" current sheet columns list with current header column index
AShtColsList = AShtColsList & f.Column & "," '<--| ...update workbook "A" columns list with corresponding found header column index
End If
Next c
End Sub
Sub CopyColumns(dsRng As Range, sht As Worksheet, AShtColsList As String, BShtColsList As String)
Dim iElem As Long
Dim AShtColsArr As Variant, BShtColsArr As Variant
If AShtColsList <> "" Then '<--| if any workbook "B" current sheet header has been found in workbook "A" data set headers
BShtColsArr = Split(Left(BShtColsList, Len(BShtColsList) - 1), ",") '<--| build an array out of workbook "B" current sheet columns indexes list
AShtColsArr = Split(Left(AShtColsList, Len(AShtColsList) - 1), ",") '<--| build an array out of workbook "A" corresponding columns indexes list
For iElem = 0 To UBound(AShtColsArr) '<--| loop through workbook "A" columns indexes array (you could have used workbook "A" corresponding columns indexes list as well)
Intersect(dsRng, dsRng.Columns(CLng(AShtColsArr(iElem)))).Copy Sht.Cells(2, CLng(BShtColsArr(iElem))) '<--| copy data set current column into workbook "B" current sheet corresponding column starting from row 2 *******
Next iElem
End If
End Sub
and should really need to have each unique name rows set in workbook "B" sheets separated by a blank row, you can write a quite simple SubSeparateRowsSet() and call it right after CopyColumns() call in main()
I am quite new to Excel Macro and need some help to achieve the following task.
I have two sheets in the same workbook; one is the main sheet that can be edited, and the other one is to extract certain columns from the main sheet. Since the main sheet may have columns inserted or deleted, my approach is to input specific titles that I would extract to sheet 2 (blank sheet initially), look up/match these texts/column titles in sheet 1 (main sheet); then, copy the entire column under that match column title and paste it to sheet 2.
I have the following code, but errors still pop up. Since I am not quite familiar with Macro syntax, I am not so sure if this approach is applicable. I do appreciate any help, comment, or suggestion. Thanks in advance.
Private Sub CommandButton1_Click()
Dim i As Long
Dim j As Long
Dim r As Long
For i = 1 To 30
For j = 1 to 30
If Sheets(2).Cells(1, j).Value = Sheets(7).Cells(1, i).Value Then
For r = 2 To 1000
Sheets(2).Cells(r, j).Copy
Sheets(7).Cells(r, i).PasteSpecial Paste:=xlPasteFormats
Sheets(7).Cells(r, i).PasteSpecial Paste:=xlPasteValue
Next r
End If
Next j
Next i
End Sub
you'd better use Find() method of Range object to find values in a range
so you may want to use this code
Option Explicit
Private Sub CommandButton1_Click()
Dim f As Range, mainShtHeaderRng As Range, blankShtHeaderRng As Range, cell As Range
Dim mainSht As Worksheet, blankSht As Worksheet
Set mainSht = Worksheets("mainSht") '<--| set your "main" sheet
Set blankSht = Worksheets("blankSht") '<--| set your "blank" sheet
Set mainShtHeaderRng = mainSht.Rows(1).SpecialCells(XlCellType.xlCellTypeConstants, xlTextValues) '<--| set your header range in the "main" sheet
Set blankShtHeaderRng = blankSht.Rows(1).SpecialCells(XlCellType.xlCellTypeConstants, xlTextValues) '<--| set your header range in the "blank" sheet
For Each cell In blankShtHeaderRng '<--| loop through "blank" sheet headers...
Set f = mainShtHeaderRng.Find(what:=cell.Value, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False) '<--|... and search them between the "main" sheet headers
If Not f Is Nothing Then '<--| if found...
Range(f, mainSht.Cells(mainSht.Rows.Count, f.Column).End(xlUp)).Copy '<--| copy "main" sheet corresponding column doqwn to its last non empty cell...
cell.PasteSpecial Paste:=xlPasteFormats '<--| ... and paste formats...
cell.PasteSpecial Paste:=xlPasteValues '<--| ... and values to "blank" sheet current header column
End If
Next cell
End Sub
I'm looking to copy data across multiple worksheets. The names of the worksheets are in column L, I want to pick up the data from columns N:R, for that particular line, and then copy that into cells D17:D21 in the corresponding sheet.
Any assistance would be great.
Cheers
DRod
Sub Macro2()
Dim wb As Workbook
Dim ws As Worksheet
Dim wsGet As Worksheet
Dim LCell As Range
Dim sDataCol As String
Dim lHeaderRow As Long
sDataCol = "L" 'Change to be the column you want to match sheet names agains
lHeaderRow = 5 'Change to be what your actual header row is
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Sheet1") 'Change this to be your Summary sheet
'Check for values in sDataCol
With ws.Range(sDataCol & lHeaderRow + 1, ws.Cells(ws.Rows.Count, sDataCol).End(xlUp))
If .Row <= lHeaderRow Then Exit Sub 'No data
'Loop through sDataCol values
For Each LCell In .Cells
If LCell.Text <> "" Then
'Check if sheet named that value exists
If Evaluate("ISREF('" & LCell.Text & "'!A1)") Then
'Found a matching sheet, copy data from columns N:R to cells D17:D21 in the corresponding sheet
Set wsGet = wb.Sheets(LCell.Text)
wsGet.Range("N[ ]:R[ ]").Copy
LCell.Activate
Range("D17:D21").PasteSpecial Paste:=xlPasteValues, Transpose:=True
End If
End If
Next LCell
End With
End Sub
there are some mistakes
wsGet.Range("N[ ]:R[ ]") is not a valid syntax.
while wsGet.Range("N:R")is.
still, that way you get the entire columns, and not just the row you need of them.
you could use "Instersect()" method or the "Resize()" method on that range to get the range you need
with wsGet.Range("N[ ]:R[ ]").Copy you're using ".Copy" method on a "wsGet" sheet range.
use it on the same range (corrected as per the preceeding suggestion) of the "ws" sheet instead
with LCell.Activate, you're activating a "cell" instead of a "sheet".
you should use wb.Sheets(LCell.Text).Activate instead
but you don't need any sheet activation since you have already set "wsGet" as the "destination" sheet, so simply use ".PasteSpecial" method on it