SUM Function in VBA - not hard coding rows and columns - excel

How do I use the lastRow variable in the sum function in vba? After I count the number of words in each row until the last row, I would like to add up the individual totals from each row into an overall total column but I do not want to hard code the range into the sum function in vba? Is there another way?
Example:
Column: A B C D E F
NRow 1: 2 3 4 5 6 7
NRow 2: 3 4 5 6 7 8
Total 3: 5 7 9 11 13 15
Want to avoid SUM(R[-2]C:R[-1]C)? I would like SUM(R[lastRow]C:R[-1]C) or something that would function in the same way.
Thank you!

Something like this which
Sets the usedrange on the active sheet from A1 to the last used cell in column F
Goes to the first cell below this range Cells(rng1.Cells(1).Row + rng1.Rows.Count, 1), resizes to the amount of columns in the range of interest (6 in this case)
Enters the forumula "=SUM(R[-" & rng1.Rows.Count & "]C:R[-1]C)" where rng1.Rows.Count gives the first row dynamically
code
Sub GetRange()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "F").End(xlUp))
Cells(rng1.Cells(1).Row + rng1.Rows.Count, 1).Resize(1, rng1.Columns.Count).Value = "=SUM(R[-" & rng1.Rows.Count & "]C:R[-1]C)"
End Sub

If you can guarantee that there is always data in column A then to find the last row you could do the following:
Option Explicit
Sub addSums()
Dim lstRow As Integer
With Excel.ThisWorkbook.Sheets("Sheet1")
lstRow = .Cells(.Rows.Count, 1).End(Excel.xlUp).Row
Dim j As Integer
For j = 1 To .Cells(1, .Columns.Count).End(Excel.xlToLeft).Column
.Cells(lstRow + 1, j) = "=SUM(R[-" & lstRow - 1 & "]C:R[-1]C)"
.Cells(lstRow + 1, j).Font.Bold = True
Next j
End With
End Sub
Seems to work ok on the following:

If your source data is coming frm an external source, the appropriate mechanism is:
Create a Named Data Range around the input table;
Set the inut table to automatically resize on refresh; and
Then use the Named Data Range in your functions.

Related

Add moving average value in last row using array in Excel VBA

'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

I am looking to find the average of the last 6 corresponding weekdays in a dataset

I am looking to create a cash forecast using the average of the last 6 days that correspond to a particular weekday.
My dataset is formatted as such:
Row 1: Headers
Column A: Date
Column F: Cash position
Column G: Day of week
After column G is filtered to a specific day (i.e. Monday), I'd like to extract the last 6 visible values in columns A & F. Using column A as an example, here is my current failed code:
Range("A1").Select
Selection.End(xlDown).Select
Range("A460:A485").Select
Range("A485").Activate
The range on line 3 was coded from shift-clicking upwards 5x from the bottom of the data set. I'm looking to copy the data within this range, while not defining what the range is.
Thanks for any help provided.
Scan up the sheet for 6 dates that are mondays and fill an array. Then dump the array to the other sheet.
Option Explicit
Sub Last6Mon()
Dim ar(1 To 6, 1 To 2) As Variant
Dim lastrow As Long, i As Long, r As Long
i = 6
With Sheets("Sheet1")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For r = lastrow To 2 Step -1
If WorksheetFunction.Weekday(.Cells(r, "A")) = vbMonday Then
ar(i, 1) = .Cells(r, "A") ' date
ar(i, 2) = .Cells(r, "F") ' cash posn
i = i - 1
If i = 0 Then Exit For
End If
Next
'another worksheet
Sheets("Sheet2").Range("A2:B7") = ar
End With
End Sub

VBA to Transpose Data

I have a dataset that I’m trying to reorient but Excel’s transpose function won’t work for what I’m trying to do. I do want to transpose the original data from rows to columns but instead I’m hoping to stack the data from each row of the original data into a single column in a separate sheet. In other words, if I have an array made up of 3 rows and 3 columns, I want to copy and paste the data so that the 9 cells of the original array become a 1 by 9 array in a separate sheet.
My VBA skills are elementary at best and I’ve attempted to write a macro with a For Next loop but just cannot seem to get it right. Any suggestions?
This code will convert a range of cells into a unidimensional array:
Dim rngSource As Range
Dim rng As Range
Dim MiMatriz() As Integer
Dim MiPos As Integer
Dim i As Integer
Set rngSource = Range("A1").CurrentRegion
ReDim MiMatriz(1 To 1, 1 To rngSource.Cells.Count)
For Each rng In rngSource.Cells
MiPos = (((rng.Row - rngSource.Cells(1, 1).Row + 1) - 1) * rngSource.Columns.Count) + (rng.Column - rngSource.Cells(1, 1).Column + 1)
MiMatriz(1, MiPos) = rng.Value
Next rng
For i = 1 To rngSource.Cells.Count
Debug.Print "Index:(1," & i & ")", MiMatriz(1, i)
Next i
Erase MiMatriz
Set rngSource = Nothing
The output I get when executing this code is:
Index:(1,1) 1
Index:(1,2) 2
Index:(1,3) 3
Index:(1,4) 4
Index:(1,5) 5
Index:(1,6) 6
Index:(1,7) 7
Index:(1,8) 8
Index:(1,9) 9

