Extracting values to fit into changing equations - vba

Not sure how to approach this situation. The best way to explain it might be visually, below is the diluted situation of what I have at hand:
Sheet 1:
Column A Column B
Chocolate 20
Vanilla 10
Strawberry 30
Sheet 2:
Column A
Chocolate + Vanilla
Vanilla / Strawberry
Chocolate / (Strawberry + Vanilla)
Goal:
Sheet 2:
Column A Column B
Chocolate + Vanilla 30
Vanilla / Strawberry 1/3
Chocolate / (Strawberry + Vanilla) 1/2
The trouble I'm having is, I could do an index/match or vlookup approach and pull the numbers corresponding to the flavors (chocolate, vanilla, strawberry) individually - but is there a way for Excel to know what arithmetic function based off Sheet 2, column A to follow instead of me manually adjusting each row to fit the right formula?
So essentially, some sort of function or VBA method that will tell Excel, "Grab & understand the arithmetic symbol/equation in that row and follow that command" so I don't have to adjust each row to the correct math symbol?

if your formulas keep sticking to Excel UI formula input rules then you could give this a try
Sub Main()
Dim cell As Range
Dim strng As String
Dim element As Variant
Dim elementValue As Variant
With Worksheets("Sheet2")
For Each cell In .Range("A1", .Cells(.Rows.Count, 1).End(xlUp))
strng = cell.Value2
For Each element In Split(Replace(Replace(cell.Value2, "(", ""), ")", ""))
If GetValue(CStr(element), elementValue) Then strng = Replace(strng, element, elementValue)
Next
cell.Offset(, 1).Formula = Replace("=" & strng, " ", "")
Next
End With
End Sub
Function GetValue(elementName As String, elementValue As Variant) As Boolean
Dim f As Range
With Worksheets("Sheet1")
Set f = .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).Find(what:=elementName, lookat:=xlWhole, LookIn:=xlValues)
If Not f Is Nothing Then
elementValue = f.Offset(, 1)
GetValue = True
End If
End With
End Function

Basically we could use RegEx to recognize the 'name' and then get the address of the value of the 'name'. Then we could derive a formula in Sheet2.
Sub Test()
Set mysheet2 = ThisWorkbook.Worksheets("Sheet2")
Set regEx = New RegExp
regEx.Pattern = "\w+"
regEx.Global = True
For i = 1 To mysheet2.UsedRange.Rows.Count
formula_str = mysheet2.Cells(i, 1).Value
Set oMatches = regEx.Execute(formula_str)
For Each oMatch In oMatches
formula_str = Replace(formula_str, oMatch, getAdd(oMatch))
Next
mysheet2.Cells(i, 2) = "=" & formula_str
Next
End Sub
'Function to get the address of 'names'
Function getAdd(keyword)
Set mysheet1 = ThisWorkbook.Worksheets("Sheet1")
getAdd = ""
With mysheet1
For i = 1 To .UsedRange.Rows.Count
If .Cells(i, 1) = keyword Then
getAdd = mysheet1.Name & "!" & .Cells(i, 2).Address
Exit For
End If
Next
End With
End Function
Currently this solution has the limit that all the 'names' must be alphabet format. If you want to use names containing digits and other characters, you should change the RegEx pattern.

Related

VBA - check if a string is is 1 of those in a column of a different sheet, in an if statement

