I have attempted to combine a simple loop code and select cases to return a desired user result (I am aware the code is incorrect). Within column J I have a series of years ranging from 2012 to 2017 dependant on the year in column J I wish to cut the data from column U to AG and paste it, in its correct place.
The code I have come up with is below;
Sub Move_data()
Dim rng As Range
Dim LR As Long
LR = Range("J1048576").End(xlUp).Row
Set rng = Range(Cells(2, 10), Cells((LR), 10))
For x = 2 To LR Step 1
Select Case Range("J" & x).Value2
Case 2012
Range("BU" & x).Cut
Range("IH" & x).Paste
Range("U" & x, ":CG" & x).Cut
Range("AH" & x).PasteSpecial
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Case 2013
Range("BU" & x).Cut
Range("IH" & x).Paste
Range("U" & x, ":CG" & x).Cut
Range("AU" & x).PasteSpecial
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Case 2014
Range("BU" & x).Cut
Range("IH" & x).Paste
Range("U" & x, ":CG" & x).Cut
Range("BH" & x).PasteSpecial
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Case 2015
Range("BU" & x).Cut
Range("IH" & x).Paste
Range("U" & x, ":CG" & x).Cut
Range("BU" & x).PasteSpecial
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Case 2016
Range("BU" & x).Cut
Range("IH" & x).Paste
Range("U" & x, ":CG" & x).Cut
Range("CH" & x).PasteSpecial
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Case 2017
Range("BU" & x).Cut
Range("IH" & x).Paste
Range("U" & x, ":CG" & x).Cut
Range("CU" & x).PasteSpecial
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
End Select
x = x + 1
Else
End If
Next x
End Sub
Also I have a feeling it may not be time efficient to loop through each line as there is over 1000 lines within the file, It may be quicker to sort and select all the same years and move the data all at once. (However I am unsure of how to do this)
Any help in code adaptation or guidance of the best way to achieve this would be much appreciated! I have attached a picture for guidance of what I am trying to achieve.
While this won't do exactly what you're looking for, it will give you an idea on how to get started using tables. This will detect unique values in your table (instead of setting your case examples) and then try to track it through. You'll have to convert your data source to a table (listobject) and there are a few other things which you will need to modify (have tried to highlight them with comments. Have a look through the code and feel free to ask any questions if it's of use.
Data source (table)
Code
Option Explicit
Sub tableLoop()
Dim ws As Worksheet
Dim tbl As ListObject
Dim i As Integer: Dim NoRow As Integer
Dim arr() As Variant
Dim c
With Application
.ScreenUpdating = False
End With
Set ws = ActiveSheet
Set tbl = ws.ListObjects(1)
On Error Resume Next
ws.ShowAllData
On Error GoTo 0
' first we will sort the table into order on year
With tbl.Sort
.SortFields.Clear
' Change the Range to match your table and year column)
.SortFields.Add Key:=Range("Table1[[#All],[Project Year]]"), SortOn:=xlSortOnValues, _
Order:=xlAscending, DataOption:=xlSortNormal
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' Get unique values in project year and put into array
With tbl.ListColumns(1).DataBodyRange
.AdvancedFilter Action:=xlFilterInPlace, Unique:=True
End With
i = 0
For Each c In tbl.ListColumns(1).DataBodyRange.SpecialCells(xlCellTypeVisible)
ReDim Preserve arr(0 To i)
arr(i) = c.Value
i = i + 1
Next c
' Change this loop for however you want the output to be
For i = 1 To UBound(arr)
Debug.Print arr(i)
With tbl
.Range.AutoFilter Field:=1, Criteria1:=arr(i)
.ListColumns(2).DataBodyRange.SpecialCells(xlCellTypeVisible).Copy
End With
With ws
NoRow = i
.Cells(NoRow, 5) = arr(i)
.Cells(NoRow, 6).PasteSpecial Paste:=xlPasteValues, Transpose:=True
On Error Resume Next
.ShowAllData
On Error GoTo 0
End With
Next i
With Application
.ScreenUpdating = True
End With
End Sub
Output
In my hasty look at your code it looks like only the Range(...).PasteSpecial line differs between the different cases. You could eliminate the Select Case structure and instead create an array that holds the columns for the PasteSpecial: cols = {"AH", "AU", "BH", "BU", "CH", "CU"}. Then you could select the column by TheCol = cols(year-2011).
Another way: since the columns are regularly spaced (13 apart) you could go by column number: col_num = 13*(year-2011) + 21. Then use something like Range.Cells(x, col_num).
Hope that helps
Related
Below is a macro which I wrote about three years ago, when I was a much less proficient VBA coder than I am today. There are a number of obvious things which I would simplify/do differently today. However, it is still in use and generally works. The manager of the relevant admin process copies and pastes this code into different workbooks every time we set up a new customer, and changes around a few of the variables. This has worked fine until today, when it has suddenly started generating a "subscript out of range" error when used in a new worksheet.
The error generates on the line workdaybook.Sheets("Sheet1").Copy salesBook.Worksheets(Sheets.Count). I have checked and "salesBook" has been defined. However, hovering the cursor over "salesBook.worksheets(Sheets.Count) brings up a "subscript out of range" message.
I know that overall, this code isn't the best-written in the world, but I am puzzled by it suddenly failing on this line, having worked previously for about three years when pasted into multiple different workbooks.
Option Explicit
Sub salescalc()
'Application.DisplayAlerts = False
'On Error GoTo Errorcatch
Application.ScreenUpdating = True
Dim salesBook As Workbook
Dim CurrentWeekSheet As Worksheet
Set salesBook = ThisWorkbook
Set CurrentWeekSheet = ThisWorkbook.ActiveSheet
Dim workday As Date
Dim nextworkday As Date
Dim workdaybook As Workbook
workday = InputBox("Insert date in format dd/mm/yy", "userdate")
nextworkday = workday + 1
'find bottom row of "table"
Dim bottomrow As Range
For x = 1 To 6500
If CurrentWeekSheet.Cells(x, 1).Interior.ColorIndex = 19 Then
coloured_row = Range("A" & x).Row
End If
Next x
Set bottomrow = Range("A" & coloured_row)
'finds beginning and end of day's range
Dim workdayrange As Range
Dim nRow As Long
Dim workday_date As Variant
Dim nStart As Long, nEnd As Long
' Work out where the range should start - works
For nRow = 1 To 65536 'change this to xldown
If CurrentWeekSheet.Range("A" & nRow).Value = workday Then
'nStart = nRow + 3
nStart = nRow + 1
Exit For
End If
Next nRow
' Work out where the range should end - works
For nRow = nStart To 65536
If CurrentWeekSheet.Range("A" & nRow).Value = nextworkday Or Range("A" & nRow).Row = bottomrow.Row Then
nEnd = nRow
Exit For
End If
Next nRow
'distinction between bottom row and next date - offset less for bottomrow
If nEnd = bottomrow.Row Then
nEnd = nEnd
Else
If nEnd <> bottomrow.Row Then
nEnd = nEnd - 2
End If
End If
Set workdayrange = CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd)
workday_date = Format(workday, "dd-mm-yy")
'identify which month is referred to based on date input by user (workday_date)- this will need to be updated in 2016
If Year(workday_date) <> 2017 Then
MsgBox "Date must be in 2017. If you require info for another year, please contact xxxxx."
Exit Sub
Else
Workbooks.Open ("U:\\(Folder)\\(subfolder)\\(Subfolder)\\2017\\" & workday_date & ".xlsx")
Worksheets("Sheet1").Activate
End If
Set workdaybook = ActiveWorkbook
Dim workdaysheet As Worksheet
Set workdaysheet = ActiveSheet
workdaybook.Activate
workdaybook.Sheets("Sheet1").Copy salesBook.Worksheets(Sheets.Count)
ActiveSheet.Name = "salesdata"
Dim sheetforcopy As Worksheet
Set sheetforcopy = Sheets.Add
sheetforcopy.Name = "Sheetforcopy"
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).EntireRow.Copy
sheetforcopy.Range("A185").PasteSpecial xlPasteValues
sheetforcopy.Range("F" & nStart & ":F" & nEnd).Formula = "=if(D185<>0,SUMIFS(salesdata!E:E,salesdata!B:B,""*""&D185&""*"",salesdata!B:B,""*Total*""),"""")"
sheetforcopy.Range("L" & nStart & ":L" & nEnd).Formula = "=IF(IF(ISNUMBER(B185)=TRUE,COUNTIF(salesdata!B:B,""*""&B185&""*"")-1,"""")=-1,0,IF(ISNUMBER(B185)=TRUE,COUNTIF(salesdata!B:B,""*""&B185&""*"")-1,""""))"
sheetforcopy.Range("F" & nStart & ":F" & nEnd).Copy
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).PasteSpecial xlPasteValues
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).Copy
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).PasteSpecial xlPasteValues
sheetforcopy.Range("L" & nStart & ":L" & nEnd).Copy
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).PasteSpecial xlPasteValues
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).Copy
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).PasteSpecial xlPasteValues
Application.DisplayAlerts = False
Worksheets("salesdata").Delete
Worksheets("sheetforcopy").Delete
Application.DisplayAlerts = True
workdaybook.Close
CurrentWeekSheet.Activate
CurrentWeekSheet.Range("F" & nStart).Select
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
'Errorcatch: MsgBox "Error - (confidential message deleted)."
End Sub
Selection.End(xlDown).Select
Range("L108").Select
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = "=SUM(R[-106]C:R[-1]C)"
Range("L108").Select
Selection.Copy
Range("L108:N108").Select
Application.CutCopyMode = False
Selection.FillRight
need to add a sum function under column L, M, and N with ability to be able to adapt to other spreadsheets where number of rows will change so the sum will not be the same range
This will find the bottom value (row 107 from your sample code) and input a SUM formula in the next row (row 108 in your sample) for columns L, M and N.
dim lr as long
with worksheets("sheet1")
lr = .cells(.rows.count, "L").end(xlup).row
.cells(lr + 1, "L").resize(1, 3).formular1c1 = _
"=sum(r2c:r" & lr & "c)"
end with
The following will insert a formula below the last cell in column B:
Sub MakeColumnSum()
Dim kolumn As String, where As Range
kolumn = "B"
Set where = Range(kolumn & Rows.Count).End(xlUp).Offset(1, 0)
where.Formula = "=SUM(" & kolumn & "1:" & kolumn & where.Row - 1 & ")"
End Sub
I have to loop through a serious of variables to filter the contents of a dataset to paste it to other sheets. The code I have to paste the data is as follows
Sheets("Source").Select
LastRow = ActiveSheet.Range("A1").Offset(ActiveSheet.Rows.Count - 1, 0).End(xlUp).Row
If ActiveSheet.AutoFilterMode = False Then ActiveSheet.AutoFilterMode = True 'Enable Filters if not exists
ActiveSheet.Range("$A$3:$AY$" & LastRow).AutoFilter Field:=4, Criteria1:= _
"SelectionABC"
Range("A3:AY" & LastRow).Copy
Sheets("DestinationX").Select
Range("A4").Select
ActiveSheet.Paste
The source is always the same, but the "SelectionABC" and the "DestinationX" will change. The selection and detonation are paired, so "SelectionABC" goes to sheet "Destination1", "SelectionDEF" goes to sheet "Destination2",...
How can I loop through the selection & destination so that I don't have the repeat the code for each data transfer?
Here is a quick untested code to help you get going.
Dim i, j As Long
Dim alpha As String
Dim b As Boolean : b = False
j = 1
'~~> UPPERCASE ALPHABETIC CHARACTERS IN THE
'~~> ASCII TABLE GO FROM 65="A" TO 91="Z"
For i = 65 To 91
If i = 89 Then '~~> BECAUSE WE ARE LEFT WITH LAST TWO LETTERS "YZ"
alpha = Chr(i) & Chr(i + 1)
b = True '~~> TO COME OUT OF LOOP AFTER "YZ"
Else
alpha = Chr(i) & Chr(i + 1) & Chr(i + 2)
i = i + 2
End If
Sheets("Source").Select
LastRow = ActiveSheet.Range("A1").Offset(ActiveSheet.Rows.Count - 1, 0).End(xlUp).Row
If ActiveSheet.AutoFilterMode = False Then ActiveSheet.AutoFilterMode = True 'Enable Filters if not exists
ActiveSheet.Range("$A$3:$AY$" & LastRow).AutoFilter Field:=4, Criteria1:= _
"Selection" & alpha '~~> ADDED alpha here
Range("A3:AY" & LastRow).Copy
Sheets("Destination" & j).Select '~~> ADDED j HERE
Range("A4").Select
ActiveSheet.Paste
j = j + 1
If b Then Exit For '~~> TO COME OUT OF LOOP AFTER "YZ"
Next
I have written some Excel VBA to add weekday dates, down column "A", for 41 worksheets. The dates build to 90 days out and then have a "Beyond mm/dd/yy" text value in the following cell. The code is run every weekday, with the exception of holidays, and builds the dates over the cell that was previously the text cell. This process works beautifully, except for the first of 41 worksheets, where the added date(s) display as text, even though their "format" will say they are a date. The other 40 display as dates. I have attempted to wrap my calculated dates in CDate() and DateValue(), and both. The closes I came was copying down the above cell, but then I will get non-weekdays, as Excel builds the next autofill. I even tried to revisit the one worksheet with the issue and roll through the IF Then Else again, but, with a defined value for the "Beyond" text row and then reassign the dates - this yielded the same result; so, I have concluded that the issue is likely related to how I have written the IF Then Else portion.
Thank you for any ideas~
Dim count As Integer
Sheets("ABCD").Activate
For count = 1 To 41
'*************************************************************************** ********************
'Inserts Dates for weekdays, until 90 days out, then a "Beyond MM/DD/YY" value for the last date
'***********************************************************************************************
Dim ThisSheet As String
'turn off auto formula calculation
Application.Calculation = xlManual
Range("A1").Activate
'find the current "Beyond" date cell
Columns("A:A").Select
Selection.Find(What:="Beyond", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Range("A" & ActiveCell.Row).Select
'Add business days to column(A:A) until the next business day would be 91 days or greater
Do Until ((Weekday(Range("A" & ActiveCell.Row - 1)) = 6) And _
(DateAdd("w", 3, Range("A" & ActiveCell.Row - 1))) >= (DateAdd("d", 91, Date))) Or _
((Weekday(Range("A" & ActiveCell.Row - 1)) <> 6) And _
(DateAdd("d", 1, Range("A" & ActiveCell.Row - 1))) >= (DateAdd("d", 91, Date)))
If Weekday(Range("A" & ActiveCell.Row - 1)) = 6 Then
ActiveCell.NumberFormat = "m/d/yyyy"
ActiveCell.Value = DateValue(DateAdd("w", 3, Range("A" & (ActiveCell.Row - 1))))
Selection.NumberFormat = "m/d/yyyy"
ElseIf Weekday(Range("A" & ActiveCell.Row - 1)) = 7 Then
ActiveCell.NumberFormat = "m/d/yyyy"
ActiveCell.Value = DateValue(DateAdd("w", 2, Range("A" & (ActiveCell.Row - 1))))
ActiveCell.Select
Selection.NumberFormat = "m/d/yyyy"
Else: ActiveCell.NumberFormat = "m/d/yyyy"
ActiveCell.Value = DateValue(DateAdd("w", 1, Range("A" & (ActiveCell.Row - 1))))
ActiveCell.Select
Selection.NumberFormat = "m/d/yyyy"
End If
Selection.Offset(1, 0).Activate
Loop
'Add in the "Beyond" date, to column(A:A)
ActiveCell.Value = "Beyond " & Format((DateAdd("d", 90, Date)), "mm/dd/yy")
Range("A1").Select
'*****************************************************************************************
'****************************************************************
'Copies down formulas to the last date or "Beyond MM/DD/YYYY" row
'****************************************************************
'Set LastRow Value for end of desired formula range
LTCashSheet_LastRow = Range("A" & Rows.count).End(xlUp).Row
'Set LastRow Value for beginning formulas to copy down
LTCashSheet_BegCopyRange = Range("B" & Rows.count).End(xlUp).Row
Range("B" & LTCashSheet_BegCopyRange & ":N" & LTCashSheet_BegCopyRange).Select
Selection.AutoFill Destination:=Range("B" & LTCashSheet_BegCopyRange & ":N" & LTCashSheet_LastRow), Type:=xlFillDefault
Range("B" & LTCashSheet_BegCopyRange & ":N" & LTCashSheet_LastRow).Select
Columns("A:A").AutoFit
'****************************************************************
'****************************************************************
'Hide Rows 11 through rows prior to today's date row*************
'****************************************************************
Set TheRng = Range("A1", Range("A" & Rows.count).End(xlUp))
CurrDtRow = TheRng.Find(What:=Date, LookAt:=xlWhole).Row
Rows("11:" & (CurrDtRow - 2)).Select
Selection.EntireRow.Hidden = True
Range("A1").Select
'****************************************************************
'Go to next sheet and repeat, through 'count'********************
ActiveSheet.Next.Select
Next count
I found helpful information from Excel VBA date formats. I did not integrate the solution to prevent the above from happening, within my IF THEN ELSE; however, I was able to add some clean up using the function and applying the code to the cells immediately above the "Beyond" value, which were the cells that were a strange hybrid of a String and a Date. I am good to go, but, feel free to comment if you think I should have gone a different route.
Thank you!
Function CellContentCanBeInterpretedAsADate(cell As Range) As Boolean
Dim d As Date
On Error Resume Next
d = CDate(cell.Value)
If Err.Number <> 0 Then
CellContentCanBeInterpretedAsADate = False
Else
CellContentCanBeInterpretedAsADate = True
End If
On Error GoTo 0
End Function
Sub FixDtFrmtWithFnctn()
Dim cell As Range
Dim cvalue As Double
Sheets("NCE1").Select
Set TheRng4 = Range("A1", Range("A" & Rows.count).End(xlUp))
DtFrmtFixRow = TheRng4.Find(What:=("Beyond"), LookAt:=xlPart).Row
Set cell = Range("A" & (DtFrmtFixRow - 1))
If CellContentCanBeInterpretedAsADate(cell) Then
cvalue = CDate(cell.Value)
cell.Value = cvalue
cell.NumberFormat = "m/d/yyyy"
Else
cell.NumberFormat = "General"
End If
Set cell = Range("A" & (DtFrmtFixRow - 2))
If CellContentCanBeInterpretedAsADate(cell) Then
cvalue = CDate(cell.Value)
cell.Value = cvalue
cell.NumberFormat = "m/d/yyyy"
Else
cell.NumberFormat = "General"
End If
Set cell = Range("A" & (DtFrmtFixRow - 3))
If CellContentCanBeInterpretedAsADate(cell) Then
cvalue = CDate(cell.Value)
cell.Value = cvalue
cell.NumberFormat = "m/d/yyyy"
Else
cell.NumberFormat = "General"
End If
End Sub
I have an Excel workbook for our projects that has a Master Sheet that contains all the doors/products for that job. In Column BF contains the number for the stage delivery that product is on. I have got this working correctly (see below) to copy each row containing a product to the sheet for that stage. These are named like this: "Stage 1 Sheet", "Stage 2 Sheet". I have this working for if there are only the two stage deliveries. I want it to do up to about 24 stages.
The problem I have is that my code is going to be very long. Is there a way it can match the value in the stage column to the number on the Stage sheets?
Here is my code:
Sub LineCopy()
Dim LR As Long, i As Long
LR = Sheets("Master Sheet").Range("A" & Rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
For i = 10 To LR
If Sheets("Master Sheet").Range("BF" & i).Value = "1" Then
Sheets("Master Sheet").Range("A" & i).EntireRow.Copy
Sheets("Stage 1 Sheet").Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial (xlPasteValues)
ElseIf Sheets("Master Sheet").Range("BF" & i).Value = "2" Then
Sheets("Master Sheet").Range("A" & i).EntireRow.Copy
Sheets("Stage 2 Sheet").Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial (xlPasteValues)
End If
Next i
Application.CutCopyMode = False
End Sub
This is the layout of my sheets. I will be adding more sheets for up to 24 stages/deliveries:
I have more I want to add to this code as well.
Have you tried to introduce a second FOR Loop within the loop:
Dim LR As Long, i As Long, x As Long
LR = Sheets("Master Sheet").Range("A" & Rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
For i = 10 To LR
For x = 1 To 24
If Sheets("Master Sheet").Range("BF" & i).Value = x Then
Sheets("Master Sheet").Range("A" & i).EntireRow.Copy
Sheets("Stage " & x & " Sheet").Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial (xlPasteValues)
End If
Next x
Next i
Application.CutCopyMode = False
End Sub