Summing cells with a variable range - excel

I have a worksheet used for my office. Occasionally, a few extra lines are needed in one section, so I created a button that adds said line (for the non-excel users). At the bottom of the section is a simple <=SUM(D82:D92)>.
In this way, whenever data is added, the amount is summed.
However, when a new line is added, the sum formula stays the same. I'm trying to change the D92 to D93 (and etc.).
How can I add a line AND update the sum formula so that it stays dynamic.
Please give advice on the below code:
'botton cell in sum range
Dim BC As String
Dim rng As Range
BC = ActiveCell.Address
Set rng = Range(Range("d82"), Range(BC))
ActiveCell.Offset(1, 0).Select
ActiveCell = WorksheetFunction.Sum(rng)
EDIT:
Fixed it!
ActiveCell.Offset(1, 3).Select
ActiveCell.FormulaR1C1 = "=SUM(R82C4:R[-1]C)"

If you don't mind using a formula with OFFSET, you could just change =SUM(D82:D92) in cell D93 into =SUM(D82:OFFSET(D93,-1,0)). This formula will update as expected: it will just keep looking for a reference to 1 row above itself to find the end of the sum range. E.g. a sub with Range("D93").EntireRow.Insert will push the formula into D94, now as =SUM(D82:OFFSET(D94,-1,0)).
Edit: I suggested OFFSET, thinking that INDEX wasn't possible in this case, but of course it is. Same result can be gained by using =SUM(D82:INDEX(D82:D93,ROW(93:93)-82)) entered into D93. The benefit would be that OFFSET is a 'volatile function', which will recalculate with each worksheet change. With INDEX, you don't have this problem.

Related

How to copy first cell of a column, do something and then copy the next first cell of a column?

Looking for a solution to probably a very simple problem but somehow I can not seem to find an answer.
I am looking for a way to copy the first cell of each column. I want to use the copied value to filter it in another workbook. I want to paste the filtered cells below and then repeat the process until all columns have been gone through.
Thus what kind of loop would I use to copy every first cell at a time, store it, then do something with it and then copy the next first cell of a column for all columns?
Could someone please me with this? Your help is much appreciated.
For i = 1 To 207
Columns(i).Select
ActiveCell.Offset(0, 0).Range("A1").Select
Selection.Copy
'How do I store the copied value to use it in an autofilter?
Next i
End Sub
Instead of selecting – and you should avoid using select –
You can set the value to a variable varname = Cells(1,i) for later use,
or better yet, apply directly to your filter.
Dim i As Long
Dim wb2 As Workbook
Set wb2 = Workbooks("Book2") 'the "Other" workbook name here
For i = 1 To wb2.Worksheets.Count 'looping each sheet
wb2.Worksheets(i).Range("A1").AutoFilter Field:=1, Criteria1:= _
ThisWorkbook.Sheets(1).Cells(1, i)
Next i
This is just an example which makes a lot of assumptions that more than likely does not fit with your reality, these has to be changed to fit your scenario, ofc.
But it would go through the specified amount of worksheets in the specified workbook, and filter the first column with the value obtained from the first workbook.
Then, there are quite a few questions on how to copy a filtered range that might give a hint.

How to use a user defined range in a formula in vba

I am trying to define a range starting at B2 (constant) to the last cell with data which will change month to month. I want to take the same range length and define another range for column A which will also start at A2 (constant) but will extend only down as far as column B goes. I'm trying to identify them as range and use the dimmed range in a formula in vba but it doesn't like it...any ideas?
Dim Data As range
Dim Time As range
range("b2").Select
'Select Range
Set Data = range("B2", range("B2").End(xlDown))
Set Time("A2", range("A2").End(xlDown))
ActiveCell.Offset(1, 1).Select
ActiveCell.FormulaR1C1 = _
"=FORECAST.ETS([#Timeline],.address(data),.address(time):R[-1]C[-2],1,0)"
You need to close the formula string, add the address, and then continue:
"=FORECAST.ETS([#Timeline],.address(" & data.address & "),.address(" &
time.address & "):R[-1]C[-2],1,0)"
Note since you're using R1C1 style, you might have to do this on both .address parts,
time.address(ReferenceStyle:=xlR1C1)
so:
"=FORECAST.ETS([#Timeline],.address(" & data.address(ReferenceStyle:=xlR1C1) & "),.address(" &
time.address(ReferenceStyle:=xlR1C1) & "):R[-1]C[-2],1,0)"
Edit: Also, I would change the keyword Time, as I think that's a reserved word. Perhaps Dim timeRng as Range?
In addition to #BruceWayne's answer, to address the first part of your question:
If I have a range B2:B50, and I want the corresponding A column, then I can use the Offset function:
Set time = data.Offset(columnOffset:=-1)
Alternatively you can construct the column like this:
Set time = Sheet1.Range("A2").Resize(Rows(data), 1) 'nrows, 1 column
Then you could put A2 anywhere
FWIW:
range("b2").Select is unnecessary and will really slow down your code if you get into this habit (it's just because the macro recorder doesn't know what you want exactly). You could use Range("B2").Offset(1,1).FormulaR1C1 with no selecting
You can name cells in excel and refer to the names: Range("myNamedCell")
Always best practice to prepend the sheet name and fully qualify references (e.g. Sheet1.Range("A1")) since that will always refer to the same cell, whereas Range("A1") refers to A1 on whichever sheet happens to be selected when you run the macro