Hello i want to simpify the formula from
If InStr(1, Sheets("Le 2250").Cells(i, 1).Value, "250-") Or _
If InStr(1, Sheets("Le 2250").Cells(i, 1).Value, "135-") Or _
If InStr(1, Sheets("Le 2250").Cells(i, 1).Value, "700-")
to have the "250-" be 1 of the values in a column of a specific sheet, rather than having to put many "Or if ()" functions with the numerous strings i have to lpok for
Any help appreciated.
Here is an alternative that uses the Evaluate method...
If Evaluate("OR(ISNUMBER(MATCH({""*250-*"",""*135-*"",""*700-*""},{""" & Sheets("Le 2250").Cells(i, 1).Value & """},0)))") Then
Note, however, the number of characters used with the Evaluate method cannot exceed 255, otherwise an error will be returned.
Basically, build an array of your test values, and loop that array until you find something.
Something like this
Sub Demo()
Dim ws As Worksheet
Dim rTestStings As Range, TestStings As Variant
Dim TestValue As Variant
Dim idx As Long
Dim Found As Boolean
'Get Test Strings from Sheet. Adjust to suit your data
With rTestStings = Worksheets("specific sheet")
Set rTestStings = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
TestStings = rTestStings.Value2
Set ws = Sheets("Le 2250")
'I'm guessing you are doing something like this
For i = SomeValue To SomeOtherValue
TestValue = ws.Cells(i, 1).Value
Found = False
For idx = LBound(TestStings, 1) To UBound(TestStings, 1)
If Not IsEmpty(TestStings(idx, 1)) Then 'incase there are gaps in your test data
If InStr(TestValue, TestStings(idx, 1)) Then
Found = True
Exit For
End If
End If
Next
If Found Then
MsgBox "Found " & TestStings(idx, 1) & " in cell " & ws.Cells(i, 1).Address
' do something ...
End If
Next i
End Sub

How To Have VBA Insert Formula Result as a Value

I got help last week getting my syntax and ranges correct, and thought I could just do a vlookup to finish it but apparently I was mistaken. It just seems like when I try to research how to accomplish this, I find various examples but I don't have the background to translate it to my code.
The macro runs and does almost everything its supposed to do. But in addition to inserting the arrays, there are 3 other cells that need values when there are blank cells in my ‘sourcerng’.
This is the logic for the cells that need values (the values are already in my worksheet, I just need to get them to these blank cells). I tried to do an IIF statement for these but I still have no idea what I'm doing. Would that be the best way? Should it just be another IF THEN statement?
rngBE - IF Column Z = 0 Then copy value from corresponding row in column O. Otherwise copy value from column Z
rngBG - IF Column AA = "Unknown" Then copy value from corresponding row in column I. Otherwise copy value from column AA.
rngBK - IF Column AB = "Unknown" Then copy value from corresponding row in column N. Otherwise copy value from column AB.
Sub AutomateAllTheThings6()
Dim arr3() As String
Dim arr11() As String
'Dim resBE As String
Dim rng3 As Range
Dim rng11 As Range
Dim rngBE As Range
Dim rngBG As Range
Dim rngBK As Range
Dim sourcerng As Range
'Dim firstRow As Long
Dim lastRow As Long
'Dim i As Long
Call OptimizeCode_Begin
'firstRow = 2
lastRow = ActiveSheet.Range("D1").End(xlDown).Row
Set rng3 = ActiveSheet.Range("BH2:BJ" & lastRow)
Set rng11 = ActiveSheet.Range("BL2:BV" & lastRow)
Set rngBE = ActiveSheet.Range("BE2:BE" & lastRow)
Set rngBG = ActiveSheet.Range("BG2:BG" & lastRow)
Set rngBK = ActiveSheet.Range("BK2:BK" & lastRow)
Set sourcerng = ActiveSheet.Range("BE2:BE" & lastRow)
arr3() = Split("UNKNOWN,UNKNOWN,UNKNOWN", ",")
arr11() = Split("UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,00/00/0000, _
00/00/0000,00/00/0000,00/00/0000,NEEDS REVIEW", ",")
For Each cell In sourcerng
If IsEmpty(cell) Then
Intersect(rng3, ActiveSheet.Rows(cell.Row)).Value = arr3
Intersect(rng11, ActiveSheet.Rows(cell.Row)).Value = arr11
'***PLS HELP***
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = "WEEEEE"
Intersect(rngBG, ActiveSheet.Rows(cell.Row)).Value = "WOOOOO"
Intersect(rngBK, ActiveSheet.Rows(cell.Row)).Value = "WAAAAA"
End If
Next
Range("BR2:BU2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.NumberFormat = "mm/dd/yyyy"
Columns("BF:BF").Select
Selection.Delete Shift:=xlToLeft
Call OptimizeCode_End
End Sub
'*********TESTING***********
'resBE = IIf(Cells(13,Z).Value = 0, Cells(13,BE).Value = Cells(13,Z), Cells(13,BE).Value = Cells(13,O))
'***************************************
'For i = firstRow To lastRow
' valZ = Range("Z" & i)
' valOh = Range("O" & i)
'
' If valZ = 0 Then
' rngBE.Value = valOh
' Else rngBE.Value = valZ
' End If
There are several ways to do your task. If you're more of an "Excel" person than VBA you might consider this approach: You can inject the syntax of any "regular" formula in R1C1 Format.
So the formula mentioned above =if($Z2=0,$O2,$Z2) is .FORMULA format for any value in row 2.
But in .FORMULAR1C1 it can be inserted in ANY cell as: =IF(RC26=0,RC15,RC26) (basically no rows up or down, but always columns O (15) and Z(26).
So, your modified code would have something like this:
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).FormulaR1C1 = "=IF(RC26=0,RC15,RC26)"
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = _
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value
Again, this is NOT the most efficient way to accomplish your task, but if you're dealing with thousandsof rows, versus tens to hundreds of thousands, I wouldn't worry about it and it gives you a new tool to use.

Iterating through row range group data and take an action

I am getting to know Excel VBA. I have a working program that uses an action button on one sheet opens a source workbook and data worksheet, selects data and puts that into a second workbook and destination sheet. I then sort the data as needed and it looks like this
Destination sheet, sorted and annotated duplicates
I am now trying to select the data based on col 2 "B" where the items are duplicated and/or not duplicated then perform an action (send an email to the manager about the staff under their control). I can get an email to work but its selecting the data that I'm having trouble with.
the output data would be col 1 & col 3 to 5 e.g.
Dear Manager1,
you staff member/s listed below have achieved xyz
Person1 22/06/2017 11/08/2017 22/08/2017
Person11 22/06/2017 11/08/2017 22/08/2017
Person15 22/06/2017 11/08/2017 22/08/2017
congratulations....
So what I hope somebody can help me with is a clue how I get to look at the data in col 2
add the Row data required to an array or something then to check the next Row add it to the same something until it is different to the next Row Pause do the action then do the next iteration. Resulting in:
Manager1 .....Person 1,11,15action
Manager10 ..... Person 10action
Manager2 ..... Person 12,16,2,25,28action
Manager3 ..... Person 13,17,26,29,3action
until last line is reached.
I am so confused with arrays / lookups and loops I have lost the plot somewhere along the way.
I have a variable lastTmp which tells me the last line of data in the set, this will vary each month.
The Range is:
Set rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).row)
The last piece of my working code is:
Dim lp As Integer
lp = 1
For Each cell In rng1
If 1 < Application.CountIf(rng1, cell.Value) Then
With cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next cell
You will be better placed to confront confusion if you do your indenting more logically. Related For / Next, If / Else / End If and With / End With should always be on the same indent level for easier reading. I rearranged your original code like this:-
For Each Cell In Rng1
If 1 < Application.CountIf(Rng1, Cell.Value) Then
With Cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With Cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next Cell
It now becomes apparent that the With Cell / End With need not be duplicated. I have further presumed that your variable lp actually was intended to hold the count. That made me arrive at the following compression of your code.
Dim Rng1 As Range
Dim Cell As Range
Dim lp As Integer
' the sheet isn't specified: uses the ActiveSheet
Set Rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
For Each Cell In Rng1
With Cell
lp = Application.CountIf(Rng1, .Value)
.Offset(0, 4) = IIf(lp, "", "NOT ") & "duplicate : "
.Offset(0, 5) = lp
End With
Next Cell
Consider using a Dictionary or Collection, whenever, checking for duplicates.
Here I use a Dictionary of Dictionaries to compile lists of Persons by Manager.
Sub ListManagerList1()
Dim cell As Range
Dim manager As String, person As String
Dim key As Variant
Dim dictManagers As Object
Set dictManagers = CreateObject("Scripting.Dictionary")
For Each cell In Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
manager = cell.Value
person = cell.Offset(0, -1).Value
If Not dictManagers.Exists(manager) Then
dictManagers.Add manager, CreateObject("Scripting.Dictionary")
End If
If Not dictManagers(manager).Exists(person) Then
dictManagers(manager).Add person, vbNullString
End If
Next
For Each key In dictManagers
Debug.Print key & " -> "; Join(dictManagers(key).Keys(), ",")
Next
End Sub
I recommend you wanting Excel VBA Introduction Part 39 - Dictionaries
Assuming your data is as in the image
Then following code will give you result as in the image below.
Sub Demo()
Dim srcSht As Worksheet, destSht As Worksheet
Dim lastRow As Long, i As Long
Dim arr1(), arr2()
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
Set srcSht = ThisWorkbook.Sheets("Sheet2") 'change Sheet2 to your data sheet
Set destSht = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your output sheet
arr1 = Application.Index(srcSht.Cells, [row(1:7000)], Array(2, 1)) 'See note below
arr2 = arr1
For i = 1 To UBound(arr1, 1)
If Not dict.exists(LCase$(arr1(i, 1))) Then
dict.Add LCase$(arr1(i, 1)), i
Else
arr2(i, 1) = vbNullString
arr2(dict.Item(LCase$(arr1(i, 1))), 2) = arr2(dict.Item(LCase$(arr1(i, 1))), 2) & "," & arr1(i, 2)
End If
Next
destSht.Range("A1").Resize(UBound(arr1, 1), UBound(arr1, 2)) = arr2 'display result
destSht.Columns("a").SpecialCells(xlBlanks).EntireRow.Delete
End Sub
Note : For details on assigning range to array see this.

Need to find an occurrence of a value in any beyond worksheet one and return the value of A:1 for each worksheet

I need to search all worksheets for the values in Worksheet one column A. The behavior should be similar to a CTRL-F find all selection. In A:1 of every worksheet is a name and if the value from column A is in that worksheet then A:1 will be returned. I do not need VLookup or HLookup. It might be doable with index and search combo, but I am not finding a good way to do that. I know I need an array search of some sort since I need to search everywhere. I have a solution that does not scale and is sloppy on the return. This is the formula I am currently using.
Column A is where the search values are pasted. Columns B-Z or however far is needed get the formula pasted in the first 200 rows which is the limit of the allowed search terms.
{=IF(OR($A2<>""),IF(OR($A2=Sheet26!$A$1:SZ$25000),Sheet26!A$1,"Not Found"),"")}
That is the formula for column Z and the sheet numbers are changed for each column that has a sheet. What I need to adjust this to is only having the formula in column B and it returning a concatenated value of all the names it found. There are lots of questions dealing with just one value or one range like this EXCEL: Need to find a value in a range of cells from another worksheet and return value from adjacent cell but nothing that actually answers what I need.
Currently the result I get is something like this.
A B C D E ...
Star Bob Not Found Ann Not Found
Light Bob Jill Not Found Not Found
378 Not Found Jill Not Found Not Found
What I would like to have is this
A B
Star Bob, Ann
Light Bob, Jill
378 Jill
How can I modify my formula to accomplish that?
Thanks
If you get tired of the formula approach, here is a VBA approach that should do what you describe.
It looks at column 1 on sheet1 to get a list of words to search for
read that list into a vba array (for speed)
for each item in the list, search each worksheet to see if the item exists
I added each item to a Dictionary, and then concatenated the results with commas, but you could also construct a string on the fly, to store in the second "column" of the array
After all is done, we write the results back to the worksheet.
It should be able to handle any reasonable number of worksheets and search terms
If necessary, you can limit the range to search on each worksheet; exclude certain worksheets from being searched; look at partial matches in a cell; select a case-sensitive search; etc.
If there are blank entries between the first and last search terms, I have excluded the search.
Option Explicit
Sub FindAllColA()
Dim WB As Workbook, WS As Worksheet
Dim WS1 As Worksheet
Dim D As Object
Dim V
Dim R As Range
Dim FirstRow As Long, LastRow As Long
Dim I As Long
Set D = CreateObject("scripting.dictionary")
Set WB = ThisWorkbook
Set WS1 = WB.Worksheets("Sheet1")
With WS1
If .Cells(1, 1) <> "" Then
FirstRow = 1
Else
FirstRow = .Cells(1, 1).End(xlDown).Row
End If
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
'V will hold both search terms and the results
V = .Range(.Cells(FirstRow, 1), .Cells(LastRow, 1)).Resize(columnsize:=2)
End With
For I = 1 To UBound(V)
If Not V(I, 1) = "" Then
D.RemoveAll
For Each WS In WB.Worksheets
If Not WS.Name = WS1.Name Then
With WS
If Not .Cells.Find(what:=V(I, 1), LookIn:=xlValues, _
lookat:=xlWhole, MatchCase:=False) Is Nothing Then
D.Add .Cells(1, 1).Text, .Cells(1, 1).Text
End If
End With
End If
Next WS
V(I, 2) = Join(D.Keys, ",")
Else
V(I, 2) = ""
End If
Next I
With WS1
Set R = .Range(.Cells(FirstRow, 1), .Cells(LastRow, 2))
R.EntireColumn.Clear
R = V
R.EntireColumn.AutoFit
End With
End Sub
Another way, would be an UDF which can be used in a wider range without any changes like:
Public Function ValString(search_term As String, cell_string As Variant, ParamArray ignored_sheets()) As Variant
Dim x As Variant
If TypeOf cell_string Is Range Then cell_string = cell_string.Address
If Not TypeOf Evaluate(cell_string) Is Range Then
ValString = CVErr(2023)
Exit Function
ElseIf Range(cell_string).Cells.Count > 1 Then
ValString = CVErr(2023)
Exit Function
End If
If IsMissing(ignored_sheets) Then
ignored_sheets = Array(Application.Caller.Parent.Name)
Else
For x = 0 To UBound(ignored_sheets)
If TypeOf ignored_sheets(x) Is Range Then
ignored_sheets(x) = ignored_sheets(x).Parent.Name
ElseIf TypeName(ignored_sheets(x)) = "String" Or IsNumeric(ignored_sheets(x)) Then
ignored_sheets(x) = Format(ignored_sheets(x), "#")
Else
ignored_sheets(x) = ""
End If
Next
End If
For Each x In ThisWorkbook.Worksheets
If IsError(Application.Match(x.Name, Array(ignored_sheets)(0), 0)) Then
If Not x.Cells.Find(search_term, , -4163, 1, , , True) Is Nothing Then
ValString = ValString & ", " & x.Range(cell_string).Value2
End If
End If
Next
If Len(ValString) Then
ValString = Mid(ValString, 3)
Else
ValString = CVErr(2042)
End If
End Function
Put the code in a Module and you can use it like a normal formula in your sheet.
Example:
=ValString(A1,"A1")
Or for your case:
=IFERROR(ValString(A1,"A1"),"Not Found")
Use: ValString([search_term],[cell_string],{[ignored_sheet1],[ignored_sheet2],...})
[search_term]: the string to look for
[cell_string]: the address of a cell as ref or string which you want to output if found
[ignored_sheets]: (optional) the sheet names as strings or a ref to them you want to ignore
If [ignored_sheets] is omitted the sheet you have the formula in will be ignored. To include all sheets in the workbook simply set it to ""
If nothing was found it will return #N/A! (which is good as you can catch this to set whatever output you want without changing the code)
If [cell_string] is not an address-string and/or goes for multiple cells, it will return #REF!
[ignored_sheets] is used as a list like =ValString(A1,"A1",Sheet1!A1,Sheet5!A1) or =ValString(A1,"A1","Sheet3","Sheet4","Sheet7","MyWhateverSheetName"). If used in the ref-way, you can rename the sheets and it will also in the formula. This is good if there is a summary sheet you do not want to check. But keep in mind: if used, the sheet with the formula itself, also needs to be included!
If you still have any questions, just ask ;)
try this UDF
Function findKeywords(findMe As String) As String
findKeywords = ""
Dim sheetToSkip As String
sheetToSkip = "Sheet1"
Dim sht As Worksheet
For Each sht In ActiveWorkbook.Sheets
If sht.Name <> sheetToSkip And Len(findMe) > 0 Then ' do not look for blank cells
' note: LookAt:=xlWhole ... whole word LookAt:=xlPart ... partial
Dim aaa As Range
Set aaa = sht.Cells.Find( _
What:=findMe, _
After:=sht.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not aaa Is Nothing Then
If Len(findKeywords) = 0 Then
findKeywords = sht.Range("a1")
Else
findKeywords = findKeywords & ", " & sht.Range("a1")
End If
End If
End If
Next sht
' If Len(findKeywords) = 0 Then findKeywords = "Not Found" ' uncomment to return "Not Found" if desired
' Debug.Print findKeywords
End Function

VBA: adding up irregular ranges

I need some help to create a macro which adds all the values on the column E between the rows with the "avg" word. the result should be displayed on the cells where the "Sum here" label is displayed. Both texts "avg" and "sum here" is just for illustrate the example, "avg" could be replaced by any other word and "sum here" should actually be the aggregation of the values above it.
The real challenge is that the number of ranges on column E is variable, so i would like to find a macro which is able to deal with "n" number of ranges on column E.
Finally, the values on column D are only the example of the expected value on the "sum here" cells.
This is what I have tried to far:
Sub Macro1()
'
' Macro1 Macro
'
Dim sumhere As Range
Dim startingpoint As Range
Dim endingpoint As Range
'
Range("C17").Select
Selection.End(xlDown).Select
If ActiveCell = "avg" Then
ActiveCell.Offset(rowoffset:=0, columnoffset:=2).Select
Set sumhere = ActiveCell
Set startingpoint = ActiveCell.Offset(rowoffset:=-1, columnoffset:=0)
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=sum(range(startingpoint:endingpoint)"
Else
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=Sum(Range(startingpoint.adress:endingpoint.adress))"
Else: End If
End If
End If
End Sub
Additionally, as you can see, I do not know, how to define a range using variables. My original idea was to combine this code with some kind of "do while" or/and "for i= 1 to x" and "next i". But I can't see how to combine it.
Using formula only, and providing that column A only has avg (or any text) on each subtotal row.
I've given two versions of the formula - the volatile version (updates everytime you change anything on the spreadsheet), and the non-volatile version (only updates if it needs to).
The formula should be entered on row 6 - change the $E6 to which ever row you need.
(volatile)
=SUM(OFFSET($E6,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)-ROW()+1,,ROW()-1-IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)))
(non volatile):
=SUM(INDEX($E:$E,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)+1):INDEX($E:$E,ROW()-1))
or if you don't mind using a helper column:
In cell B6:
=IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)
In E6: (volatile)
=SUM(OFFSET($E6,$B6-ROW()+1,,ROW()-1-$B6))
or (non volatile):
=SUM(INDEX($E:$E,$B6):INDEX($E:$E,ROW()-1))
Edit:
Thought I'd add a UDF to calculate it to if you're after VBA.
Use the function =AddSubTotal() in the rows you want the sub total to be shown in, or use =AddSubTotal("pop",6) to sum everything in column F (col 6) using "pop" rather than "avg".
Public Function AddSubTotal(Optional Delim As String = "avg", Optional ColNumber = 5) As Double
Dim rCaller As Range
Dim rPrevious As Range
Dim rSumRange As Range
Set rCaller = Application.Caller
With rCaller.Parent
Set rPrevious = .Range(.Cells(1, 1), .Cells(rCaller.Row - 1, 1)).Find(Delim, , , , , xlPrevious)
If Not rPrevious Is Nothing Then
Set rSumRange = rPrevious.Offset(1, ColNumber - 1).Resize(rCaller.Row - rPrevious.Row - 1)
Else
Set rSumRange = .Range(.Cells(1, ColNumber), .Cells(rCaller.Row - 1, ColNumber))
End If
End With
AddSubTotal = WorksheetFunction.Sum(rSumRange)
End Function
The following VBA routine assumes that
your data is in Columns C:E
Nothing else relevant (nothing numeric) in that range
Your "key word" where you want to show the sum is avg
avg (the key word) is hard-coded in the macro
You could easily modify this routine to also perform an average of those values, and put those results, for example, in Column D
Any of the above are easily modified
Option Explicit
Sub TotalSubRanges()
Dim vSrc As Variant, rSrc As Range
Dim dAdd As Double
Dim I As Long
Const sKey As String = "avg"
Set rSrc = Range(Cells(1, "C"), Cells(Rows.Count, "C").End(xlUp)).Resize(columnsize:=3)
vSrc = rSrc
'Do the "work" in a VBA array, as this will
' execute much faster than working directly
' on the worksheet
For I = 1 To UBound(vSrc, 1)
If vSrc(I, 1) = sKey Then
vSrc(I, 3) = dAdd
dAdd = 0
Else
If IsNumeric(vSrc(I, 3)) Then dAdd = dAdd + vSrc(I, 3)
End If
Next I
'write the results back to the worksheet
' and conditionally format the "sum" cells
With rSrc
.EntireColumn.Clear
.Value = vSrc
.Columns(3).AutoFit
.EntireColumn.ColumnWidth = .Columns(3).ColumnWidth
.FormatConditions.Delete
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:="=" & .Item(1, 1).Address(False, True) & "=""" & sKey & """"
With .FormatConditions(1)
.Interior.ColorIndex = 6
End With
End With
End Sub
Surely you just need something like:
Sub sums()
Dim i As Integer, j As Integer, k As Integer
j = Range("C1048576").End(xlUp).Row
k = 1
For i = 1 To j
If Range("C" & i).Value <> "" Then
Range("E" & i).Value = "=Sum(E" & k & ":E" & i - 1 & ")"
k = i + 1
End If
Next i
End Sub
Change:
Dim startingpoint As Range
Dim endingpoint As Range
To:
Dim startingpoint As Variant
Dim endingpoint As Variant
As the startingpoint and endingpoint is used in a formula, you cant define them as a Range.