the code below allows me to delete rows if a cells contains certain values. now for some reason it takes me a lot of time(30 mins and counting).
' to delete data not meeting criteria
Worksheets("Dashboard").Activate
n1 = Range("n1")
n2 = Range("n2")
Worksheets("Temp Calc").Activate
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
For z = lastrow To 2 Step -1
If Cells(z, 6).Value = "CNF" Or Cells(z, 4).Value <= n1 Or Cells(z, 3).Value >= n2 Then
Rows(z).Delete
End If
Next z
a google search and some talk with forum member sam provided me with two options
to use filter.(i do want to use this).
using arrays to store the entire worksheet and then copy data that only matches my criteria.He was kind enough to help me come up with the following code.But i am not familiar with working on data in an array.
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
lastCol = Cells(1, Column.Count).End(xlRight).Row
arr1 = Range("A1:Z" & lastrow)
ReDim arr2(1 To UBound(arr1, 1), 1 To UBound(arr1, 2))
j = j + 1
For i = 1 To UBound(arr1, 1)
If arr1(i, 6) <> "CNF" And arr1(i, 4) > n1 And arr1(i, 3) < n2 Then
For k = 1 To lastCol
arr2(j, k) = arr1(i, k)
Next k
j = j + 1
End If
Next i
Range(the original bounds) = arr2
my question is is there a faster way of deleting rows in an array other than the ones mentioned above? Or is array or filter the best options i've got.I am open to suggestions.
Update my new code looks like this. it does not filter the date rangeeven if they are hardcoded can anybody tell me what i am doing wrong ?
Option Explicit
Sub awesome()
Dim Master As Workbook
Dim fd As FileDialog
Dim filechosen As Integer
Dim i As Integer
Dim lastrow, x As Long
Dim z As Long
Application.ScreenUpdating = False
Dim sngStartTime As Single
Dim sngTotalTime As Single
Dim ws As Worksheet
Dim FltrRng As Range
Dim lRow As Long
Dim N1 As Date, N2 As Date
sngStartTime = Timer
Sheets("Dashboard").Select
N1 = Range("n1").Value
N2 = Range("n2").Value
Sheets("Temp Calc").Select
'Clear existing sheet data except headers
'Sheets("Temp Calc").Select
'Rows(1).Offset(1, 0).Resize(Rows.Count - 1).ClearContents
'The folder containing the files to be recap'd
Set fd = Application.FileDialog(msoFileDialogFilePicker)
fd.InitialFileName = "G:\Work\" '<----- Change this to where the files are stored.
fd.InitialView = msoFileDialogViewList
'allow multiple file selection
fd.AllowMultiSelect = True
fd.Filters.Add "Excel Files", "*.xls*"
filechosen = fd.Show
'Create a workbook for the recap report
Set Master = ThisWorkbook
If filechosen = -1 Then
'open each of the files chosen
For i = 1 To fd.SelectedItems.Count
Workbooks.Open fd.SelectedItems(i)
With ActiveWorkbook.Worksheets(1)
Range("O2", Range("O" & Cells(Rows.Count, "O").End(xlUp).Row)).Copy Master.Worksheets(2).Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
Range("p2", Range("P" & Cells(Rows.Count, "P").End(xlUp).Row)).Copy Master.Worksheets(2).Range("B" & Rows.Count).End(xlUp).Offset(1, 0)
Range("Q2", Range("Q" & Cells(Rows.Count, "Q").End(xlUp).Row)).Copy Master.Worksheets(2).Range("C" & Rows.Count).End(xlUp).Offset(1, 0)
Range("R2", Range("R" & Cells(Rows.Count, "R").End(xlUp).Row)).Copy Master.Worksheets(2).Range("D" & Rows.Count).End(xlUp).Offset(1, 0)
Range("A2", Range("A" & Cells(Rows.Count, "A").End(xlUp).Row)).Copy Master.Worksheets(2).Range("E" & Rows.Count).End(xlUp).Offset(1, 0)
Range("AC2", Range("AC" & Cells(Rows.Count, "AC").End(xlUp).Row)).Copy Master.Worksheets(2).Range("F" & Rows.Count).End(xlUp).Offset(1, 0)
End With
' Sheets(1).Range("D4", Sheets(1).Range("D" & Sheets(1).Cells(Rows.Count, "D").End(xlUp).Row)).Copy Sheets(2).Range("B" & Sheets(2).Rows.Count).End(xlUp).Offset(1, 0)
ActiveWorkbook.Close (False)
Next i
End If
Set ws = ThisWorkbook.Worksheets("Temp Calc")
'~~> Start Date and End Date
N1 = #5/1/2012#: N2 = #7/1/2012#
With ws
'~~> Remove any filters
.AutoFilterMode = False
'~~> Get the last row
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Identify your data range
Set FltrRng = .Range("A1:F" & lRow)
'~~> Filter the data as per your criteria
With FltrRng
'~~> First filter on blanks
.AutoFilter Field:=6, Criteria1:="="
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'~~> Delete the filtered blank rows
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
ws.ShowAllData
'~~> Next filter on Start Date
.AutoFilter Field:=3, Criteria1:="<" & N1, Operator:=xlAnd
'~~> Finally filter on End Date
.AutoFilter Field:=4, Criteria1:=">" & N2, Operator:=xlAnd
'~~> Filter on col 6 for CNF
'.AutoFilter Field:=6, Criteria1:="CNF"
'~~> Delete the filtered rows
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
'~~> Remove any filters
.AutoFilterMode = False
End With
sngTotalTime = Timer - sngStartTime
MsgBox "Time taken: " & (sngTotalTime \ 60) & " minutes, " & (sngTotalTime Mod 60) & " seconds"
Application.Goto (ActiveWorkbook.Sheets("Dashboard").Range("A4"))
Sheets("Dashboard").Select
Application.ScreenUpdating = True
End Sub
this works for me ..... thank you everyone.... it is achieved using an advanced filter
Dim x, rng As Range
x = Array("BENIN-00001", "BENIN-00002", "BENTB-0001", "BENTB-0002", "BENTB-0003", "BENTB-0004", _
"BENTB-0005", "BENTB-0006", "BENTB-0007", "BENTB-0008", "BENTH-00001", "CRPTB-00002", "GDSGL-00001", _
"GDSIN-00001", "GDSIN-00002", "GDSIN-00003", "LSIED-00001", "LSIES-00001", "PRSGS-00001", "PRSGS-00002", _
"PRSGS-00003", "PRSGS-00006", "PRSGS-00007", "PRSGS-00008", "PRSPS-00001", "PRSPS-00002", "PRSTB-0001", _
"PRSTB-0002", "PRSTB-0003", "PRSTB-0004", "PRSTB-0005", "PRSTB-0006", "PRSTB-0007", "SNMIN-00001", "SNMIN-00002", _
"TRGIN-00001", "TRGIN-00002", "TRGTH-00001", "BENEU-00002", "BENEU-00003", "GDSEU-00002", "GDSEU-00003", _
"GDSEU-00004", "PRSGS-00005", "PRSGS-00061", "PRSPS-00004", "PRSPS-00005", "TRGEU-00002", "TRGGB-00001", _
"BENMX-00001", "BENUS-00001", "BENUS-00002", "GDSCA-00001", "GDSGL-00002", "GDSMX-00001", "GDSUS-00001", _
"GDSUS-00002", "LSIPP-00001", "PRSGS-00004", "PRSPS-00003", "TRGMX-00001", "TRGUS-00001")
With Sheets("Temp Calc").Cells(1).CurrentRegion
On Error Resume Next
.Columns(6).SpecialCells(4).EntireRow.Delete
On Error GoTo 0
Set rng = .Offset(, .Columns.Count + 1).Cells(1)
.Cells(1, 5).Copy rng
rng.Offset(1).Resize(UBound(x) + 1).Value = Application.Transpose(x)
.AdvancedFilter 1, rng.CurrentRegion
.Offset(1).EntireRow.Delete
On Error Resume Next
.Parent.ShowAllData
On Error GoTo 0
rng.EntireColumn.Clear
End With
Related
I have a monthly report with 25K-30K lines from which I want to delete lines based on cell values. The report has a dynamic number of rows each month but the number of columns are fixed, from A to X. I am using the For Next Loop to search into the cells for the values that will trigger the deletion of rows, in the worksheet "Data" of the report. There is a second sheet in this report named "Public accounts" where the macro searches and adds a tag (public or private) into each of the rows in the "Data" sheet. It then checks several conditions (like if the values of the cells in columns R and S are equal then the line is deleted) using the For Next loop and if they are true the lines are deleted in the "Data" sheet of the report.
My problem is that it takes far too long to run (10-15 mins) in its condition. Can you please help me to speed it up? I am attaching the code that I am using.
Sub Format_Report()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Worksheets("Data").Activate
LR = Range("A" & Rows.Count).End(xlUp).Row
Range("X2").Formula = "=if(isnumber(Match(A2,'Public accounts'!A:A,0)),""Public"",""Private"")"
Range("X2").AutoFill Destination:=Range("X2:X" & LR)
Last = Cells(Rows.Count, "A").End(xlUp).Row
For i = Last To 1 Step -1
If (Cells(i, "R").Value) = (Cells(i, "S").Value) Then
Cells(i, "A").EntireRow.Delete
End If
Next i
For i = Last To 1 Step -1
If (Cells(i, "G").Value) = "ZRT" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
For i = Last To 1 Step -1
If (Cells(i, "G").Value) = "ZAF" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
For i = Last To 1 Step -1
If (Cells(i, "G").Value) = "E" Then
Cells(i, "A").EntireRow.Delete
End If
Next i
For i = Last To 1 Step -1
If Cells(i, 24) = "Public" Then
Cells(i, 24).EntireRow.Delete
End If
Next i
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Please, test the next code. It should work very fast, using arrays, sort, delete at once, resort and clear the helper sort column:
Sub Format_Report()
Dim wsD As Worksheet, lastRD As Long, lastCol As Long
Dim arr, arrMark, arrSort, i As Long, boolFound As Boolean
Set wsD = ActiveSheet 'Worksheets("Data")
lastRD = wsD.Range("A" & wsD.rows.count).End(xlUp).row
lastCol = wsD.UsedRange.column + wsD.UsedRange.Columns.count + 1
arrSort = Evaluate("row(1:" & lastRD & ")") 'build an array to resort after deletion
wsD.Range("X2:X" & lastRD).Formula = "=if(isnumber(Match(A2,'Public accounts'!A:A,0)),""Public"",""Private"")"
wsD.Calculate
arr = wsD.Range("G1:X" & lastRD).Value2 'place the range in an array for faster iteration
ReDim arrMark(1 To UBound(arr), 1 To 1) 'reDim the array to keep deletion marks
For i = 1 To lastRD
If arr(i, 12) = arr(i, 13) And (arr(i, 12) <> "") Or _
arr(i, 1) = "ZRT" Or _
arr(i, 1) = "ZAF" Or _
arr(i, 1) = "E" Or _
arr(i, 18) = "Public" Then
arrMark(i, 1) = "Del": boolFound = True 'write in array an boolFound = true to confirm at least a row to be deleted
End If
Next i
Application.ScreenUpdating = False: Application.DisplayAlerts = False
wsD.cells(1, lastCol).Resize(UBound(arrMark), 1).Value2 = arrMark 'drop arrMark content at once:
wsD.cells(1, lastCol + 1).Resize(UBound(arrSort), 1).Value2 = arrSort
'sort the range based on arr column:
wsD.Range("A1", wsD.cells(lastRD, lastCol + 1)).Sort key1:=wsD.cells(1, lastCol), Order1:=xlAscending, Header:=xlNo ' sort the range by deletion column
With wsD.cells(1, lastCol).Resize(lastRD, 1)
If boolFound Then 'if at least a row to be deleted:
.SpecialCells(xlCellTypeConstants).EntireRow.Delete
End If
End With
'Resort the range based on arrSort column:
wsD.Range("A1", wsD.cells(lastRD, lastCol + 1)).Sort key1:=wsD.cells(1, lastCol), Order1:=xlAscending, Header:=xlNo
wsD.cells(lastRD, lastCol + 1).EntireColumn.ClearContents 'clear the column with the initial order
Application.ScreenUpdating = True: Application.DisplayAlerts = True
MsgBox "Ready..."
End Sub
I am trying to eliminate line items that cancel each other out.
For example, below the two rows that add to zero would be deleted (i.e., 87.1 and -87.1).
-87.1
890
87.1
898989
The code that I am using mostly works but in cases where there are numerous lines with the same values it is deleting all of them instead of just one matching value per observation. For example, below, I would want it to cancel out two of the -87.1s and two of the 87.1s but one would be leftover because there is no number directly offsetting it.
-87.1
890
87.1
898989
87.1
-87.1
-87.1
Sub x()
Dim n As Long, rData As Range
Application.ScreenUpdating = False
n = Range("C" & Rows.Count).End(xlUp).Row
Range("AV2:AV" & n).Formula = "=IF(I2=0,0,COUNTIFS($C$2:$C$" & n & ",C2,$I$2:$I$" & n & ",-I2))"
With ActiveSheet
.AutoFilterMode = False
.Rows(1).AutoFilter field:=48, Criteria1:=">0"
With .AutoFilter.Range
On Error Resume Next
Set rData = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rData Is Nothing Then
rData.EntireRow.Delete shift:=xlUp
End If
End With
.AutoFilterMode = False
End With
Application.ScreenUpdating = True
End Sub
I think you need something like this:
Sub DeleteOppositeNumbers()
Dim Fnd As Range, r As Long
'By: Abdallah Ali El-Yaddak
Application.ScreenUpdating = False
'Loop through the column bottom to top.
For r = Range("C" & Rows.Count).End(xlUp).Row To 2 Step -1
If Cells(r, 3).Value > 0 Then 'If the value is positive
'Sreach for it's opposite
Set Fnd = Columns(3).Find(-Cells(r, 3).Value, LookAt:=xlWhole)
'If found, delete both.
If Not Fnd Is Nothing Then Rows(r).Delete: Fnd.EntireRow.Delete
End If
Next
'Just to restore normal behaviour of sreach
Set Fnd = Columns(3).Find(Cells(3, 2).Value, LookAt:=xlPart)
Application.ScreenUpdating = True
End Sub
Perhaps Something Simpler:
Sub x()
Dim ar() As Variant
Dim i As Integer
Dim j As Integer
Dim n As Integer
n = Range("C" & Rows.Count).End(xlUp).Row
Range("AV2:AV" & n).Formula = "=IF(I2=0,0,COUNTIFS($C$2:$C$" & n & ",C2,$I$2:$I$" & n & ",-I2))"
ar = ActiveSheet.Range("AV2:AV" & last).Value
For i = LBound(ar) To UBound(ar)
For j = LBound(ar) To UBound(ar)
If i <> j Then
If ar(i, 1) = ar(j, 1) Then
ar(i, 1) = ""
ar(j, 1) = ""
End If
End If
Next
Next
For i = LBound(ar) To UBound(ar)
ActiveSheet.Range("AV" & i + 1).Value = ar(i, 1)
Next
ActiveSheet.Range("AV2:AV" & last).SpecialCells(xlCellTypeBlanks).Delete xlUp
End Sub
I have tried and tested this one.
You could try:
Option Explicit
Sub test()
Dim arr As Variant
Dim LastRow As Long, i As Long, j As Long
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
arr = Range("A1:A" & LastRow)
For i = UBound(arr) To LBound(arr) Step -1
For j = UBound(arr) - 1 To LBound(arr) Step -1
If arr(i, 1) + arr(j, 1) = 0 Then
.Rows(i).EntireRow.Delete
.Rows(j).EntireRow.Delete
Exit For
End If
Next j
Next i
End With
End Sub
I have a table which contains merged cells both column and rows as shown in attached picture. I want to unmerge "Only" rows while leaving columns merged. Consider the following snippet of table. In the image attached "Contract
For y = 1 To lRow
p = 1
c = y
d = 1
z = lRow + y
t = Cells(y, 1).Value
For x = 1 To t
Cells(z, p).Value = Cells(c, d).Value
Cells(c, d).Select
' Debug.Print
Selection.End(xlToRight).Select
c = ActiveCell.Row
d = ActiveCell.Column
p = p + 1
Next
Next
Sub ColorMergedCells()
Dim c As Range
Dim startcolumn, endcolumn, startrow, endrow As Long
For Each c In ActiveSheet.UsedRange
If c.MergeCells And c.MergeArea.Rows.Count >= 2 Then
c.Interior.ColorIndex = 28
With c.MergeArea.Rows
.UnMerge
' .Formula = c.Formula
End With
'
'startcolumn = ActiveCell.Column
'endcolumn = Selection.Columns.Count + startcolumn - 1
'startrow = ActiveCell.Row
'endrow = Selection.Rows.Count + startrow - 1
End If
Next
End Sub
Based on your snapshot of requirements , I have wrote a very simple code which shall appear to be crude but I have kept it this way so that you can adjust its various elements as per your actual data. Sample data taken by me and results obtained are shown in the snapshot pasted below, which is followed by code.
Sub Merge_unmerge()
Dim wb As Workbook
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim LastRow As Long
Dim LastCol As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
With ws
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
Set rng = ws.Range("A1:D" & LastRow)
For Each cell In rng
cell.UnMerge
Next cell
For i = 2 To LastRow
If Range("A" & i) = "" Then
Range("A" & i).Value = Range("A" & i - 1).Value
End If
Next i
For i = 2 To LastRow
If Range("D" & i) = "" Then
Range("D" & i).Value = Range("D" & i - 1).Value
End If
Next i
For i = 1 To LastRow Step 2
Range("B" & i & ":C" & i).Merge
Range("B" & i & ":C" & i).HorizontalAlignment = xlCenter
Next i
End Sub
Never mind. I solved for the issue at hand. Posting if it helps others.
Sub ColorMergedCells()
Dim c As Range
Dim startcolumn, endcolumn, startrow, endrow As Long
For Each c In ActiveSheet.UsedRange
If c.MergeCells And c.MergeArea.Rows.Count >= 2 Then
c.Interior.ColorIndex = 28
startcolumn = c.Column
endcolumn = c.MergeArea.Columns.Count + startcolumn - 1
startrow = c.Row
endrow = c.MergeArea.Rows.Count + startrow - 1
With c.MergeArea.Rows
.UnMerge
.Formula = c.Formula
End With
For J = startrow To endrow
Application.DisplayAlerts = False
Range(Cells(J, startcolumn), Cells(J, endcolumn)).Merge
Application.DisplayAlerts = True
Next
End If
Next
End Sub
Struggling a bit with this code, I haven't ever had to reference one column and copy and paste to another tab in VBA so here goes..
I have an excel document with a table on it similar to below:
I need my code to look in column A find the first name, in this case, Nicola. I then want it to look at column B and check to see if she has the word "Internet" appear in any of the records stored against her, as she does the code will ignore her and move down to the next name on the list, in this case, Graham. It will then look to column B and check if he has the word "Internet". As he doesn't, the code needs to copy the Information from column A & B in relation to this persons name and paste the information into another sheet in the workbook.
Sub Test3()
Dim x As String
Dim found As Boolean
Range("B2").Select
x = "Internet"
found = False
Do Until IsEmpty(ActiveCell)
If ActiveCell.Value = x Then
found = True
Exit Do
End If
ActiveCell.Offset(1, 0).Select
Loop
If found = False Then
Sheets("Groupings").Activate
Sheets("Groupings").Range("A:B").Select
Selection.Copy
Sheets("Sheet1").Select
Sheets("Sheet1").Range("A:B").PasteSpecial
End If
End Sub
Any help would be greatly appreciated.
Thanks
Paula
Private Sub Test3()
Application.ScreenUpdating = False
Set sh1 = Sheets("Groupings") 'data sheet
Set sh2 = Sheets("Sheet1") 'paste sheet
myVar = sh1.Range("D1")
Lastrow = sh1.Range("B" & Rows.Count).End(xlUp).Row
For i = 2 To Lastrow '2 being the first row to test
If Len(sh1.Range("A" & i)) > 0 Then
Set myFind = Nothing
If WorksheetFunction.CountA(sh1.Range("A" & i, "A" & Lastrow)) > 1 Then
If Len(sh1.Range("A" & i + 1)) = 0 Then
nextrow = sh1.Range("A" & i).End(xlDown).Row - 1
Else
nextrow = nextrow + 1
End If
Set myFind = sh1.Range("B" & i, "B" & nextrow).Find(What:=myVar, LookIn:=xlFormulas, LookAt:=xlWhole)
Else
nextrow = Lastrow
Set myFind = sh1.Range("B" & i, "B" & nextrow).Find(What:=myVar, LookIn:=xlFormulas, LookAt:=xlWhole)
End If
If myFind Is Nothing Then
sh1.Range("A" & i, "B" & nextrow).Copy
sh2.Range("A" & sh2.Range("B" & Rows.Count).End(xlUp).Row + 1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End If
End If
Next
End Sub
I don't clearly see the structure of your data, but assuming the original data is in Worksheet Data, I think the following is going to do what you want (edited to search for two conditions).
Private Sub Test3()
Dim lLastRow as Long
Dim a as Integer
Dim i as Integer
Dim sText1 As String
Dim sText2 As String
sText1 = Worksheets("Data").Cells(1, 5).Value 'search text #1, typed in E1
sText2 = Worksheets("Data").Cells(2, 5).Value 'search text #2, typed in E2
lLastRow = Cells(Rows.Count, 1).End(xlUp).Row
a = 1
For i = 2 To lLastRow
If (Worksheets("Data").Cells(i, 1).Value <> "") Then
If (Worksheets("Data").Cells(i, 2).Value <> sText1 And Worksheets("Data").Cells(i + 1, 2).Value <> sText1 And Worksheets("Data").Cells(i, 2).Value <> sText2 And Worksheets("Data").Cells(i + 1, 2).Value <> sText2) Then
Worksheets("Groupings").Cells(a, 1).Value = Worksheets("Data").Cells(i, 1).Value
Worksheets("Groupings").Cells(a, 2).Value = Worksheets("Data").Cells(i, 2).Value
Worksheets("Groupings").Cells(a, 3).Value = Worksheets("Data").Cells(i + 1, 2).Value
a = a + 1
End If
End If
Next
End Sub
I have a list of Projects:
Project A
Project B
Project C
and milestones for each project with a date for the milestone:
Project A Milestone 1 01/01/2015
Project A Milestone 2 01/02/2015
Project A Milestone 3 01/03/2015
Project B Milestone 1 01/04/2015
I am looking to generate a type of timeline for all of the projects in one sheet, with milestones displayed in their respective month.
Column A would have the list of projects and row 1 have months, then to display the milestone where the month and project match.
So far I have been able to extract the list of projects using a macro:
Sub UniqueList()
Dim rListPaste As Range
Dim iReply As Integer
On Error Resume Next
Set rListPaste = Application.InputBox _
(Prompt:="Please select the destination cell", Type:=8)
If rListPaste Is Nothing Then
iReply = MsgBox("No range nominated," _
& " terminate", vbYesNo + vbQuestion)
If iReply = vbYes Then Exit Sub
End If
Range("A1", Range("A65536").End(xlUp)).AdvancedFilter _
Action:=xlFilterCopy, CopyToRange:=rListPaste.Cells(1, 1), Unique:=True
End Sub
After this I am pretty stuck. Any advice would be greatly appreciated.
I've done a similar setup to develop a milestone chart based on the current week. I've modified it to match your requirements:
Sub CreateMilestoneChart()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim LastRow As Long
Dim i As Long, j As Long
Dim FirstMonth As Long
Dim FirstYear As Long
Dim LastMonth As Long
Dim LastYear As Long
Dim curRange As Range
Set ws1 = Worksheets("Project List")
Set ws2 = Worksheets("Milestone Chart")
Application.ScreenUpdating = False
LastRow = ws1.Cells(Rows.Count, 1).End(xlUp).Row
'-----You will want to modify or remove these lines once-----
'-----you get the sheet formatted the way you want-----------
ws2.Cells.Clear
ws2.Range("A1").Value = "Milestone Chart"
ws2.Range("A2").Value = "Generated on " & Date
ws2.Range("A3").Value = "Month:"
ws2.Range("A3").HorizontalAlignment = xlRight
'------------------------------------------------------------
ws1.Range("A1:C" & LastRow).Copy
ws2.Range("A4").PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone _
, SkipBlanks:=False, Transpose:=False
LastRow = LastRow + 3
For i = 4 To LastRow
ws2.Cells(i, 1).Value = ws2.Cells(i, 1).Value & " " & ws2.Cells(i, 2).Value
Next i
ws2.Range("A4:A" & LastRow).HorizontalAlignment = xlRight
ws2.Range("B4:B" & LastRow).Delete Shift:=xlToLeft
With ws2.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("B4:B" & LastRow), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SetRange Range("A4:B" & LastRow)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
FirstMonth = DatePart("m", ws2.Range("B4").Value)
FirstYear = DatePart("yyyy", ws2.Range("B4").Value)
LastMonth = DatePart("m", ws2.Range("B" & LastRow).Value)
LastYear = DatePart("yyyy", ws2.Range("B" & LastRow).Value)
ws2.Range("B3").Value = CDate(FirstMonth & "/" & FirstYear)
Set curRange = ws2.Range("B3")
i = 1
Do Until DatePart("m", curRange.Value) = LastMonth And DatePart("yyyy", curRange.Value) = LastYear
Set curRange = ws2.Cells(3, i + 2)
curRange.Value = DateAdd("m", 1, ws2.Cells(3, i + 1).Value)
i = i + 1
Loop
ws2.Cells(3, i + 2).Value = DateAdd("m", 1, ws2.Cells(3, i + 1).Value)
For i = 4 To LastRow
j = 2
Do Until ws2.Cells(i, j).Value >= ws2.Cells(3, j).Value And ws2.Cells(i, j).Value < ws2.Cells(3, j + 1).Value
ws2.Cells(i, 2).Insert Shift:=xlToRight
ws2.Cells(i, 2).Value = "'-----------------"
j = j + 1
Loop
Next i
Application.ScreenUpdating = True
End Sub
It's not very clean by any means, but it will work. You will need to modify it to fit your needs.