How to generate an alphanumeric tree changes with criteria?

I'm working on excel sheet template used for SAP System and I have 2 columns looks like below:
Column C Column E
Level Element Code
3 ABCD.01.01.01
4 ABCD.01.01.01.01
4 ABCD.01.01.01.02
4 ABCD.01.01.01.03
3 ABCD.01.01.02
4 ABCD.01.01.02.01 'I Want to Restart Numbering Here
4 ABCD.01.01.02.02
4 ABCD.01.01.02.03
I succeeded in level 3 to be automated in the whole sheet by Macro as below
Sub AutoNumber3()
Dim Rng, C As Range
Dim Lrow As Long
Dim i As Integer
Lrow = Cells(Rows.Count, 1).End(xlUp).Row
Set Rng = Worksheets("Union").Range("C2:C" & Lrow)
i = 1
For Each C In Rng.Cells
If C.Value = 3 Then
For i = 1 To i Step 1
C.Offset(0, 2).Value = "ABCD.01.01." & i
Next i
End If
Next C
End Sub
and I used the same for level 4 as below
Sub AutoNumber4()
Dim Rng, C As Range
Dim Lrow As Long
Dim i As Integer
Lrow = Cells(Rows.Count, 1).End(xlUp).Row
Set Rng = Worksheets("Union").Range("C2:C" & Lrow)
i = 1
For Each C In Rng.Cells
If C.Value = 4 Then
For i = 1 To i Step 1
C.Offset(0, 2).Value = "ABCD.01.01.01" & i
Next i
End If
Next C
End sub
I want to Restart the numbering of level 4 from 1 each time the cells values in the level column = 3 by using Do Until the next C.Value = 3, I = 1 But I can not put it correctly in the Autonumber4 procedure
Your help is highly appreciated since this sheet may reach to 50000 or 100000 rows which is impossible to fill them manually
Thanks, Regards
Moheb Labib
Try this
Sub AutoNumber()
Dim rngLevels As Range, cl As Range
Dim lLastRow As Long, i As Long
Dim sElemCode As String
Dim vLevelsCounter() As Long
With ThisWorkbook.Sheets("Union")
lLastRow = Evaluate("=COUNTA(" & .Name & "!C:C)")
lLastRow = WorksheetFunction.Max(lLastRow, .Cells(Rows.count, "C").End(xlUp).Row)
Set rngLevels = .Range("C2:C" & lLastRow)
End With
For Each cl In rngLevels.Cells
' Uncomment "If" to use it on filtered data only
'If Not cl.EntireRow.Hidden Then
UpdateLevelsCounters vLevelsCounter, cl.Value
sElemCode = "ABCD"
For i = 1 To cl.Value
sElemCode = sElemCode & "." & Format(vLevelsCounter(i), "00")
Next i
cl.Offset(0, 2).Value = sElemCode
'End If
Next cl
End Sub
Function UpdateLevelsCounters(ByRef arr() As Long, lLevel As Long)
If lLevel < 1 Then Exit Function
Dim i As Long
ReDim Preserve arr(1 To lLevel)
For i = LBound(arr) To lLevel - 1
If arr(i) = 0 Then arr(i) = 1
Next i
arr(lLevel) = arr(lLevel) + 1
End Function
This should work for levels other than 3 and 4 as well (I hope)
You've not specified if your count will be always of two digits or not, and if it can be something like 01.20.99.99, but This formula can lead you in the good way (not tested with 100000 records)
=IF(C2=3;"ABCD.01.01."&TEXT(COUNTIF($C$2:C2;C2);"00");INDIRECT("E"&SUMPRODUCT(MAX(--($C$2:C2=3)*ROW($C$2:C2))))&"."&TEXT(SUMPRODUCT(--($C$2:C2=4)*--(ROW($C$2:C2)>SUMPRODUCT(MAX(--($C$2:C2=3)*ROW($C$2:C2)))));"00"))
This is how it works:
A) First, we check if cell in colum C is a 3 or 4. In case is 3, we do ;"ABCD.01.01."&TEXT(COUNTIF($C$2:C2;C2);"00"); This will count how many times does the number 3 appear in range $C$2:C2 and will concatenate to string ABCD.01.01. The trick here is using $C$2:C2, because it makes a range dynamic (but it can overload calculus times)
B) If not 3, then we do a really complext part I'm going to try to explain. Also, we use the trick of dynamic range
SUMPRODUCT(MAX(--($C$2:C2=3)*ROW($C$2:C2)))) this part is used twice. It will get the last row number of the last 3 value in column C.
Example:ROW($C$2:C6) will get an array of just row numbers, like {2;3;4;5;6}. --($C$2:C6=3) will return an array of zero/one depending if cell equals/not equals to 3, something like {1;0;0;0;1}. ($C$2:C6=3)*ROW($C$2:C6)) will multiply both arrays, so we get {1;0;0;0;1}*{2;3;4;5;6}={2;0;0;0;6}. And with MAX we get max value from that array, That 6 means the last position of a 3 value.
We use INDIRECT combined with the number of step 1 to get the text inside the cell
SUMPRODUCT(--($C$2:C2=4)*--(ROW($C$2:C2)>SUMPRODUCT(MAX(--($C$2:C2=3)*ROW($C$2:C2)))));" Everything after the > is the same logic than step 1. It will return the row number of last cell containing a 3. Part SUMPRODUCT(--($C$2:C2=4)*--(ROW($C$2:C2) will just get row numbers of those cells containing a 4 value, and which row numbers are higher than value obtained in step 1. That way you make sure how to count the cells containing 4 values, between two cells containing 3 values.
We concatenate everything to form the final string.
TEXT functions are just used to force the calculation to be 2 digits.
You can use this manually, or you can insert the formula using VBA, drag down, and then converting everything into values (I would probably would do that). Something like this could work.
Sub Macro1()
Dim LR As Long
LR = Range("C" & Rows.Count).End(xlUp).Row 'last non blank row in column c
Range("E2").FormulaR1C1 = _
"=IF(RC[-2]=3,""ABCD.01.01.""&TEXT(COUNTIF(R2C3:RC[-2],RC[-2]),""00""),INDIRECT(""E""&SUMPRODUCT(MAX(--(R2C3:RC[-2]=3)*ROW(R2C3:RC[-2]))))&"".""&TEXT(SUMPRODUCT(--(R2C3:RC[-2]=4)*--(ROW(R2C3:RC[-2])>SUMPRODUCT(MAX(--(R2C3:RC[-2]=3)*ROW(R2C3:RC[-2]))))),""00""))"
Range("E2").AutoFill Destination:=Range("E2:E" & LR), Type:=xlFillDefault
Range("E2:E" & LR) = Range("E2:E" & LR).Value 'paste into values
End Sub
NOTE: Probably you will need to adapt this depending on the results (we do not know if the count of 3 or 4 values can have 3 or 4 digits, and so on).

