This question is not that much about any application but more about finding algorithm that would work.
I have a code for moving data from Excel to MS Project. My current solution works until I don't have empty rows in between.
I have data starting from row 19. So 19 - 18 is first task, 20 - 18 is second task and so on. However once I have empty rows in between, let's say:
Task 1 (row 19)
Task 2 (row 20)
Task 3 (row 22)
My code is not working as there is task 2 in MS Project and next should be number 3, however 22 - 18 is 4. Anybody have any good solution for this?
Here is the code:
' Move data to project
For i = 19 To lRow
strValue = WorksheetToOperate.Range("C" & i)
strStartDate = WorksheetToOperate.Range("E" & i)
strEndDate = WorksheetToOperate.Range("F" & i)
Strresource = WorksheetToOperate.Range("J" & i)
' Import tasks
If (WorksheetToOperate.Range("C" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("C" & i).Value)) Then
newproj.Tasks.Add strValue
End If
' Import start date
If (WorksheetToOperate.Range("E" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("E" & i).Value)) Then
newproj.Tasks(i - 18).Start = strStartDate
End If
' Import end date
If (WorksheetToOperate.Range("F" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("F" & i).Value)) Then
newproj.Tasks(i - 18).Finish = strEndDate
End If
' Import recources
If Not ExistsInCollection(newproj.Resources, Strresource) Then _
newproj.Resources.Add.Name = Strresource
If (WorksheetToOperate.Range("J" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("J" & i).Value)) Then
newproj.Tasks(i - 18).ResourceNames = Strresource
End If
Next i
The problem of skipping empty rows is greatly simplified by using a Task object variable to keep track of the task that was just added. Also, use the With statement with the Worksheet object to further simplify the code.
Sub CreateSchedule()
Dim prj As MSProject.Application
Set prj = CreateObject("MSProject.Application")
prj.Visible = True
Dim newProj As MSProject.Project
Set newProj = prj.Projects.Add
Dim i As Long
Dim t As MSProject.Task
For i = 19 To 28 'lRow
With WorksheetToOperate
If Not IsEmpty(.Range("C" & i)) Then
Set t = newProj.Tasks.Add(CStr(.Range("C" & i)))
t.Start = CDate(.Range("E" & i))
t.Finish = CDate(Range("F" & i))
t.ResourceNames = CStr(.Range("J" & i))
End If
End With
Next i
End Sub
I have managed to get it working with additional variable m:
Dim m
m = 0
' Move data to project
For i = 19 To lRow
If IsEmpty(WorksheetToOperate.Range("C" & i).Value) Then
m = m + 1
Else
strValue = WorksheetToOperate.Range("C" & i)
strStartDate = WorksheetToOperate.Range("E" & i)
strEndDate = WorksheetToOperate.Range("F" & i)
Strresource = WorksheetToOperate.Range("J" & i)
' Import tasks
If (WorksheetToOperate.Range("C" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("C" & i).Value)) Then
newproj.Tasks.Add strValue
End If
' Import start date
If (WorksheetToOperate.Range("E" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("E" & i).Value)) Then
newproj.Tasks(i - (18 + m)).Start = strStartDate
End If
' Import end date
If (WorksheetToOperate.Range("F" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("F" & i).Value)) Then
newproj.Tasks(i - (18 + m)).Finish = strEndDate
End If
' Import recources
If Not ExistsInCollection(newproj.Resources, Strresource) Then _
newproj.Resources.Add.Name = Strresource
If (WorksheetToOperate.Range("J" & i).Value <> "") And _
(Not IsError(WorksheetToOperate.Range("J" & i).Value)) Then
newproj.Tasks(i - (18 + m)).ResourceNames = Strresource
End If
End If
Next i
Related
I have a very large data set that gets updated multiple times a day. It can vary from 1000-20000 entries. I have a macro in place that searches for specific criteria and makes a new table from that data and works but it takes a very long time to sift through all the points. I want to know if there is a more eloquent way to achieve the same result.
I tried a new different methods of the same thing. Poked around at other solutions but could not get them to fit what I needed. I even tried the advanced filtering tables but to no avail.
Function AgedDivert()
'Pull from scraped data to display compact data set
On Error GoTo ErrorHandler
ufProgress.Caption = "Loading Aged Divert"
ufProgress.LabelProgress.Width = 0
pasterow = 31
sname = "Aged Divert Report"
ThisWorkbook.Sheets(sname).Rows(30 & ":" & 999999).Clear
ThisWorkbook.Sheets("Temp").Range("1:1").Copy ThisWorkbook.Sheets(sname).Range("30:30")
RowCount = WorksheetFunction.CountA(ThisWorkbook.Sheets("Scraped Data").Range("A:A"))
'Create new data sort by age and location
For i = 2 To RowCount
pctComplete = (i - 2) / (RowCount - 2)
'Filter out Direct Loads, PA2, Less than 180 Minutes, Secondary, not diverted
If Len(ThisWorkbook.Sheets("Scraped Data").Range("D" & i).Value) <> 2 And _
(ThisWorkbook.Sheets("Scraped Data").Range("J" & i).Value = "Ship Sorter" Or _
ThisWorkbook.Sheets("Scraped Data").Range("K" & i).Value = "Divert Confirm") And _
ThisWorkbook.Sheets("Scraped Data").Range("D" & i).Value <> "" And _
ThisWorkbook.Sheets("Scraped Data").Range("M" & i).Value > 180 And _
ThisWorkbook.Sheets("Scraped Data").Range("I" & i).Value <> "Left to Pick" And _
InStr(1, ThisWorkbook.Sheets("Scraped Data").Range("C" & i).Value, "Location") = 0 And _
(InStr(1, ThisWorkbook.Sheets("Scraped Data").Range("C" & i).Value, "Warehouse A") > 0 Or _
InStr(1, ThisWorkbook.Sheets("Scraped Data").Range("C" & i).Value, "Warehouse C") > 0 Or _
InStr(1, ThisWorkbook.Sheets("Scraped Data").Range("C" & i).Value, "PA") = 0) Then
ThisWorkbook.Sheets("Scraped Data").Range(i & ":" & i).Copy ThisWorkbook.Sheets(sname).Range(pasterow & ":" & pasterow)
pasterow = pasterow + 1
End If
ufProgress.LabelProgress.Width = pctComplete * ufProgress.FrameProgress.Width
ufProgress.Repaint
Next i
ufProgress.Caption = "Loading Complete. Cleaning Data"
'Remove Unnecessary Data
ThisWorkbook.Sheets(sname).Columns("R").Delete
ThisWorkbook.Sheets(sname).Columns("Q").Delete
ThisWorkbook.Sheets(sname).Columns("O").Delete
ThisWorkbook.Sheets(sname).Columns("N").Delete
ThisWorkbook.Sheets(sname).Columns("L").Delete
ThisWorkbook.Sheets(sname).Columns("K").Delete
ThisWorkbook.Sheets(sname).Columns("J").Delete
ThisWorkbook.Sheets(sname).Columns("H").Delete
ThisWorkbook.Sheets(sname).Columns("F").Delete
ThisWorkbook.Sheets(sname).Columns("E").Delete
ThisWorkbook.Sheets(sname).Range("C30:C999999").Delete
ThisWorkbook.Sheets(sname).Range("B30:B999999").Delete
'Set Data as Table
ThisWorkbook.Sheets(sname).ListObjects.Add(xlSrcRange, ThisWorkbook.Sheets(sname).Range("A30:F" & pasterow), , xlYes).Name = "AgedDivert"
AgedDivert = True
Exit Function
ErrorHandler:
AgedDivert = False
Debug.Print "Error occured in Aged Divert"
Debug.Print Err.Number & ": " & Err.Description
End Function
Copy the data to an array, filter to another array and copy back to sheet. 20,000 rows should take a few seconds.
Function AgedDivert()
Dim wb As Workbook
Dim wsData As Worksheet, wsReport As Worksheet, wsTemp As Worksheet
Dim arData, arReport
Dim lastrow As Long, i As Long, r As Long
Dim colC, colD, colI, colJ, colK, colM, msg As String
Dim t0 As Single: t0 = Timer
Const RPT_NAME = "Aged Divert Report"
'Pull from scraped data to display compact data set
On Error GoTo ErrorHandler
Set wb = ThisWorkbook
With wb
Set wsData = .Sheets("Scraped Data")
Set wsReport = .Sheets(RPT_NAME)
Set wsTemp = .Sheets("Temp")
End With
' copy data
With wsData
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
' copy sheet to array
arData = .Range("A1:P" & lastrow)
ReDim arReport(1 To lastrow, 1 To 6) ' A to F
For i = 2 To lastrow
colC = arData(i, 3)
colD = arData(i, 4)
colI = arData(i, 9)
colJ = arData(i, 10)
colK = arData(i, 11)
colM = arData(i, 13)
'Filter out Direct Loads, PA2, Less than 180 Minutes,
'Secondary, not diverted
If Len(colD) <> 2 And colD <> "" And _
(colJ = "Ship Sorter" Or colK = "Divert Confirm") _
And colM > 180 _
And colI <> "Left to Pick" _
And InStr(1, colC, "Location") = 0 And _
(InStr(1, colC, "Warehouse A") > 0 Or _
InStr(1, colC, "Warehouse C") > 0 Or _
InStr(1, colC, "PA") = 0) Then
r = r + 1 ' report row
arReport(r, 1) = arData(i, 1) ' A
arReport(r, 2) = arData(i, 4) ' D
arReport(r, 3) = arData(i, 7) ' G
arReport(r, 4) = arData(i, 9) ' I
arReport(r, 5) = arData(i, 13) ' M
arReport(r, 6) = arData(i, 16) ' P
End If
Next i
End With
' output
With wsReport
' delete existing table
.Rows("30:" & .Rows.Count).Clear
.Range("A30:F30") = Array("Col A", "Col D", "Col G", "Col I", "Col M", "Col P")
If r = 0 Then
MsgBox "No data to report", vbExclamation
Else
' copy rows and set Data as Table
.Range("A31").Resize(r, 6) = arReport
.ListObjects.Add(xlSrcRange, .Range("A30:F" & 30 + r), xlYes).Name = "AgedDivert"
End If
End With
msg = lastrow - 1 & " rows scanned from " & wsData.Name & vbLf & _
r & " rows copied to " & wsReport.Name
MsgBox msg, vbInformation, Format(Timer - t0, "0.0 secs")
AgedDivert = True
Exit Function
ErrorHandler:
AgedDivert = False
Debug.Print "Error occured in Aged Divert"
Debug.Print Err.Number & ": " & Err.Description
End Function
Sub myfunction()
Dim convert_i, convert_k As String
Dim i, j, k, l As Long
For i = 2 To 583
For k = i + 1 To 583
j = InStr(Range("F" & k).Text, Range("F" & i).Text)
If j > 0 Then
l = InStr(Range("F" & k).Text, " \ ")
If l > 1 Then
convert_i = Range("F" & i).Text & ""
convert_k = Range("F" & k).Text & ""
pos = InStrRev(convert_k, convert_i) - 1
Range("F" & k).Value = Right(convert_k, Len(convert_i) - pos)
Range("F" & k).Value = Range("F" & i).Text + Range("F" & k).Text
Else:
Range("F" & k).Value = Range("F" & i).Value + " \ " + Range("F" & k).Value
End If
End If
Next k
Next i
MsgBox ("Finished ")
End Sub
The code works for the most part however it's inconsistent and I'm baffled as to why. The desired result is like
CP \ CP01 \ CP0103
And through the document I think like at least a good 70 percent is of this format but I do not have the time to go trough remaining 30 percent manually. I would very much appreciate any help.
Please check images below:
The expected result:
enter image description here
Create a Tree
Adjust the values in the constants section.
Option Explicit
Sub createTree()
Const wsName As String = "Sheet1"
Const First As String = "F2"
Const len1 As Long = 2
Const len2 As Long = 4
Const Sep As String = " \ "
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim rg As Range
With wb.Worksheets(wsName).Range(First)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Sub
Set rg = .Resize(lCell.Row - .Row + 1)
End With
Dim Data As Variant: Data = rg.Value ' assuming there is data in F3 at least
Dim sLen As Long: sLen = Len(Sep)
Dim tLen As Long: tLen = len1 + sLen + len2
Dim cString As String
Dim cPref1 As String
Dim cPref2 As String
Dim r As Long
For r = 1 To UBound(Data, 1)
cString = Trim(Data(r, 1))
Select Case Len(cString)
Case len1
cPref1 = cString
cPref2 = ""
Data(r, 1) = cString
Case len2
cPref2 = cPref1 & Sep & cString
Data(r, 1) = cPref2
Case Else
If Len(cPref2) = tLen Then
cPref2 = cPref2 & Sep & cString
Else
cPref2 = Left(cPref2, tLen) & Sep & cString
End If
Data(r, 1) = cPref2
End Select
Next r
rg.Value = Data
End Sub
Sub myfunction()
Dim convert_i, convert_k, last_part As String
Dim i, j, k, l As Long
For i = 2 To 583
For k = i + 1 To 583
j = InStr(Range("F" & k).Text, Range("F" & i).Text)
If j > 0 Then
l = InStrRev(Range("F" & k).Text, " \ ")
If l > 0 Then
convert_i = Range("F" & i).Value
convert_k = Range("F" & k).Value
last_part = Right(convert_k, Len(convert_k) - l - 2)
Range("F" & k).Value = Range("F" & i).Text & " \ " & last_part
Else:
Range("F" & k).Value = Range("F" & i).Value & " \ " & Range("F" & k).Value
End If
End If
Next k
Next i
MsgBox ("Finished ")
End Sub
I realised my implementation was terrible, I was confused by getting it almost 70 percent right. The above code got the job done. Might as well delete the question as I don't think it would be of help to anybody.
I got this code but it doesn't seem to run all the way to the end. Gets stuck and debugger just highlights either the Loop keyword or i = i + 1 row. What am I doing wrong?
I tried If statement or For … Next but nothing seems to work.
Sub Macro1()
'
' Macro1 Macro
'
Dim i As Integer
i = 2
Do Until i > 586
Range("B2").Formula = "=sheet2!CS" & i & ""
Range("B3").Formula = "=sheet2!CR" & i & ""
Range("B4").Formula = "=sheet2!CQ" & i & ""
Range("B5").Formula = "=sheet2!CP" & i & ""
Range("B6").Formula = "=sheet2!CO" & i & ""
Range("B7").Formula = "=sheet2!CN" & i & ""
Range("B8").Formula = "=sheet2!CM" & i & ""
Range("B9").Formula = "=sheet2!CL" & i & ""
Range("B10").Formula = "=sheet2!CK" & i & ""
Range("B11").Formula = "=sheet2!CJ" & i & ""
Range("B12").Formula = "=sheet2!CI" & i & ""
Range("B13").Formula = "=sheet2!CH" & i & ""
Range("B14").Formula = "=sheet2!CG" & i & ""
'Copy and PasteSpecial a Range
Range("AL18").Copy
Worksheets("Sheet2").Range("CV" & i & "").PasteSpecial Paste:=xlPasteValues
i = i + 1
Loop
End Sub
Doesn't seem like there's any problems with the code when I tested it..
Here's your code albeit made shorter and see if it works.
Sub Macro1()
Dim i As Long, j As Long
Dim colltr As String
For i = 2 To 586
For j = 2 To 14
colltr = Split(Cells(1, 99 - j).Address, "$")(1)
Range("B" & j).Formula = "=sheet2!" & colltr & i
Next j
'Copy and PasteSpecial a Range
Worksheets("Sheet2").Range("CV" & i & "").value = Range("AL18").value
Next i
End Sub
A Simple Slow Version
Sub LoopTrouble()
Dim i As Integer
Dim j As Integer
For i = 2 To 586
For j = 1 To 13
Sheet1.Cells(j + 1, 2) = Sheet2.Cells(i, 98 - j)
' Sheet1.Cells(j + 1, "B") = Sheet2.Cells(i, 98 - j)
' Sheet1.Range("B" & j + 1) = Sheet2.Cells(i, 98 - j)
Next
Sheet2.Cells(i, 100) = Sheet1.Cells(18, 38)
Next
End Sub
A Faster 'Semi' Array Version
Sub LoopTroubleFaster()
Dim i As Integer
Dim j As Integer
Dim vntLT As Variant
Dim vntPaste As Variant
vntLT = Sheet2.Range(Cells(2, 85), Cells(586, 97)).Value2
ReDim vntPaste(1 To 13, 1 To 1)
For i = 1 To 585
For j = 1 To 13
vntPaste(j, 1) = vntLT(i, j)
Next
Sheet1.Range("B2:B14") = vntPaste
Sheet2.Cells(i + 1, 100) = Sheet1.Cells(18, 38)
Next
End Sub
I'm trying to get an output in column U for multiple worksheets in a workbook. The return string will either be "Yes" or "No" depending on which column is not blank and if the difference between two dates are > 150. This is the code I have written, but nothing shows up in column U. Could anyone help me figure out why this isn't working?
Sub Compliance()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim i As Integer
Dim listLength
listLength = ws.Cells(Rows.Count, "M").End(xlUp).Row - 1
For i = 2 To listLength + 2
If IsEmpty(ws.Range("P" & i)) = True And IsEmpty(ws.Range("O" & i)) = True And IsEmpty(ws.Range("N" & i)) = True And DateDiff("d", ws.Range("M" & i), ws.Range("K" & i)) > 150 Then
ws.Range("U" & i) = "Yes"
ElseIf IsEmpty(ws.Range("P" & i)) = True And IsEmpty(ws.Range("O" & i)) = True And DateDiff("d", ws.Range("N" & i), ws.Range("M" & i)) < 150 Then
ws.Range("U" & i) = "Yes"
ElseIf IsEmpty(ws.Range("P" & i)) = True And DateDiff("d", ws.Range("O" & i), ws.Range("N" & i)) < 150 Then
ws.Range("U" & i) = "Yes"
ElseIf DateDiff("d", ws.Range("N" & i), ws.Range("M" & i)) < 150 Then
ws.Range("U" & i) = "Yes"
Else
ws.Range("U" & i) = "No"
End If
Next
Next ws
End Sub
When you start your loop with For Each ws In ThisWorkbook.Worksheets then Excel starts processing from Sheet1 and I'm sure your list in another sheet.
So, the reason nothing changes in your U column is, because you are
looking at wrong Worksheet.
As long as you would like to loop through all of your worksheets, start to follow your lists from Sheet1. If you would like to loop in different order, you should define this in your code.
And below I edited some part of your code and also added msgbox ws.name to show you in which sheet Excel is working now.
Why Use Integer Instead of Long?
Option Explicit
Sub Compliance()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim i As Long
Dim listLength
MsgBox ws.Name
listLength = ws.Cells(ws.Rows.Count, "M").End(xlUp).Row - 1
For i = 2 To listLength + 2
If IsEmpty(ws.Range("P" & i)) = True And IsEmpty(ws.Range("O" & i)) = True And IsEmpty(ws.Range("N" & i)) = True And DateDiff("d", ws.Range("M" & i), ws.Range("K" & i)) > 150 Then
ws.Range("U" & i) = "Yes"
ElseIf IsEmpty(ws.Range("P" & i)) = True And IsEmpty(ws.Range("O" & i)) = True And DateDiff("d", ws.Range("N" & i), ws.Range("M" & i)) < 150 Then
ws.Range("U" & i) = "Yes"
ElseIf IsEmpty(ws.Range("P" & i)) = True And DateDiff("d", ws.Range("O" & i), ws.Range("N" & i)) < 150 Then
ws.Range("U" & i) = "Yes"
ElseIf DateDiff("d", ws.Range("N" & i), ws.Range("M" & i)) < 150 Then
ws.Range("U" & i) = "Yes"
Else
ws.Range("U" & i) = "No"
End If
Next
Next ws
End Sub
I have been trying to run the same Do While loop function across multiple worksheets in a workbook and compile the data in another worksheet. The code works for the one worksheet that is specified but how do I get it to work across the others that are in the workbook at the same time?
Also worth mentioning that I only want it to run on some of the worksheets not all that are in the workbook (sheets are named as years - 2014, 2015 etc).
This is the code
Sub Total_Button1_Click()
Dim i As Integer
Dim strSheetFrom As String
Dim j As Integer
Dim strSheetTo As String
i = 3
j = 2
strSheetFrom = "2014"
strSheetTo = "Total"
Do While Trim(Sheets(strSheetTo).Range("B" & CStr(j)).Text) <> ""
j = j + 2
Loop
Do While Trim(Sheets(strSheetFrom).Range("B" & CStr(i)).Text) <> ""
If UCase(Trim(Sheets(strSheetFrom).Range("A" & CStr(i)).Text)) = "Y" Then
Sheets(strSheetTo).Range("B" & j & ":G" & j).Value = Sheets(strSheetFrom).Range("B" & i & ":G" & i).Value
Sheets(strSheetTo).Range("H" & j & ":I" & j).Value = Sheets(strSheetFrom).Range("I" & i & ":J" & i).Value
Sheets(strSheetTo).Range("J" & j & ":J" & j).Value = Sheets(strSheetFrom).Range("L" & i & ":L" & i).Value
Sheets(strSheetTo).Range("K" & j & ":K" & j).Value = Sheets(strSheetFrom).Range("Q" & i & ":Q" & i).Value
Sheets(strSheetTo).Range("L" & j & ":AH" & j).Value = Sheets(strSheetFrom).Range("s" & i & ":AO" & i).Value
j = j + 1
End If
i = i + 1
Loop
MsgBox "Total book created"
End Sub
Try making your strSheetFrom variable an array something like this:
strSheetFrom = new strSheetFrom[3]
strSheetFrom[2] = "2012"
strSheetFrom[1] = "2013"
strSheetFrom[0] = "2014"
Then put your code into another loop like so:
dim w as integer
for w = 0 To 3
Do While Trim(Sheets(strSheetTo).Range("B" & CStr(j)).Text) <> ""
j = j + 2
Loop
Do While Trim(Sheets(strSheetFrom[w]).Range("B" & CStr(i)).Text) <> ""
If UCase(Trim(Sheets(strSheetFrom[w]).Range("A" & CStr(i)).Text)) = "Y" Then
Sheets(strSheetTo).Range("B" & j & ":G" & j).Value = Sheets(strSheetFrom[w]).Range("B" & i & ":G" & i).Value
Sheets(strSheetTo).Range("H" & j & ":I" & j).Value = Sheets(strSheetFrom[w]).Range("I" & i & ":J" & i).Value
Sheets(strSheetTo).Range("J" & j & ":J" & j).Value = Sheets(strSheetFrom[w]).Range("L" & i & ":L" & i).Value
Sheets(strSheetTo).Range("K" & j & ":K" & j).Value = Sheets(strSheetFrom[w]).Range("Q" & i & ":Q" & i).Value
Sheets(strSheetTo).Range("L" & j & ":AH" & j).Value = Sheets(strSheetFrom[w]).Range("s" & i & ":AO" & i).Value
j = j + 1
End If
i = i + 1
Loop
w -= 1
next
I haven't tested it, but something like that. You get the idea.
Use a For Each and iterate over the Worksheet collections like this
'Variables
Dim useWorkSheet As Worksheet
Dim totalWorkSheet As Worksheet
Dim yearAsNumeric As Integer
Dim startingYear As Integer
'Settings
startingYear = 2014
'To reference the total worksheet so we can work with it
Set totalWorkSheet = ActiveWorkbook.Worksheets("Total")
'Iterate over each item in the collection
For Each useWorkSheet In ActiveWorkbook.Worksheets
'Force the name into a numeric value. If it starts with anything non numeric (A-Z|a-z|$,#,etc) then it will return 0
yearAsNumeric = Val(useWorkSheet.Name)
'Greater than or equal to the year we want to start with?
If yearAsNumeric >= startingYear Then
'Yes. Do your stuff here
useWorkSheet.Name
End If
Next