I am trying to create a rolling twelve month sum in excel using vb. The last column with data is dynamic so I created a variable to store that column number each time. I then tried to create a loop that would output the sum of the previous 12 cells and move to the next cell until it reached 12 calculation cells. But this is not working... Is there something obvious that I am doing wrong?? Thanks!
Sub OSR_ReportComplete()
Dim lCol As Long
Dim p As Integer
lCol = Cells(7, Columns.Count).End(xlToLeft).Column
For p = 0 To 12
Range(Cells(15, lCol - p)).Value = Application.Sum(Range(Cells(7, lCol -
p), Cells(7, lCol - p - 12)))
Next p
End Sub
Could be done in a neater way but this works
Sub OSR_ReportComplete()
Dim lCol As Long
Dim p As Integer
lCol = Cells(7, Columns.Count).End(xlToLeft).Column
For p = 0 To 12
Cells(15, lCol - p) = Application.Sum(Range(Cells(7, lCol - p), Cells(7, lCol - p - 12)))
Next p
End Sub
Also, If your lCol evaluates to a value less then 25 it will fall over though due to trying to reference a column less than 1. I'd recommend using Offset instead
However, a rolling total can be done very easily in excel formulas also. Say your first value is in Column A and your date is in row 1, in row 2 enter in Column A =SUM($A$1:A1) and drag along the rest of your range
Giving:
Formulas:
The following code will generate the formulas above for row 1 in one line instead of doing your loop. This is an example as I don't understand the layout of your sheet from your code
Sub OSR_ReportComplete2()
With Range(Cells(1, 1), Cells(1, Columns.Count).End(xlToLeft))
.Offset(1, 0).Formula = "=SUM(" & .Cells(1).Address & ":" & Split(.Cells.Address(RowAbsolute:=False, ColumnAbsolute:=False), ":")(0) & ")"
End With
End Sub
This is easier to write in R1C1 notation (however will be displayed in A1 in Excel Sheet)
Sub OSR_ReportComplete2()
With Range(Cells(1, 1), Cells(1, Columns.Count).End(xlToLeft))
.Offset(1, 0).FormulaR1C1 = "=SUM(" & .Cells(1).Address(ReferenceStyle:=False) & ":R[-1]C)"
End With
End Sub
First of all, I suggest you don't use VBA for this. Instead, use the inbuilt OFFSET function, like this.
Your code is quite complex, but maybe you should be using xlToRight instead of xlToLeft to find the last column.
If you want to use VBA, you can try using WorkSheetFunction.offset, putting it into a range variable and then summing that range.
Related
'The code should add the moving average to last row using array. The Prices to be use to average are in range "E6:E7555". The values will be written in "G7555". There is an existing moving average values in range "G6:G7554". Need help from Excel VBA expert to correct the codes which I think in step 1 and 2 below.
Options Explicit
Sub Add_MovingAverage_to_LastRows()
Dim maArray As Variant
Dim runSum, ma() As Double
Dim i, lRow, iPeriod, iCol As Long
iPeriod = 7
'set last row and reference range to calculate
With Worksheets("Sheet1")
lRow = .Cells(Rows.Count, 1).End(xlUp).Row 'Row "7555"
maArray = .Range(.Cells(lRow - (iPeriod -1), 5), .Cells(lRow, 5)).Value2 'Column "E"
End With
'set the lower and upper bound
ReDim ma1(lRow - (iPeriod - 1) To lRow, 1 To 1)
'step 1 calculate the SUM for last row, sum (row "7549" to row "7555")
runSum = 0
For i = lRow - (iPeriod-1) To lRow
runSum1 = runSum1 + maArray(i, 1)
Next
'step 2 calculate the AVERAGE for last row, average (row "7549" to row "7555")
ma(1, 1) = runSum / iPeriod
'write the values to worksheet
iCol = 7 'Column "G"
With Worksheets("Sheet1")
.Range(.Cells(lRow, iCol), .Cells(lRow, iCol)).Value2 = ma
End With
Erase maArray: Erase ma
End Sub
I think you may want to stick to a Formula on G column instead of VBA. If you add this formula on G6 and drag down: (Test it out on column H next to it)
=AVERAGE(INDIRECT("E"&IF(ROW()-6<6,6,ROW()-6)&":"&"E"&ROW()))
The IF Statement is to not break the formula on the first few rows of the file.
It will always grab the last 6+current row of values in Column E to calculate the Average.
Edit: Summary
="E"&IF(ROW()-6<6,6,ROW()-6)&":"&"E"&ROW() if you paste this into I6 and drag down you can see how it is just graving the 7 Cell Range you are looking for. When you enclose this into INDIRECT then you can use this inside other formulas as a "literal range" as in my answer above with AVERAGE.
Edit 2: VBA Code to automatically drag/fill down formula.
Sub UpdateFill()
Dim lRow As Long, lFormulaRow
With ThisWorkbook.Sheets("Sheet1")
lDateRow = .Cells(Rows.Count, 1).End(xlUp).Row
lFormulaRow = .Cells(Rows.Count, 7).End(xlUp).Row
If lDateRow > lFormulaRow Then
.Range("G" & lFormulaRow & ":G" & lDateRow).FillDown
End If
End With
End Sub
Its been a while since I have done any sort of coding, so I am very rusty.
I am trying to write some VBA code, so that when a button is clicked in the excel sheet it will check another sheet in the same workbook and copy of specific cell values. This is based on a criteria in one of the columns (column 15, I counted); I should probably add that the data is in a table. if that row meets the specified criteria from column 15, then specific columns are copied over to the worksheet with the button.
I do have some code, but I know there is a lot missing from it.
Would appreciate some input, am I on the right track? can anyone help on there I can get more info and tips on the coding I need to use. Not sure if there is an easy way to do this using tables?
Private Sub CommandButton1_Click()
''This will count how many rows are populated in the table''
a = Worksheets("Billable").Cells(Rows.Count, 1).End(xlUp).Row
''Loop will run from row 6 to the last row (Row 6 is the first row in table)''
For i = 6 To a
''If statement which will check the status column for Detailed Estimate Submitted > column 15''
If Worksheets("Billable").Cells(i, 15).Value = "Detailed Estimate Submitted" Then
Worksheets("Billable").Rows(i).Copy
Worksheets("PM_Forecast").Activate
b = Worksheets("PM_Forecast").Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("PM_Forecast").Cells(a + 1, 1).Select
ActiveSheet.Paste
End If
Next
Application.CutCopyMode = False
End Sub
An example of the table: -
I only need to copy over 3 of the columns if they meet a specific criteria in the status column
You did not answer my question regarding the format pasting...
So, please, test the next code which pastes on a classical way. But without selecting and declaring all used variables:
Private Sub CommandButton1_Click()
Dim shB As Worksheet, shPM As Worksheet, lastRowB As Long, lastRowPM As Long
Dim i As Long, lastCol As Long
Set shB = Worksheets("Billable")
Set shPM = Worksheets("PM_Forecast")
lastRowB = Worksheets("Billable").Cells(Rows.Count, 1).End(xlUp).row
'Loop will run from row 6 to the last row (Row 6 is the first row in table)''
For i = 6 To lastRowB
If shB.Cells(i, 15).Value = "Detailed Estimate Submitted" Then
lastCol = shB.Cells(i, Columns.Count).End(xlToLeft).Column
lastRowPM = shPM.Cells(Rows.Count, 1).End(xlUp).row
shB.Range(shB.Range("A" & i), shB.Cells(i, lastCol)).Copy _
shPM.Cells(lastRowPM + 1, 1)
End If
Next
Application.CutCopyMode = False
End Sub
For array using variant you must only declare a new variable Dim arr As Variant and replace this part:
shB.Range(shB.Range("A" & i), shB.Cells(i, lastCol)).Copy _
shPM.Cells(lastRowPM + 1, 1)
with this one:
arr = shB.Range(shB.Range("A" & i), shB.Cells(i, lastCol)).Value
shPM.Cells(lastRowPM + 1, 1).Resize(, UBound(arr, 2)).Value = arr
Then, delete the code line Application.CutCopyMode = False. It is not necessary, anymore, since the Clipboard memory is not used...
And use Option Explicit on top of your module. It will save you many times, when the code will become complex.
I cant comment so I will ask the question through this answer:
Your variable b is currently not being used, I think it was meant to be in this line: Worksheets("PM_Forecast").Cells(b + 1, 1).Select but you have written a instead of b.
Does this solve your current issue?
I am working on some data organization and need to move information into a form. I have a column of IP addresses and some columns have more than one, or even none. I preferably need to be able to go through the column and keep the first 13 characters and remove everything after it, have 581 lines I need it to be able to run through. I think if I could get a VBA that will run through the column and find the first "," and remove that and anything after it will work. I tried this, but it did nothing.
Sub cleanup()
lastrow = Range("G581").End(xlUp).Row
For i = 2 To lastrow
If Len(Cells(i, 1)) > 13 Then
Cells(i, 1) = Left(Cells(i, 1), 14)
End If
Next i
End Sub
As mentioned before, you are counting the rows in column G, hopefully column G and Column A have the same number of rows.
I'm thinking column G may be the issue.
lastrow = Range("A581").End(xlUp).Row
For i = 2 To lastrow
If Len(Cells(i, 1)) > 13 Then
Cells(i, 1) = Left(Cells(i, 1), 14)
End If
Next i
Honestly there could be a few reasons that you're having trouble. The big one is that when you use the Left() function, you tell it to keep 14 characters instead of the 13 you said that you want!
Another option could be that excel thinks you are trying to do this operation on another sheet. To combat this one, I made a worksheet object and qualified all of the ranges mentioned just to be sure that excel knows what location you're talking about.
An (unlikely based on your description, but possible) problem could be with the way that you specify the lastrow. If you have a column of contiguous values (although your description would suggest you do not) and you specify the last row of that column with a value, the .End(xlUp) will actually trace back up to the last cell with no value which could be the first row - so the loop never even runs! You could avoid this just by changing it to lastrow = Range("G582").End(xlUp).Row (one row below what you know to be the last full row), but why bother when you can just let excel do the work for you like in my answer below.
Sub cleanup()
Dim ws As Worksheet
Set ws = sheets("Sheet1")
Dim lastRow As Long
lastRow = ws.Range("G" & rows.count).End(xlUp).row
Dim i As Long
For i = 2 To lastRow
If Len(ws.Cells(i, 1)) > 13 Then
ws.Cells(i, 1) = Left(ws.Cells(i, 1), 13)
End If
Next i
End Sub
I also made a version with the second option that you mentioned in the question about splitting each row by a comma ",".
Sub cleanup2()
Dim ws As Worksheet
Set ws = sheets("Sheet1")
Dim lastRow As Long
lastRow = ws.Range("G" & rows.count).End(xlUp).row
Dim i As Long
For i = 2 To lastRow
ws.Cells(i, 1) = Split(ws.Cells(i, 1), ",")(0) 'option 2
Next i
End Sub
Hope this solves your problem!
The Before image represents educational background that an employee has reported. As you can see, the report that produces the info creates unwanted duplicates and spaces. Can't do anything about that. The After image shows my desired end result. To get there, I manually:
Deleted the duplicates
Copy/pasted the remaining occupied cells to the top of the range without shifting any cells below the range. (Yes, there is data below the range).
How do I automate this with VBA? The Before image represents the range F3:H18 and I've named the range edVal.
This range will always be 3 columns (F-H). The number of rows will vary but I already have code that selects edVal, no matter how many rows it may be.
I have other code doing other stuff elsewhere in the sheet but then I come to this education portion. Assume I've dim'd edVal as Range and edVal is selected. Seems the additional code I need will fit between "With edVal" and End With.
Thanks!
How about something like below, it will remove duplicates and loop through F3 to F18 and if it finds a blank row, it will delete and shift up, but then it will insert a blank row at the bottom, thus leaving your data below the range intact.
Simply adjust the code below with your value from edVal:
Sub foo()
Dim ws As Worksheet: Set ws = Worksheets("Sheet1")
ws.Range("$F$3:$H$18").RemoveDuplicates Columns:=Array(1, 2, 3), Header:=xlNo
For i = 3 To 18
If ws.Cells(i, "F").Value = "" Then
ws.Range("F" & i & ":H" & i).Delete Shift:=xlUp
ws.Range("F18:H18").Insert Shift:=xlDown
End If
Next i
End Sub
Adjusting the values for edVal worked as suggested. That code is between the "With edVal / End With" lines. Also shown, as requested by the moderator, is the code I already had.
Sub foo()
Dim empRange as Range, edVal as Range
Dim f as Integer, l as Integer
Set empRange = Range("A3:Z18") 'rows 1 and 2 are headers
With empRange
f = empRange.Row
l = empRange.Rows.Count
Set edVal = Range(.Cells(, 6), .Cells(l, 8)) 'edVal is columns F-H of the larger empRange
With edVal
.RemoveDuplicates Columns:=Array(1, 2, 3), HEADER:=xlNo
For i = f To l + 2 'adding 2 to account for two header rows
If Range("F" & i).Value = "" Then
Range("F" & i & ":" & "H" & i).Delete Shift:=xlUp
Range("F" & l + 2 & ":" & "H" & l + 2).Insert Shift:=xlDown
End If
Next I
End With
End With
End Sub
I have a report generated from a program that opens in excel similar to the image below
I have been using this code to sum the amounts:
Sub SumTotals()
lastrow = Sheets("sheet1").Cells(Rows.Count, 2).End(xlUp).Row
Range("b" & lastrow + 1) = WorksheetFunction.Sum(Sheets("sheet1").Range("b2:b" & lastrow))
Range("a" & lastrow + 1) = Cells(1, 1)
Range("a1:b" & lastrow).Select
Selection.Delete Shift:=xlUp
End Sub
The issue I am running into is that the code sums both company A and company B and the ends up
Company A 5,625.07
What I am trying to accomplish is
Company A 2,053.73
Company B 3,571.34
When I get these reports there is generally 100 + companies so if I can speed up the process it would be very beneficially.
I suspect the first line of code is the problem
lastrow = Sheets("sheet1").Cells(Rows.Count, 2).End(xlUp).Row
As this is placing the end at cell B8 instead of B3. I am trying to figure out how to sum the first column of numbers until the first blank row, and then carry on to the numbers below and carry out the same process. Any suggestions are appreciated.
ideal end result
Ideal result
Try this
Sub x()
Dim r As Range
For Each r In Columns(2).SpecialCells(xlCellTypeConstants).Areas
r(r.Count + 1).Value = Application.Sum(r)
r(r.Count + 1).Offset(, -1).Value = r(1).Offset(, -1).Value
r.EntireRow.Delete shift:=xlUp
Next r
End Sub
You can also try this non VBA approach. Enter this formula =IF(A1<>"",SUM(OFFSET(B1,0,0,MIN(IF(B1:B80="",ROW(B1:B80))))),"") in cell C1 to sum all values before blank row. drag formula to the bottom of your values. Since it is an array formula, you must enter it using CTRL+SHIFT+ENTER. You can then filter out blank values in column A to get your result.