Move cells with pattern search and copy

I would like to achieve following in VBA
Input Workbook1
Index Value
1 a
2 a
3 b
4 c
5 a
6 b
7 a
8 c
Output Workbook2
I would like to have output in following format so that I can generate graph with same X axis
Index Value 1 Value 2 Value 3
1 a
2 a
3 b
4 c
5 a
6 b
7 a
8 c
I am using two functions, the first one to move two columns from workbook 1 to workbook2
Sub MOVE()
Sheets("Workbook1").Columns("A").Copy Sheets("Sheet1").Range("A1")
Sheets("Workbook1").Columns("B").Copy Sheets("Sheet1").Range("B1")`
end sub
2nd function is:
Sub move_a()
Worksheets("Sheet1").Activate
Dim myR As Range
Set myR = Range("B:B").Find("PATTERN_A")
Do While Not myR Is Nothing
myR.Insert xlToRight
Set myR = Range("B:B").FindNext
Loop
end sub
but 2nd one is not working
I've put your two operations into a single sub procedure. First the copy is performed on the entire block and then cells are inserted according to the value in column B to shift the values to the right.
Sub move_it_all()
Dim rw As Long, a As Long
With Worksheets("Sheet1")
Worksheets("Workbook1").Cells(1, 1).CurrentRegion.Copy _
Destination:=.Cells(1, 1)
For rw = 2 To .Cells(Rows.Count, "B").End(xlUp).Row
a = Asc(UCase(Right(.Cells(rw, 2).Value2, 1)))
If a > 65 And a <= 90 Then
.Cells(rw, 2).Resize(1, a - 65).Insert shift:=xlToRight
End If
.Cells(1, a - 63) = "Value" & a - 64
Next rw
End With
End Sub
I will admit to some confusion over the worksheet named Workbook1. You might have to adjust the source and target of the Copy & Paste.
        

Resources