VBA Script to Fill Blank Cells Within an Excel Column with the Average of Nonblank Cells within that Same Column

I have no experience with VBA and would love some help. As the title indicates, I'm looking for a script that fills a certain number of blank cells within column G with the average of all nonblank cells within that same range (e.g. fill all blank cells in G16:G59 with the average of all nonblank cells within G16:G59).
To make things more complicated, I'd need to vary the range somewhat dynamically as I wouldn't be sure as to how many rows I'd need to apply this script to and a work colleague who would be using this script might not have any experience with VBA either... The easiest solution I can think of is to have another cell contain the name of the last row in the range, or something like this: "Fill all blank cells in G16:Gx with the average of all nonblank cells within G16:Gx, where x = the row name listed in cell G12". Within G12 I'd have a text that states the last row to define the range, e.g. cell G12 contains the text "G80", which makes the range within the script to read G16:G80.
I know I'm asking for a lot, so if you can even just provide guidance on the first bit, I'd be very grateful! Thank you in advance for your time.
I think we can all remember what it was like when we first started out with VBA coding – which is why I’m helping you here. Normally, you’d be lucky to get any assistance with your question without providing at least some code & a description where it wasn’t doing what you wanted. Using the Record Macro button is always a good place to start.
Assumptions made here are that the data is on “Sheet1” in your file, and that there is a value in the last cell in Column G in the range you’re interested in. If that isn’t the case, let me know and I’ll show an alternative method to find the last row.
I’ve added descriptions about what (most) code does in each case to help you understand what’s going on. Let me know how you go with it.
Option Explicit '<~~ get in the habit of putting this at the top of your code
Sub FillInBlanks()
Dim ws As Worksheet '<~~ declare all variables
Dim LastRow As Long '<~~ use Long not Integer
Dim cel As Range '<~~ use intuitive variable names
Dim avg As Double '<~~ Double used here if you want decimal places in the average
Set ws = Sheets("Sheet1") '<~~ be explicit with sheet references
'Best practice to determine last used row in column
LastRow = ws.Cells(Rows.Count, "G").End(xlUp).Row '<~~ finds the last used row in column G
'Calculate the average & assign it to the variable "avg"
avg = Application.Average(ws.Range("G16:G" & LastRow))
'Loop through each cell in the defined range - one at a time
For Each cel In ws.Range("G16:G" & LastRow)
If cel = "" Then '<~~ = If the cell is blank, then...
cel = avg '<~~ ...put the average value (avg) in the cell
cel.Font.Color = RGB(51, 102, 0) '<~~ change color to suit
End If
Next cel '<~~ go to the next cell in the range
End Sub

Repeating Formula with Incrementing Cell Reference

