I'm looking to improve my code to dynamically set ranges where data exist instead of hard coding the values. The starting value of the range will never change, but the ending value will if more month columns are added. What is the best way to approach this. Would be easier to make the range user defined?
Here's what I have:
The code will split data by unique group name starting at C5 into separate worksheets.
Public Sub Splitdatatosheets()
' Splitdatatosheets Macro
Dim Rng As Range
Dim Rng1 As Range
Dim vrb As Boolean
Dim sht As Worksheet
'Find unique value for splitting
Set Rng = Sheets("Sheet1").Range("C5")
'Find starting row to copy (Re-code to dynamically set)
Set Rng1 = Sheets("Sheet1").Range("A5:M5")
vrb = False
Do While Rng <> ""
For Each sht In Worksheets
If sht.Name = Left(Rng.Value, 31) Then
sht.Select
Range("A2").Select
Do While Selection <> ""
ActiveCell.Offset(1, 0).Activate
Loop
Rng1.Copy ActiveCell
ActiveCell.Offset(1, 0).Activate
Set Rng1 = Rng1.Offset(1, 0)
Set Rng = Rng.Offset(1, 0)
vrb = True
End If
Next sht
If vrb = False Then
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Name = Left(Rng.Value, 31)
'Copy header rows (Re-code to dynamically set) to new worksheet first cell
Sheets("Sheet1").Range("A4:M4").Copy ActiveSheet.Range("A1")
Range("A2").Select
Do While Selection <> ""
ActiveCell.Offset(1, 0).Activate
Loop
Rng1.Copy ActiveCell
Set Rng1 = Rng1.Offset(1, 0)
Set Rng = Rng.Offset(1, 0)
End If
vrb = False
Loop
End Sub
Here's the updated code for anyone who stumbles across this question.
Public Sub Splitdatatosheets()
' Splitdatatosheets Macro
Dim rng As Range
Dim Rng1 As Range
Dim Rng2 As Range
Dim vrb As Boolean
Dim sht As Worksheet
Dim R_Start, R_End, H_Start, H_End As Range
'Set Header
Set H_Start = Cells(4, 1)
Set H_End = H_Start.End(xlToRight)
'Set Data range
Set R_Start = Cells(5, 1)
Set R_End = R_Start.End(xlToRight)
'Find unique value for splitting
Set rng = Sheets("Sheet1").Range("C5")
'Find starting row to copy
Set Rng1 = Range(R_Start, R_End)
Set Rng2 = Range(H_Start, H_End)
vrb = False
Do While rng <> ""
For Each sht In Worksheets
If sht.Name = Left(rng.Value, 31) Then
sht.Select
Range("A2").Select
Do While Selection <> ""
ActiveCell.Offset(1, 0).Activate
Loop
Rng1.Copy ActiveCell
ActiveCell.Offset(1, 0).Activate
Set Rng1 = Rng1.Offset(1, 0)
Set rng = rng.Offset(1, 0)
vrb = True
End If
Next sht
If vrb = False Then
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Name = Left(rng.Value, 31)
'Copy header rows to new worksheet first cell
Rng2.Copy ActiveSheet.Range("A1")
Range("A2").Select
Rng1.Copy ActiveCell
Set Rng1 = Rng1.Offset(1, 0)
Set rng = rng.Offset(1, 0)
End If
vrb = False
Loop
End Sub
Related
I'm am trying to automatically filter a column and copy/paste all the unique values to a new sheet each. Here's the code that I have been working with, however I'm facing this error when running the code:
Run-time error '1004': The extract range has a missing or invalid field name.
Sub Filter()
Application.ScreenUpdating = False
Dim x As Range
Dim rng As Range
Dim last As Long
Dim sht As String
'specify sheet name in which the data is stored
sht = "Filter This"
'change filter column in the following code
last = Sheets(sht).Cells(Rows.Count, "C").End(xlUp).Row
Set rng = Sheets(sht).Range("A1:H" & last)
Sheets(sht).Range("C1:C" & last).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=Range("AA1"), _
Unique:=True
For Each x In Range([AA2], Cells(Rows.Count, "AA").End(xlUp))
With rng
.AutoFilter
.AutoFilter Field:=3, Criteria1:=x.Value
.SpecialCells(xlCellTypeVisible).Copy
Sheets.Add(After:=Sheets(Sheets.Count)).Name = x.Value
ActiveSheet.Paste
End With
Next x
'Turn off filter
Sheets(sht).AutoFilterMode = False
With Application
.ScreenUpdating = True
.CutCopyMode = False
End With
End Sub
You can use collections to filter out the unique items, instead of using the advanced filter.
Sub UsingCollection()
Dim cUnique As Collection, ws As Worksheet, fRng As Range
Dim Rng As Range
Dim Cell As Range
Dim sh As Worksheet
Dim vNum As Variant
Set sh = ThisWorkbook.Sheets("Filter This")
Set Rng = sh.Range("C2:C" & sh.Cells(sh.Rows.Count, "C").End(xlUp).Row)
Set cUnique = New Collection
On Error Resume Next
For Each Cell In Rng.Cells
cUnique.Add Cell.Value, CStr(Cell.Value)
Next Cell
On Error GoTo 0
For Each vNum In cUnique
With sh
Set fRng = .Range("C1:H" & .Cells(.Rows.Count, "C").End(xlUp).Row)
End With
Set ws = Sheets.Add(After:=Sheets(Sheets.Count))
With ws
.Name = vNum
With fRng
.AutoFilter Field:=3, Criteria1:=vNum
fRng.Copy ws.Range("A1")
End With
.AutoFilterMode = False
End With
Next vNum
End Sub
I got stuck in the below-mentioned code, what I want to do is to get the value from Range("C4:C" & LastRow) in worksheets X2 that will b changing every time and compare each value with all open workbooks name. If match found then search that value in A column of worksheet X1 and copy all those rows.
The final objective is to paste those rows into those open workbooks which have the same value. For eg: Range C4 has TW00 then the code will search workbooks which have name "TW00.xlsx" and copy all the rows from worksheet X1 which have TW00 value in column A in the worksheet named TW00.xlsx.
Dim BookNames()
ReDim BookNames(Windows.Count)
n = 1
For n = 1 To Windows.Count
BookNames(n) = Workbooks(n).Name
If Workbooks(n).Name = Workbooks("A.xlsx").Worksheets("X2").Range("C4:C" & LastRow).Value Then
Set Rng = Workbooks("A.xlsx").Worksheets("X1").Range("A2:A50000")
For Each c In Rng.Cells
If c.Value = Workbooks("A.xlsx").Worksheets("X2").Range("C4").Value Then
If Not CopyRng Is Nothing Then
Set CopyRng = Application.Union(CopyRng,
Workbooks("A.xlsx").Worksheets("X1").Rows(c))
Else
Set CopyRng = Workbooks("A.xlsx").Worksheets("X1").Rows(c)
End If
End If
Next c
CopyRng.Copy
Workbooks(n).Activate
Worksheets.Add
ActiveSheet.Name = "X1"
ActiveSheet.Paste
End If
Next n
is that code help you?
Sub test()
Dim lastRow As Long
dim sheetName as string
Dim sourceDataSheet As worksheet
Dim sourceSheetsName as worksheet
dim targetDataSheet as worksheet
Dim wkb As Variant
set sourceDataSheet = ActiveWorkbook.Worksheets("X2")
set sourceSheetsName = ActiveWorkbook.Worksheets("X1")
With sourceSheetsName
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
sheetName = .Cells(lastRow, "A")
For Each wkb In Application.Workbooks
If wkb.Name <> .Name And wkb.Name = sheetName Then
set targetDataSheet = wkb.Worksheets.Add
with sourceDataSheet
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
for i = 1 to lastRow
if .Cells(i, "A").Value = sheetName then
.Cells(i, "A").EntireRow.Copy
targetDataSheet.Cells(i, "A").PasteSpecial Paste:=xlPasteValues
end if
next i
end with
End If
Next wkb
End With
End Sub
I have created a macro to create worksheets from a list,this works fine but i have a problem, if i only have one item in the list i get an error, here is the macro:
Sub CreateSheetsFromAList()
Application.ScreenUpdating = False
Sheets("Master").Select
Sheets("Stock Removal").Visible = True
Dim MyCell As Range, MyRange As Range
Set MyRange = Sheets("Master").Range("A14")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
Sheets("Stock Removal").Copy after:=Sheets(Sheets.Count) 'creates a new worksheet
Sheets(Sheets.Count).Name = MyCell.Value ' renames the new worksheet
Next MyCell
Sheets("Stock Removal").Select
ActiveWindow.SelectedSheets.Visible = False
Application.ScreenUpdating = True
End Sub
You should rather use xlUp than xlDown, it is safer!
You selected the whole column previously (from row 14, until the end of the sheet!)
This will run smoothly! ;)
Sub CreateSheetsFromAList()
Application.ScreenUpdating = False
Dim wsM As Worksheet, wsSR As Worksheet
Dim MyCell As Range, MyRange As Range, LastRow As Double
Set wsM = ThisWorkbook.Sheets("Master")
Set wsSR = ThisWorkbook.Sheets("Stock Removal")
wsM.Select
wsSR.Visible = True
Set MyRange = wsM.Range("A14")
LastRow = wsM.Range("A" & wsM.Rows.Count).End(xlUp).Row
If LastRow > 14 Then
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
wsSR.Copy after:=Sheets(Sheets.Count) 'creates a new worksheet
Sheets(Sheets.Count).Name = MyCell.Value ' renames the new worksheet
Next MyCell
Else
wsSR.Copy after:=Sheets(Sheets.Count) 'creates a new worksheet
Sheets(Sheets.Count).Name = MyRange.Value ' renames the new worksheet
End If
wsSR.Select
ActiveWindow.SelectedSheets.Visible = False
Application.ScreenUpdating = True
End Sub
The problem is in case if only Cell A14 has data, and the entire column A (below cell A14) is blank, in that case MyRange.End(xlDown) will result in "A1048576". So you need to find the last row in Column A, and then check if it's 14 >> If it is then your MyRange should consist of 1 cell, and that's Cell A14.
Try the code below to replace the way you Set MyRange :
With Sheets("Master")
If .Cells(.Rows.Count, "A").End(xlUp).Row = 14 Then ' if only cell A14 has data in entire Column A
Set MyRange = Sheets("Master").Range("A14")
Else
Set MyRange = Sheets("Master").Range("A14", Range("A14").End(xlDown))
End If
End With
Try with changing:
Set MyRange = Sheets("Master").Range("A14")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
To:
With Sheets("Master")
Set MyRange = .Range(Range("A14"), .Range("A" & .Range("A" & .Rows.Count).End(xlUp).row))
End With
I want to create worksheets from a list in excel using VBA, I have the below code which works fine. But it doesn't remove duplicates from the list, and if I use remove duplicates, it throws an error. :). I don't want the original column altered.
Set MyRange = Sheets("YES").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown)).RemoveDuplicates
For Each MyCell In MyRange
Sheets.Add After:=Sheets(Sheets.Count) ' creates a new worksheet
Sheets(Sheets.Count).Name = UCase(MyCell.Value) ' renames the new worksheet
ActiveSheet.Range("A1").Select ' selects current worksheet
Cells(1, 1).Font.Bold = True ' changes fornt to bold
ActiveCell.Value = ("Column Name") ' enters values into cell
ActiveSheet.Range("A2").Select
ActiveCell.Value = UCase(MyCell.Value) ' enters column name in cell
Next MyCell
thanks
How about this code. It will leave the original column in tact and remove the dupes in a holding range. It's also qualified more cleanly.
Dim wsYes as Worksheet
Set wsYes = Worksheets("YES")
With wsYes
Dim myRange as Range
Set myRange = .Range("A2",.Range("A2").End(xlDown))
myRange.Copy .Cells(1,.Columns.Count) 'copy to far right column
.Cells(1,.Columns.Count).Resize(myRange.Rows.Count,1).RemoveDuplicates 1, xlNo
Set myRange = .Range(.Cells(1,.Columns.Count),.Cells(1,.Columns.Count).End(XlDown))
For Each MyCell In myRange
Dim sName as String
sName = UCase(MyCell.Value)
Dim wsNew as Worksheet
Set wsNew = Sheets.Add(After:=Sheets(Sheets.Count)) ' creates a new worksheet
With wsNew
.Name = sName
.Range("A1").Value = "Column Name"
.Range("A1").Font.Bold = True
.Range("A2").Value = sName
End With
Next MyCell
myRange.Clear
End with
Easy way (but not the best I think if you have many data):
Set MyRange = Sheets("YES").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
Dim index1 As Integer
Dim index2 As Integer
index1 = 0
For Each Cell1 In MyRange
index1 = index1 + 1
index2 = 0
For Each Cell2 In MyRange
If index2 >= index1
Then Exit For
If MyCell.Value = Cell2.Value
Then Goto NextCell1
Next Cell2
Sheets.Add After:=Sheets(Sheets.Count) ' creates a new worksheet
Sheets(Sheets.Count).Name = UCase(MyCell.Value) ' renames the new worksheet
ActiveSheet.Range("A1").Select ' selects current worksheet
Cells(1, 1).Font.Bold = True ' changes fornt to bold
ActiveCell.Value = ("Column Name") ' enters values into cell
ActiveSheet.Range("A2").Select
ActiveCell.Value = UCase(MyCell.Value) ' enters column name in cell
NextCell1:
Next Cell1
I require assistance with the following please:
I need to filter a Range A9 - A32 for any data in column G.
Then i need to copy the data, but only columns A - E & G to sheet 2.
then delete the filtered data and return back to non filtered view.
I have tried the following without success:
Dim shSrc As Worksheet
Dim shDst As Worksheet
Dim rSrc As range
Dim rDst As range
Dim range
Dim numCol As Long ' number of columns to copy
On Error GoTo EH
range = ("A:E,G:G")
' select source and dest sheets
Set shSrc = ActiveWorkbook.Worksheets("Active Snag List")
Set shDst = ActiveWorkbook.Worksheets("Snag History")
' Select initial rows
Set rSrc = shSrc.Cells(9, 7)
Set rDst = shDst.Cells(2, 1)
' loop over source
Do While rSrc <> ""
' Test Source row, Qty = 0 and Name is not blank
With rSrc
If .Offset(0, 2) = 0 And .Value <> "" Then
'Copy
.Resize(1, range).Copy rDst.Resize(1, range)
Set rDst = rDst.Offset(1, 0)
End If
End With
Set rSrc = rSrc.Offset(1, 0)
Loop
Exit Sub
EH:
MsgBox "Error " & Err.Description
Thank you in advance!
To get your code working , replace the IF section with this
If .Offset(0, 2) = 0 And .Value <> "" Then
'Copy
'Cells A:E
rDst.Resize(1, 5).Value = .EntireRow.Cells(1, 1).Resize(1, 5).Value
' Cell G
rDst.Offset(0, 6).Value = .Value
Set rDst = rDst.Offset(1, 0)
End If
Why not use Autofilter rather than looping through cells? It will me much faster. See this example.
CODE(TRIED AND TESTED)
Option Explicit
Sub Sample()
Dim shSrc As Worksheet, shDst As Worksheet
Dim rDst As range, rng As range, rngtocopy As range
Dim lastrow As Long
On Error GoTo EH
'~~> Select source and dest sheets
Set shSrc = ThisWorkbook.Worksheets("Active Snag List")
Set shDst = ThisWorkbook.Worksheets("Snag History")
'~~> Select initial rows
Set rDst = shDst.Cells(2, 1)
With shSrc
'~~> Remove any filters
.AutoFilterMode = False
'~~> Get the last row of Col G
lastrow = .range("G" & .Rows.Count).End(xlUp).Row
With .range("A8:G" & lastrow)
'~~> Filter G Col for non blanks
.AutoFilter Field:=7, Criteria1:="<>"
'~~> Get the offset(to exclude headers)
Set rng = .Offset(1, 0).SpecialCells(xlCellTypeVisible)
'~~> Remove Col F from the resulting range
Set rngtocopy = Union(shSrc.range(Replace(rng.Address, "G", "E")), _
shSrc.range(Replace(rng.Address, "A", "G")))
'~~> Copy cells to relevant destination
rngtocopy.Copy rDst
'~~> Delete the filtered results
rng.EntireRow.Delete
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
Exit Sub
EH:
MsgBox "Error " & Err.Description
End Sub
SNAPSHOTS
Sheet 1 before the macro runs
Sheet 2 after the macro runs
Sheet 1 after the macro runs