I'm trying to run a For Next loop that pulls specific numbers from specific cells in order to calculate total hours for a time card using Excel (it's the best program for what I'm using it for). I cannot, however figure out how to do it because I'm using R1C1 because I'm running the a count for the locations. I am not hidebound to this particular method of doing things, but a variation on it worked in Access (but I can't use Access for this because, reasons).
Attached is the code snippet I'm using.
For Weekday = 11 To 17 'Determine the day of the week by Row Reference (R1C1 format)
For DayValue = 29 To 34 'Determin time input (Call, Meal In, Meal Out, Wrap, etc.) for day of the week
WrkTimes(DayValue) = ActiveSheet.Cells.FormulaR1C1 = "R" & (Weekday) & "C" & (DayValue) 'Select the correct time input
Next DayValue 'Process the next time input
TotHrs(Weekday) = WrkTimes(34) - WrkTimes(29) - (WrkTimes(31) - WrkTimes(30)) - (WrkTimes(33) - WrkTimes(32)) 'Calculate total hours for the day
ActiveSheet.Cells(Weekday, 37) = TotHrs(Weekday) 'Display total hours for the day in the proper cell
Yes there is a Next command at the bottom of this loop, I just have other non-relevant code between this and the end. I'm getting the error on the DayValue lines
My first suggestion is to use
Cells(r, c)
Then you can put in your offsets such as
Cells(r-3, c)
You would just need to store the r and c values, which you've done as Weekday (r) and DayValue (c).
In the case if what you're looking at with the loop:
Cells(Weekday, DayValue).Value
This would give the value in the cell you've referenced.
Edit:
Suggestion from my comment:
Dim i as Long, j as Long, k as Long
For Weekday = 11 To 17
i = Cells(Weekday, 34).Value 'Should allow you to have an output for each row
For DayValue = 33 To 29 Step -1 'Saving column 34 outside of the dayvalue loop
j = i
k = Cells(Weekday, DayValue).Value
i = j - k
Next DayValue
Cells(Weekday, 37).Value = i
Next Weekday
So, to clarify why I kept getting the subscript out of range (after following the suggestions from Scott and Phylogenesis), it turns out that my Weekday(x) was set for 7 when it should have been set at 17.
Thanks for all the help.
Related
I am fairly new at VBA and I am currently trying to rework an existing macro that sums hours of a workday up by employee for the week.
I need a macro that will sum up the work hours by just a single day instead of a weekly total. There are two entries per day for each employee. Then, this total is copy and pasted into a different column.
I can not use a pivot table as this macro will be used on a different spreadsheet every week. I also can not have a reference sheet. This is going to be applied to a spreadsheet that is emailed every week, so it is constantly changing.
Basically... if the date in Column B is the same, I need the sum of hours in Column C, then that Sum is pasted over to a new column (D is fine).
Below is what the original report looks like at this point:
A B C
Joe Smith -- 03/26/2018 -- 3.65
Joe Smith -- 03/26/2018 -- 4.46
Joe Smith -- 03/27/2018 -- 5.45
Joe Smith -- 03/27/2018 -- 2.93
The existing macro is :
For Each x In n.Range(n.Range("B2"), n.Range("B" & Rows.Count).End(xlUp))
x.Value = Month(x.Value) & "/" & Day(x.Value) & "/" & Year(x.Value)
Next x
For Each x In n.Range(n.Range("J2"), n.Range("J" & Rows.Count).End(xlUp))
Set r = n.Range(x.Address)
r.Offset(0, 1).Value =
Format(Application.WorksheetFunction.Max(n.Range(n.Range("B2"), n.Range ("B" & Rows.Count).End(xlUp))), "MM/DD/YYYY")
r.Offset(0, 2).Value = Application.WorksheetFunction.SumIf(n.Range("A:A"), x.Value, n.Range("E:E"))
For I = 3 To UBound(TableHeaders)
ch = TableHeaders(I)
r.Offset(0, I).Value = Application.WorksheetFunction.SumIfs(a.Range("R:R"),
a.Range("L:L"), ch, a.Range("A:A"), x.Value)
Next I
d.RemoveAll
Next x
I can not use a pivot table as this macro will be used on a different
spreadsheet every week.
well, this is not a reason. You could run change source for pivot table any time.
This is going to be applied to a spreadsheet that is emailed every week
But at least layout of the workbooks is preserved?
The simplest way is to use formula:
=SUMIFS(C:C, A:A, A2, B:B, B2)
Paste it to D2 and drag down. You could also put formulas to A:C that just refers to proper values in source file, like:
=[WorkbookFromEmail.xlsx]Sheet1!A2
and drag it left to C and down to as many rows as you think you will need and some more. Then you could only change the name of linked file in Data/Edit Links.
As far, you don't need VBA. But you could make some macro for refreshing links to other workbook if you found manual job too troubling. This is however different story.
Alternatively, you could save the source file always under the same name, like BookFromMail.xlsx and then open the master file with formulas and refresh it.
Here's an all code way. You'll have to adjust the code to find the range you want to read and also figure out where to write to.
Sub SumEeDays()
Dim vaValues As Variant
Dim i As Long
Dim dc As Scripting.Dictionary
Dim sKey As String
'set a reference to the MS Scripting Runtime
'then you wont get an error on this line
Set dc = New Scripting.Dictionary
'Make a 2d array of the values you want process
vaValues = Sheet1.Range("a1").CurrentRegion.Value
'loop through the 2d array
For i = LBound(vaValues, 1) To UBound(vaValues, 1)
'create a unique key to keep track of ee name and date
sKey = vaValues(i, 1) & "||" & vaValues(i, 2)
If dc.Exists(sKey) Then
'If the key already exists, add the hours to what's there
dc.Item(sKey) = dc.Item(sKey) + vaValues(i, 3)
Else
'If the key doesn't exist, create it and add the hours
dc.Add sKey, vaValues(i, 3)
End If
Next i
'Loop through the dictionary of unique name/dates
For i = 1 To dc.Count
With Sheet1.Range("J1")
'Keys returns an array and Split splits it on "||"
'The 0th element of the array is the name
'The 1st element is the date
.Offset(i - 1, 0).Value = Split(dc.Keys(i - 1), "||")(0)
.Offset(i - 1, 1).Value = Split(dc.Keys(i - 1), "||")(1)
.Offset(i - 1, 2).Value = dc.Items(i - 1)
End With
Next i
End Sub
Aim:
So I'm brand new to VBA and was working on a quick script which tries to record the hours of work done in a week.
It does this by copying the numbers of hours of work done per day for each day contained in row 27 from column B (Monday) to column G (Saturday) in the This Week sheet and pastes that infomation in the Hours sheet starting at column B and working along to column G.
I want to do this so that I can keep a permanent record of the hours done even after I've changed my This week sheet schedule for the next week.
So I've tried to write this script so that if it sees the row above full of infomation it tries the row below.
This would result in a permanent, chronological (every week starts a new row) record of how many hours done per day every week.
I'd run this at the end of the week before clearing the schedule for the next week.
Code:
Sub Hours()
Dim i As Integer
Dim j As Integer
Dim hoursdone As Long
j = 0
For i = 2 To 7
hoursdone = Worksheets("This Week").Cells(27, i).Value
Do While Worksheets("Hours").Cells(j, i).Value <> Empty
j = j + 1
Loop
Worksheets("Hours").Cells(i, j) = hoursdone
Next i
End Sub
The Error I keep getting:
Application-defined or object-defined error
When I click Debug it takes me to Line 9 and I've no idea what's gone wrong?
Thanks
PS: I want to eventually tie this thing to a button within my This week sheet to press, recording the data, allowing me to clear This week
That would be because j is starting out at the number 0. You can not have Cells(0, 2), rows start at the number 1.
Change the j = 0 to j = 1, and also move that inside your For...Next statement.
For i = 2 To 7
j = 1
hoursdone = Worksheets("This Week").Cells(27, i).Value
Do While Worksheets("Hours").Cells(j, i).Value <> Empty
j = j + 1
Loop
Worksheets("Hours").Cells(i, j) = hoursdone
Next i
End Sub
If you do not move the j = 1 inside your For statement, then the next iteration j will be what is was where it left off from when it was inside your Do...loop.
I have two columns with random times and the times come from two different sources so the columns do not have the same amount of data points. I want to start with the first time in the first column and compare it to each time in the second column. If there is a match in times, I would like to pull relevant data. After a match is found (if there is one) I would like for the code to go to the second cell in the first column and compare it to every value in the second column and so on.
Here is the code I have so far:
Sub TransferInfo()
'Activate the Sub to Convert and Format Dates
Call ConvertDates
'Define Variables
Dim st As Worksheet
Dim ts As Worksheet
Dim lastrow As Long
Dim i As Integer
j = 2
'Find and set the last used row
Set st = ThisWorkbook.Worksheets("Data Table")
lastrow = st.Cells(st.Rows.Count, "B").End(xlUp).Row
Set ts = ThisWorkbook.Worksheets("ShopFloor")
'Cycle through/compare Row J, Column 18 based on each cell in Row I, Column 14
For i = 2 To lastrow
Do Until IsEmpty(ts.Cells(j, 8)) Or IsEmpty(st.Cells(j, 2))
If st.Cells(i, 14).Value = ts.Cells(j, 18).Value Then
st.Cells(i, 15).Value = ts.Cells(j, 2).Value
Exit Do
Else
st.Cells(i, 15).Value = ""
End If
j = j + 1
Loop
j = 2
Next i
End Sub
The other sub that I call at the beginning of this sub simply rounds the times in each column to the nearest 15 minute interval to increase the likelihood of matches between the columns.
My question is: The code does not copy and paste any more information although there are times that match between the two columns. Why would the code that I have not work? Also, with larger data sets I am afraid that this the code may crash Excel and because I have a loop within a loop trying to process a lot of data a lot of times, but I don't know of a more efficient way to accomplish what I am trying to without this code.
If anyone has any insights as to why this code doesn't work I would greatly appreciate any help.
Thanks!
Based on your code, it looks like you just need an INDEX/MATCH formula. Use this in O2 and copy down:
=IFERROR(INDEX(B:B,MATCH(N2,R:R,0)),"")
No need for VBA
I have an excel sheet with lots of data. As you may know, this comes with lots of problems. One major one is having too much data. I am not familiar with vba, but I wanted to know how to clean data.
I have a sheet with 3 fields: date, time, and temp. The temperature is recorded on a minute by minute basis. The temperature is only recorded from 7 am to 10 pm, but the sheet is on a 24 hour basis. So my sheet has a lot of blank cells. So, I want to write a code that states:
if ((time < 7am) or (time > 10pm)):
delete row
Can I do this?
Also, another problem is that the data is not collected on weekends. I am not given a day field, only a date field in this format: 20130102 which is January 02 2013. I want to:
if ((date = saturday) or (date = sunday)):
delete row
Are either of these doable?
My sheets looks like the following:
A .............. B ......... .... C
date........ time ......... temp
Since both your dates and times are formatted differently than normal, we need to manipulate the values to get something to test against. Consider the following example (I've commented each line to help you follow along):
Sub DeleteRows()
Dim lastRow As Long
Dim Cell As Long
Dim dt As Date
'Work with the active sheet.
With ActiveSheet
'Find the last row of your dataset.
lastRow = .Range("A:A").Find("*", searchdirection:=xlPrevious).Row
'Format your time column to a readable time.
.Columns("B").NumberFormat = "[$-F400]h:mm:ss AM/PM"
'Loop through the rows, beginning at the bottom.
For Cell = lastRow To 2 Step -1
'Piece together the date.
dt = Mid(.Cells(Cell, 1), 7, 2) & "/" & _
Mid(.Cells(Cell, 1), 5, 2) & "/" & Left(.Cells(Cell, 1), 4)
'If the date is a Sat or Sun, delete the row.
If Weekday(dt) = 1 Or Weekday(dt) = 7 Then
.Rows(Cell).EntireRow.Delete
'If the time is before 7am or after 10pm, delete the row.
ElseIf Hour(.Cells(Cell, 1)) < 7 Or Hour(.Cells(Cell, 1)) > 22 Then
.Rows(Cell).EntireRow.Delete
End If
Next Cell
End With
MsgBox "Done!"
End Sub
A few things to note about the code. First, we must start at the bottom of the list because as we delete rows, the remaining rows shift upwards. If we were to go from top to bottom (e.g. A1 to A10), if we deleted row 5, row 6 would slide into its place, and the loop would skip row 5 (previously row 6) and go on to row 6. In other words, looping from top to bottom when deleting rows will ultimately skip rows unintentionally.
Second, I had to guess on your time format. While I believe I guessed correctly, I may not have. If I was wrong and my code doesn't change the time column into a readable time, record a macro while changing the format of that column and substitute the new format with mine ("[$-F400]h:mm:ss AM/PM"
).
And lastly, since your date column is an abnormal format (for Excel), we need to reorder the date so that Excel can read it. Once we've done that, we can use the resulting date to see if the date was a Sat. or Sun.
You can do it this way, assuming the column that contains your date is the 2nd (B) :
Dim i As Integer
for i = 1 to cellsCount
If Hour(Cells(i, 2)) < 7 Or Hour(Cells(i, 2) > 22 Then
Rows(i).Delete
Else If WeekDay(Cells(i, 2)) = 7 Or WeekDay(Cells(i, 2)) = 1 Then
Rows(i).Delete
End If
next
You can have more information about the WeekDay function here :
http://msdn.microsoft.com/en-us/library/82yfs2zh%28v=vs.90%29.aspx
I have been doing some basic VBA programming in Excel 2010 but I have been struggling with this challenge for some time. Basically, I have a sheet that is formatted like this (It actually has 62 columns and rows=# of days in the given month):
Column A will be hidden but is used in a few formulas.
Row 15 shows whether or not the station is open 24/7(all) or only Monday-Friday(M-F).
the values presented are arbitrary counts. However, a blank count represents a problem unless... the station is M-F and
I need to get my code to identify a station that is open M-F and then fill in any particular Sat. or Sun (for that station) with the word "closed." then search for the next station that is M-F and repeat the process.
Initially I was having my code start with an actual value and then use several activecell.offset functions to find empty cells and then check conditions but I couldn't get it to work out. Then I tried to check from the station name or the schedule row but I couldn't get the multiple if/nested offset statements to work either.
I would really appreciate any help or insight you could provide that would show me the best approach. I don't really need the code that does it I just need a pseudo code walk-through unless you are kind enough to write out the code.
Thanks for your help!
I had a similar problem I worked out before. I modified it to your spreadsheet:
Dim d As Long, s As Long
d = 1 'weekdays column
s = 40 'status row
Dim r As Long, c As Long
r = ActiveSheet.Cells(Rows.Count, d).End(xlUp).Row
c = ActiveSheet.Cells(s, Columns.Count).End(xlToLeft).Column
Dim i As Long, cell As Range
i = 0
Dim days() As Long
For Each cell In Range(Cells(1, d), Cells(r, d))
If cell.Value = "Sat" Or cell.Value = "Sun" Then
ReDim Preserve days(i)
days(i) = cell.Row
i = i + 1
End If
Next cell
For Each cell In Range(Cells(s, 1), Cells(s, c))
If cell.Value = "M-F" Then
For i = LBound(days) To UBound(days)
Cells(days(i), cell.Column).Value = "closed"
Next i
End If
Next cell