So I have built a formula that has Absolute Cell References, and wanted to repeat the same formula down 3000 cells with each one referencing increment cells. (1st formula referring to Cell $A$1, 2nd formula referring to $A$2) I know that I could easily do this without referencing exact cells and the Fill Handle and this is currently how it's set up, however there's a very large number of people who work in this spreadsheet that have bad Excel manners, and regularly delete rows and cells or copy and paste, which breaks the formulas.
Rather than manually editing the same formula in each cell to change the references from relative to absolute, I was wanting to run a Macro to automatically run the formula for 3000 cells.
I had at first built a Macro that fills 20 cells with the formula, but it didn't adjust the formula based on the active cell. (Always entered with range $A$1:$A$20, and not $A$21:$a$40 when started further down) I changed the Macro to loop, but it looks with all formulas referencing $A$1 rather than updating.
The Macro set up to loop is as follows:
Sub HDDatesRef()
ActiveCell.Select
ActiveCell.FormulaR1C1 = "=IF(AND(HD!R1C1>0,ISBLANK(HD!R1C4)),HD!R1C1,""n/a"")"
ActiveCell.Offset(1, 0).Range("A1").Select
Loop Until ActiveCell.Value = ""
End Sub
Any and all help with figuring this out would help immensely. Right now I also have access to Liknedin Learning, so if there's any suggestions for courses on there I should look into so I can understand what I need to do will help with this.
The Excel application object has a function called ConvertFormula which you can use to change a formula between reference styles (A1 or R1C1) and to specify whether the rows and columns should be relative references or absolute references.
If you start off by creating the formula in each row as a relative reference then you can use ConvertFormula to turn it into an absolute reference. The only restriction is that the formula cannot be longer than 255 characters.
Adapting your code and following the advice in How to avoid using Select in Excel VBA gives us:
Option Explicit
Sub HDDatesRef()
Dim r As Range
' If we know the cell address we want to start in then we could use that directly
' e.g. Set r = Worksheets("HD").Range("E1")
Set r = ActiveCell
Do
' The With block just saves us typing r.FormulaR1C1 multiple times
With r
' Don't know what your relative formula would be. I've assumed that we are
' working in column E but adjust as appropriate
.FormulaR1C1 = "=IF(AND(HD!RC[-4]>0,ISBLANK(HD!RC[-1])),HD!RC[-4],""n/a"")"
' Take the formula we already have which is in R1C1 format, keep it in R1C1 format,
' change it from a relative reference based on cell r to an absolute reference
' and make that the new formula for this cell
.FormulaR1C1 = Application.ConvertFormula(.FormulaR1C1, xlR1C1, xlR1C1, xlAbsolute, r)
End With
' Move down one row
Set r = r.Offset(1, 0)
Loop Until r.Value = ""
End Sub
In case you aren't familiar with them. here are the references for Option Explicit and With...End With
You can do this without looping, Excel is smart enough to know you want incremental.
As an example do run this on a fresh sheet:
Sub ShowIncremental()
Range("A1:A10").Formula = "=Row(A1)"
Range("B1:B10").Formula = "=A1*2"
Range("C1:C10").Formula = "=sum(B$1:B1)"
End Sub
Notice the formulas created in A1:C10. Notice Excel incremented them even though the code didn't say to except in the case where we absoluted B$1.
I recommend you do something similar with your code to avoid looping, this will be much much faster.

Excel VBA to look for the last non-blank cell in a column and convert it to a value

I have a column thats rows are currently filled with this formula:
=IF(VLOOKUP(H3,B:D,3,0)="NOT_FOUND","",VLOOKUP(H3,B:D,3,0))
That fills rows one by one with the value that it finds. I am hoping that there is a macro that will search the column (I) for the single last non-blank cell and convert the formula answer into a value so I can eventually sum all of the values. I assume it is not a very difficult macro, but I have no experience working with VBA so any help would be appreciated!
Here is a pic of part of the table I am trying to make. Where the 13.8 is I would like for that to be converted to just a value since it is the last non-blank cell in the column. Please let me know if this makes sense or if more info is needed. Thank you!
nofriendsnojo, if overwriting the formula in a cell (in this case VLOOKUP) is all you need, then please see my code below:
Sub paste_values()
Dim lastRow As Long
Dim cel As Range
'get last non-blank cell in column I
lastRow = Cells(Rows.Count, "I").End(xlUp).Row
'loop that overwrites cell contents with simple values
For Each cel In Range("I1:I" & lastRow)
cel.Copy
cel.PasteSpecial xlPasteValues
Next cel
Application.CutCopyMode = False
End Sub
This is the basic code, it copies values of the cell and then pastes them to the same cell as values. This ultimately gets rid of any formula and converts the result of the formula into a simple value. It is quite common practice.
Of course, you can then add some references like ThisWorkbook. or ActiveSheet. or whatever scope you need.
I hope this solves your issue or atleast directs you in the right way.

Resources