How to iteratively copy each columns in one sheet to different sheets - excel

I am trying to use VBA to realize the following goal:
I have two sheets: "revenue" and "sales tax", and they record the revenue and sales tax of 100 stores from May 1st to May 28th. Now I am trying to create a sheet for each store recording its revenue and sales tax from May 1st to May 28th.
Sub test1()
Sheets("Sheet1").Select
Sheets("Sheet1").Copy Before:=Sheets(17)
Sheets("revenue").Select
Range("D154:D168").Select
Selection.Copy
Sheets("Sheet1 (2)").Select
Range("C5").Select
ActiveSheet.Paste
Sheets("sales tax").Select
Range("D138:D152").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet1 (2)").Select
Range("F5").Select
ActiveSheet.Paste
Sheets("Sheet1 (2)").Select
Sheets("Sheet1 (2)").Name = " reportF "
End Sub
Using this code I am only able to establish a file for 1 store each time. What loop syntax should I use to loop through all stores?

It looks like your data has the store name in column D? This code runs down all cells in column D and copies them into separate sheets depending on the contents
Sub ExampleCode
Dim r as range 'declare a pointer variable
Dim ws as worksheet 'declare a worksheet variable
set r = Range("d1") 'point to fist cell
Do 'Start a loop
If SheetNotExist(r.text) then 'if no sheet of that name
set ws = worksheets.add(after:=worksheets.count) 'add one
ws.name = r.text 'and name it as text in r
End if
r.copy worksheets(r.text).cells(rows.count,4).end(xlup).offset(1,0) 'copy to next blank cell
set r = r.offset(1,0) 'shift pointer down one cell
Loop until r.text = "" 'keep going until r is empty
End Sub
Function SheetNotExist(s as string) as boolean 'check if sheet exists
On error goto nope 'jump on error
Dim ws as worksheet
set ws = worksheets(s) 'this will error if sheet doesn't exist
'so if we get here the sheet does exist
SheetNotExist = False 'so return false
Exit Function 'and go back
nope: 'we only get here if sheet doesn't exist
SheetNotExist = True 'so return that
End Function
Written on my phone - don't have excel so there may be typos - code may not compile therefore,

Related

Excel Macro to Insert Row, with formatting, below header of named range

I would like the user to be able to click the green button on the right of each named range to insert a new data entry row below the named range header. The code I have hard codes the insert row number for the first named range. I need a way to have the code to be smart enough to know that the first row below the header of the second, third, & forth named range will changed.
Another big part is that the inserted row needs to have the same formatting (dropdowns, formulas, color, etc.) as the rows below.
First named range button code:
Sub BidSheetAddRow_Materials()
' BidSheetAddRow_Materials Macro
Rows("19:19").Select
Selection.Copy
Rows("19:19").Select
Selection.Insert Shift:=xlDown
Range("A19").Select
Application.CutCopyMode = False
Selection.ClearContents
Range("C19").Select
Selection.ClearContents
Range("K19").Select
Selection.ClearContents
End Sub
Based on the screenshot all table headers are in colA, the first input row is 3 cells below the header, and the first input cell on each table row is a merged cell.
So this works for me:
Sub AddMaterial()
AddRow "MATERIALS"
End Sub
Sub AddRate()
AddRow "RATE"
End Sub
Sub AddRow(TableHeader As String)
Dim f As Range, ws As Worksheet, c As Range
Set ws = ThisWorkbook.Worksheets("Input") 'or whatever
Set f = ws.Columns("A").Find(what:=TableHeader, lookat:=xlWhole) 'find the header
If Not f Is Nothing Then
Set c = f.Offset(3) 'step down to first input row below header
Do While c.Offset(1).MergeArea.Cells.Count > 1 'keep looping while `c` is merged
Set c = c.Offset(1)
Loop
c.Offset(1).EntireRow.Insert shift:=xlDown 'insert
c.EntireRow.Copy c.Offset(1) 'copy
c.Offset(1).EntireRow.ClearContents 'clear new row
Else
MsgBox "Table header '" & TableHeader & "' not found!"
End If
End Sub
Before/after:

Excel VBA code to filter two columns and extract data

this is my first post and I am super excited about it. I apologize in advance if my writing wouldn't make sense since I'm not super familiar with coding/programming terms.
Here is the Micro_Enabled_Excel_File which I'm using.
I have an excel file with multiple columns and rows. The number of rows will increase as time passes. I'm trying to filter two columns and copy the latest/most recent datapoint(row) and paste it in a new sheet to create a status report.
Excel Dataset: image
What the results would look like: image
What I have done so far:
Created a Micro to go through columns "SCOPE" and "TRADE NAME" to grab the unique entries and copy it into another sheet called "Code".
Sub First_COPY_STYLE_TO_REPORT()
'creating the Report sheet
Sheets("Report").Select
Cells.Select
Selection.Delete Shift:=xlUp
Sheets("Status Updates").Select
Cells.Select
Selection.Copy
Sheets("Report").Select
ActiveSheet.Paste
Rows("2:1048576").Select
Application.CutCopyMode = False
Selection.ClearContents
End Sub
Created a Micro to create a template for sheet "Report" which will eventually be filled with the results of next Micro.
Sub Second_COPY_UNIQUE_TO_CODE()
'add title to filter columns in the Code sheet
Sheets("Code").Select
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1").Select
ActiveCell.FormulaR1C1 = "Filter1"
Range("B1").Select
ActiveCell.FormulaR1C1 = "Filter2"
'creating the filter criteria also known as scope and trade name
'Finds Duplicates on SCOPE column and copies it to a new sheet called CODE
Sheets("Status Updates").Select
Dim s1 As Worksheet, s2 As Worksheet
Set s1 = Sheets("Status Updates")
Set s2 = Sheets("Code")
s1.Range(Range("B2"), Range("B2").End(xlDown)).Copy s2.Range("A2")
s2.Range("A:A").RemoveDuplicates Columns:=1, Header:=xlNo
'Finds Duplicates on NAME column and copies it to a new sheet called CODE
Dim s3 As Worksheet, s4 As Worksheet
Set s3 = Sheets("Status Updates")
Set s4 = Sheets("Code")
s1.Range(Range("C2"), Range("C2").End(xlDown)).Copy s2.Range("B2")
s4.Range("B:B").RemoveDuplicates Columns:=1, Header:=xlNo
'Clears formating and autofits column widths
Sheets("Code").Cells.ClearFormats
ThisWorkbook.Worksheets("Code").Cells.EntireColumn.AutoFit
End Sub
Created a Micro (Not Functioning) which includes two loops to filter two columns, sort the first column and copy and paste the second row of the sheet into the sheet "Report".
Sub Third_Generate_Latest_Status_Report()
Dim a1 As Long, a2 As Long, b1 As Long, b2 As Long
a1 = Cells.Find("Filter1").Offset(1, 0).Row
a2 = Cells.Find("Filter1").End(xlDown).Row
b1 = Cells.Find("Filter2").Offset(1, 0).Row
b2 = Cells.Find("Filter2").End(xlDown).Row
Dim g As Long, i As Long
For g = a1 To a2 'Look up for Filter1 column. Then loop through all criterias.
ActiveSheet.Range("$C$1:$J$300").AutoFilter Field:=2, Criteria1:=g
For i = b1 To b2 'Look up for Filter2 column. Then loop through all criterias.
ActiveSheet.Range("$C$1:$J$300").AutoFilter Field:=3, Criteria1:=i
'sort the NO column from largest to smallest (to get the latest/most recent update).
'I have copied this part of the code from the Micro I recorded.
ActiveWorkbook.Worksheets("Status Updates").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Status Updates").AutoFilter.Sort.SortFields.Add2 _
Key:=Range("C1:C300"), SortOn:=xlSortOnValues, Order:=xlDescending, _
DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Status Updates").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
'I think I need to add code here to copy the row to sheet Report, and run the loop again
End With
Next i 'take next value in column Filter2
Next g 'take next value in column Filter1
End Sub
What I believe I need:
Sheet "Status Updates" - Filter "SCOPE" column and run through all criteria.Then,
Sheet "Status Updates" - Filter "TRADE NAME" column and run through all criteria.
Sort the "NO" column to get the most recent datapoint.
Copy the first row of data (meaning, the first row after the titles)
Paste it in another sheet called "Report".
Could you please take a look at my code and let me know what my mistakes are?
This is my first time coding/programming/using VBA.
Having an extra "code" sheet usually just makes things unnecessarily complicated. And because your "Status Updates" sheet is already sorted with Oldest updates to Newest updates, we know that for any given unique combo, you'll always want the bottom update. We can guarantee pulling that if we loop over your data backwards (from bottom row to first row, that's what the Step -1 does).
Then use a dictionary to check for unique combinations and pull the first encountered row (remember we're going backwards, so the first encountered row will be the latest update) for each unique combo and copy those rows over to your report sheet.
In the end, here's a fairly beginner friendly version of code for this task. I've commented it heavily for clarity so that you can follow along and understand what it does.
Sub tgr()
'Declare and set workbook and worksheet object variables
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim wsUpdt As Worksheet: Set wsUpdt = wb.Worksheets("Status updates")
Dim wsRprt As Worksheet: Set wsRprt = wb.Worksheets("Report")
'Declare and set a range variable that contains your data
Dim rUpdateData As Range: Set rUpdateData = wsUpdt.Range("A2:G" & wsUpdt.Cells(wsUpdt.Rows.Count, "A").End(xlUp).Row)
'Verify data actually exists
If rUpdateData.Row < 2 Then Exit Sub 'If the beginning row is the header row, then no data actually exists
'Use a dictionary object to keep track of unique Scope and Trade Name combos
Dim hUnqScopeTrades As Object: Set hUnqScopeTrades = CreateObject("Scripting.Dictionary")
'Declare your resulting Copy Range variable. This will be used to gather only the range of rows that will be copied over to the Report worksheet
Dim rCopy As Range
'Declare a looping variable
Dim i As Long
'Loop through each row in your Status Updates data. Because your updates are already sorted Oldest to Newest, begin at the end and loop backwards to guarantee newest updates are found first
For i = rUpdateData.Rows.Count To 1 Step -1
'Verify this Scope/Trade combo hasn't been seen before
If Not hUnqScopeTrades.Exists(rUpdateData.Cells(i, 2).Value & "|" & rUpdateData.Cells(i, 3).Value) Then
'This is a newly encountered unique combo
'Add the combo to the dictionary
hUnqScopeTrades(rUpdateData.Cells(i, 2).Value & "|" & rUpdateData.Cells(i, 3).Value) = i
'If this is the first unique combo found, rCopy will be empty, check if that's the case
If rCopy Is Nothing Then
'rCopy is empty, add the first found unique combo to it
Set rCopy = rUpdateData.Cells(i, 1)
Else
'rCopy is not empty, add all additional unique combos with the Union method
Set rCopy = Union(rCopy, rUpdateData.Cells(i, 1))
End If
End If
Next i
'Clear previous results (if any)
wsRprt.Range("A1").CurrentRegion.Offset(1).Clear
'Verify rCopy isn't empty and then copy all rows over
If Not rCopy Is Nothing Then rCopy.EntireRow.Copy wsRprt.Range("A2")
End Sub

Excel Macro to copy worksheet to new worksheet paste values only

I am working on a Macro in Excel that will make a copy of the current worksheet and paste the values into a new worksheet. The worksheet name would be the same just with a number after it [ie Sheet, Sheet1(2)]
My code does this correctly except that it copies and pastes everything to Sheet1(2). I only want it to paste the values (not formulas) from Sheet1 to Sheet1(2). I'm a novice at vba at best so any suggestions are greatly appreciated.
Sub SPACER_Button4_Click()
' Compile Button to Generate Quote
'
'variables definitions
ActiveSheetValue = ActiveSheet.Name
'
'This section creates a copy of the active worksheet and names it with the next corresponding number
Sheets(ActiveSheetValue).Copy After:=Sheets(ActiveSheetValue)
'This section should look for X value in each row, column 4. If value equals X, it deletes the row on the copied sheet
Dim i As Integer
i = 26
Do Until i > 300
If ActiveSheet.Cells(i, 11).Value = "X" Then
Rows(i).Delete
Skip = True
End If
'
If Skip = False Then
i = i + 1
End If
'
Skip = False
Loop
'This part hides columns on Right K thru R of new copied sheet
Sheets(ActiveSheet.Name).Range("K:R").EntireColumn.Hidden = True
End Sub
If the data is contiguous, consider creating a new sheet, selecting and copying the range of data, and pasting onto the new sheet using the below code.
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
I use something like this:
Sub KopyKat()
Dim s1 As Worksheet, s2 As Worksheet
Dim r As Range, addy As String
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
For Each r In s1.UsedRange
If r.Value <> "" Then
If Not r.HasFormula Then
addy = r.Address
r.Copy s2.Range(addy)
End If
End If
Next r
End Sub

Macro to delete contents of cells using their references

I have a list of references of cells to be deleted. The list of references is in sheet "test_url". The list of references point to cells to be deleted that are in another sheet "main_lists".
What I am after is a macro that takes all the references listed in "test_url" sheet, and select their cells in "main_lists" sheet and delete them.
The following macro is what I recorded for two references only in an attempt to demonstrate my problem that necessitated me to copy the reference from "test_url" sheet, then paste it in the NameBox of "main_urls" sheet to select the contents of the designated cell then delete its contents. This process was done manually one cell at a time for a list of 10-20 addresses/references. However, recently this list is over 2000 entries and it is growing:
Sub DeletePermittedCells()
'DeletePermittedCells Macro
Sheets("test_urls").Select
Range("B2").Select
Sheets("test_urls").Select
Selection.Copy
Sheets("main_lists").Select
Application.Goto Reference:="R200045C1"
Application.CutCopyMode = False
Selection.ClearContents
Sheets("test_urls").Select
Range("B3").Select
Selection.Copy
Sheets("main_lists").Select
Application.Goto Reference:="R247138C1"
Application.CutCopyMode = False
Selection.ClearContents
Sheets("test_urls").Select
End Sub
Can someone help with this issue please?
Try this one:
Sub DeletePermittedCells()
Dim rng As Range
Dim arr, c
With Sheets("test_urls")
'storing data in array makes your code much faster
arr = .Range("B2:B" & .Cells(.Rows.Count, "B").End(xlUp).Row).Value
End With
With Sheets("main_lists")
Set rng = .Range(arr(1, 1))
For Each c In arr
Set rng = Union(rng, .Range(c))
Next
End With
rng.ClearContents
End Sub
storing addresses in array (rather than reading each cell from worksheet directly) makes your code much faster.
Note, code assumed that your addresses stored in range B2:B & lastrow where lastrow - is row of last cell with data in column B
This assumes that the list of cells to be cleared in is column A:
Sub ClearCells()
Dim s1 As Worksheet, s2 As Worksheet
Dim N As Long, I As Long, addy As String
Set s1 = Sheets("test_url")
Set s2 = Sheets("main_lists")
N = s1.Cells(Rows.Count, "A").End(xlUp).Row
For I = 1 To N
addy = s1.Cells(I, 1).Value
s2.Range(addy).ClearContents
Next I
End Sub

Convert numbers stored as text to numbers?

How can I convert numbers stored as text to numbers?
I have tried setting:
ActiveSheet.Range("H154").NumberFormat = "General"
But it doesn't work!
The only things I've found that work are using "Text to columns" or clicking the cell to edit it and then clicking Enter.
But I would really like to find a way to turn number cells in a sheet stored as text into numbers using VBA.
A general technique is to Copy PasteSpecial, Multiply by 1
In code, something like this:
Sub ConvertToNumber()
Dim rng As Range
Dim cl As Range
Dim rConst As Range
' pick an unused cell
Set rConst = Cells(1, 4)
rConst = 1
Set rng = Cells.SpecialCells(xlCellTypeConstants)
rng.NumberFormat = "General"
rConst.Copy
rng.PasteSpecial xlPasteValues, xlPasteSpecialOperationMultiply
rConst.Clear
End Sub
Just use CDbl():
ActiveSheet.Range("H154") = CDbl(ActiveSheet.Range("H154"))
I'm not a coding expert and the "Number Stored as Text" error plagued me for a long time.
I finally found this:
Delimited Text-to-Columns in a Macro
Which got me to this:
Sub ConvertTextToNumber()
Sheets("Worksheet_Name").Select
Range("A1").Select
Selection.TextToColumns _
Destination:=Range("A:A"), _
DataType:=xlDelimited
End Sub
I use this in a macro to copy & reorder columns in a new sheet:
Sub ColumnReorder()
'**********************************************************
'Paste this macro into the Workbook of each new "Employee_List_Weekly_Update"
'Functionality:
'1. Column order in the "Employee_List_Weekly_Update" worksheet changes fairly often.
' The macro will find each column by header name,
' select that column and copy it to the new sheet.
'2. The macro also converts "Employee ID#" to a number,
' removing the "Number saved as Text" error.
'**********************************************************
'Create new sheet
Sheets.Add.Name = "Roster_Columns_Reordered"
'Repeat for each column or range
'Find Column in "Employee_List_Weekly_Update" - Copy it - Paste it in "Roster_Columns_Reordered" - Employee ID#
Dim a As Integer
Sheets("Employee_List_Weekly_Update").Select
Set rngData = Range("A1").CurrentRegion
a = Application.WorksheetFunction.Match("Employee ID#", Range("A1:BB1"), 0)
Columns(a).Select
Selection.Copy
Sheets("Roster_Columns_Reordered").Select
Range("A1").Select
ActiveSheet.Paste
'Use TextToColumns to convert "Number Stored as Text "
Selection.TextToColumns _
Destination:=Range("A:A"), _
DataType:=xlDelimited
'Find Column in "Employee_List_Weekly_Update" - Copy it - Paste it in "Roster_Columns_Reordered" - Name
Dim b As Integer
Sheets("Employee_List_Weekly_Update").Select
Set rngData = Range("A1").CurrentRegion
b = Application.WorksheetFunction.Match("Name", Range("A1:BB1"), 0)
Columns(b).Select
Selection.Copy
Sheets("Roster_Columns_Reordered").Select
Range("B1").Select
ActiveSheet.Paste
'Go to "Roster_Columns_Reordered" - Add AutoFilter - Freeze Top Row
Rows("1:1").Select
Selection.AutoFilter
With ActiveWindow
.SplitColumn = 2
.SplitRow = 1
End With
Rows("2:2").Select
ActiveWindow.FreezePanes = True
Range("A1").Select
End Sub
if you want to convert a selection (even with text in it!), you can use the code by firefiend (http://www.ozgrid.com/forum/showthread.php?t=64027&p=331498#post331498)
I think the magic is in .Value = .Value
vba
Sub macro()
Range("F:F").Select 'specify the range which suits your purpose
With Selection
.NumberFormat = "General"
.Value = .Value
End With
End Sub

Resources