Using .formula to sum values in dynamic ranges - vba

I created a macro that changes the layout of a raw excel file and displays some usefull information to the user. But I am stuck now.
I want to display the sum of some cells in a column, but the cell where the sum is and the column itself is set dynamically.
My layout looks like this:
Year1 Year2 Year3
Sum1 100
Sum2 50 48
Sum3 72 81
Sum4 26
------------------------------------------
Total
I want to display the sum of (sum1, sum2, sum3, sum4) in the Total row under each Year.
The thing is my years are set dynamically depending on the value of a parameter (i.e. I can have 2, 3 or 5 years).
I know I can do it using Range.WorksheetFunction.Sum but the problem is my values are subject to some changes after the macro has been used and if I use this function the values won't change afterward.
That's why I want to use the function Range.Formula to display these values as I can enter the sum like I did for the values already in the sheet.
But unlike these values (that are the sum of values in an unique column easy to select), I am not able to select the colum dynamically.
Here is what I tried to give you an idea of what I want to do if it is unclear to you:
For i=0 to Duration-1
Sheet2.Range("D" & RNumber + 7 + Duration).Offset(0,1+2*i).Formula="=SUM(Sheet2!" & Range("D" & RNumber + 6).Offset(0,1+2*i) & ":Sheet2!" & Range("D" & RNumber + 6 + Duration).Offset(0,1+2*i) & ")"
Next i
But it doesn't work as the sum doesn't recognize the range. I know I should have something like
"=SUM(Sheet2!E" & Firstrow & ":Sheet2!E" & Lastrow & ")"
but then i won't be able to select my colum dynamically.

It looks like your problem stems from the fact that you can't do things like "Sheet2!" & D+3+1 for columns the same way you can do "Sheet2!D" & 5+6+2 for rows. My opinion is that this is one of the many flaws created by the insane A1 reference style, and the best way to attack this problem at the root is to simply switch to R1C1 at the beginning of the macro.
With Application
If .ReferenceStyle = xlA1 Then
.ReferenceStyle = xlR1C1
End If
End With
Some day, I plan to start a non-profit effort to rid the world of the mis-begotten $A4:B$3 craziness altogether. But too many people are used to A1. And if you send your boss or co-worker a spreadsheet with numbers for columns, she might get upset or confused.
So the other solution is to introduce some ADDRESS() elements into your SUMs. The ADDRESS function takes only numbers (well, string for sheet reference) and will return a cell reference in whichever style you want.
For i=0 to Duration-1
Sheet2.Cells(RNumber+7+Duration, 5+2*i).Formula = _
"=SUM(INDIRECT(ADDRESS(" & RNumber + 6 & "," & 5+(2*i) & ",1,TRUE,""Sheet2""),1):" _
& "INDIRECT(ADDRESS(" & RNumber + 6 + Duration & "," 5+(2*i) & _
",1,TRUE,""Sheet2""),1))"
Next i
Or you can use Application.WorksheetFunction.Address() and avoid having the INDIRECT()s cluttering up your fomulas.

Related

Average of the cells of a column after multiplying them with cells from other column

How can I perform this calculation (something like AVERAGEPRODUCT) and make it work? I want to calculate the sum the cells of column Z after multiplying them with the cells of column AC and then divide them with the last cell of column AC. I am sorry for providing so few data but literally, the only thing I need is a working method to perform the line of code below. The line is really messed up . sorry for that.
Picture!
.Range("Z" & i & ").Formula = "=SUM((Z2:Z" & cnt + 1 & ")*(AC2:AC" & cnt + 1 & ")/Range("AC" & i))"
The worksheet function might look something like this:
=SUMPRODUCT(AVERAGE(A1:A3*B1:B3))
With VBA you could use something like this instead:
Range("C1").Value = Evaluate("=Average(A1:A3*B1:B3)")
For you:
.Range("Z" & i).Value = Evaluate("=AVERAGE(Z2:Z" & cnt + 1 & "*AC2:AC" & cnt + 1 & ")")

Index/Match and sum a range based on match

Good morning. Have tried asking this several times with no success.
I have written VBA code with several index/matches.
pasterOH = "=IFERROR(IF(RC[-3]=""Subtotal"","""",IF(RC[-4]="""",INDEX(" & stapler & OH1 & "'!" & "C" & (Month(Worksheets("SEL Onsite OH").Cells(6, 6).Value) + 6) & ",MATCH(RC[-3], " & stapler & OH1 & "'!C3,FALSE),1),INDEX(" & stapler & OH1 & "'!" & "C" & (Month(Worksheets("SEL Onsite OH").Cells(6, 6).Value) + 14) & ",MATCH(RC[-4], " & stapler & OH1 & "'!C2,FALSE),1))),""0"")"
The code works fine to look up a cell in the column that I have specified and return a dollar value if it is a match.
Here is my issue: I need to be able to sum all of the dollar values to the left of the match. For example, in the picture I posted a simple picture of a table with index/match. In the formula the target is column E:E, whereby "tom' returns "5". What I am trying to do is if excel can find and match "tom" in column 5, it would sum B2,C3,D4,E5. I could write this if I knew what column "tom" would be in every time. I need to have a VBA solution that goes along with the pasterOH from above that will allow me to sum the values of column 7 (January) picked by my (Month(Worksheets("SEL Onsite OH").Cells(6, 6).Value) + 6)through whatever column the month happens to be.
For example, In May, the column I am looking up and returning a match to is column 11. I would like the code to start with the matching entry (our "tom" in this case), and sum the results of Jan- May (columns 7-11) and return that as the answer instead of tom's result only in May.
Hope that is clear. I posted a couple of times and found this answer, but don't know if it will apply or how to edit my VBA formula.
Thanks for any assistance
Extending a conditional index-match to sum across a range
Sample table pic
The formula you want is
=SUM(INDEX(B:F,MATCH(C11,A:A,0),0))

Passing a variable into Left(string, VariableHere) not working

I have a data import file that has dates on it without a comma (i.e. January 1 2015). Excel won't recognize this as a date, and I need to work with these dates to find other dates (i.e. Date + Length of trip to find the last day of the trip, etc.)
I'm trying to use VBA to accomplish this and have the logic down but I'm encountering an error.
I'm taking the length of the date (January 1 2015), the 5 right characters (2015), the left of the whole length minus the 5 right characters(JANUARY 1), and then combining these variables with a comma inserted: Left(value-5) & ", " & Right(value, 5)
I'm using this code:
'correct date from JANUARY 1 2000 to JANUARY 1, 2000 so excel can recognize as date
LengthTrpDpt = Len(wb1.Sheets("BL Import").Cells(ioi, TrpDepCol)) 'length of date
LengthRightTrpDPt = Right(wb1.Sheets("BL Import").Cells(ioi, TrpDepCol), 5) 'finds right 5 of date " 2015"
NewListedDate = Left(wb1.Sheets("BL Import").Cells(ioi, TrpDepCol), LengthTrpDpt - 5) & ", " & LengthRightTrpDPt
The problem is with the NewListedDate variable. I can change the part LengthTrpDpt - 5 to a number and it works fine. For some reason I can't have an equation here though. I tried perform the equation in a separately saved variable (LengthMath = LengthTrpDpt - 5) and using LengthMath instead, but that doesn't work either.
LengthTrpDpt works fine and MsgBox's the correct number. Any ideas? Thanks!
You should just Split() the date string to get your three parts, then piece them back together however you'd like:
Dim a
a = Split(wb1.Sheets("BL Import").Cells(ioi, TrpDepCol))
NewListedDate = a(0) & " " & a(1) & ", " & a(2)
Use Range.TextToColumns method on the column of text-that-look-like-dates.
with worksheets("Sheet1").columns(1) '<-change this to reflect the actual worksheet and column
.TextToColumns Destination:=.cells(1,1), DataType:=xlFixedWidth, FieldInfo:=Array(0, 3)
end with
The 0 tells T2C that it should just put everything back into the original column. The 3 is the MDY TextFileColumnDataTypes property.
imho, it is ALWAYS better to work with numbers and dates rather than a string representation approximating them.

Loop Through Columns to Insert SUM Formula

I have a program where I need to find the sum of columns 7 through 30 (G through AD). What I am trying to do is loop through and insert the formula (=SUM(Columns(i)2:Columns(i)1000)) but obviously Columns(i) is not the letter but the number. This is not complying with the format required by SUM so I'm wondering what I can do instead.
I have a program where old sheets will be deleted and new ones added holding configuration data for a product. This means formulas cannot be in the sheet inself or any other sheet referring to it. Prices will be held in columns G through P and U through AD which I need to find the total for and place it in row 1 above the corresponding data. When I try:
For i = 7 To 30
wsNewSheet.Cells(1, i).Value = "=SUM(" & Columns(i).Select & "2:" & Columns(i).Select & "1000)"
Next i
Columns(i) is returning as "True" for some reason. I have also tried to place the totals in a different sheet as the new sheets (wsNewSheet) were being created.
For i = 2 To 30
For f = 7 To 30
wsTotals.Cells(1, i).Value = "=SUM(" & wsNewSheet & "!" & Columns(f).Select & "2:" & Columns(f).Select & "1000)"
Next f
Next i
However, this did not work either. This statement returned "Run-time error '438.' Object doesn't support this property or method." I attemped to do research on this error, but could not fix my situation. Thanks for any help.
You don't need loop here, use single line instead:
Range("G1:AD1").Formula = "=SUM(G2:G1000)"
Excel automatically adjust formulas:
in G1 you'd have =SUM(G2:G1000)
in H1 you'd have =SUM(H2:H1000)
....
in AD1 you'd have =SUM(AD2:AD1000)

Better Excel Formula for Complex Lookup

I am trying to improve a complex lookup procedure that I have inherited. The lookup was being generated through several UDF combined with some standard worksheet functions. However, the issue was that when the user updated some data in the source sheet, the re-calc time was unacceptable.
So, I took a look and thought I may be able to write a better Excel formula only solution. Well, I did find a solution, but it is too much for Excel to handle on large data sets, and it crashes (understandably so!) when my VBA runs the formulas against the dataset.
Now, I could implement this in VBA fully, but then the user would have to press a button or something to update after every change. What I would like is a more simpler approach, if there is one, using some of the advanced Excel 2007 formulas. Since I am not as well-versed on those formulas, I am reaching out for some help!
Okay, here is what I have to work with.
SourceSheet
Tid's, Settlement Dates, and month-end prices (layer periods identified by 1,2,3, etc) in columns like below
Tid SettleDate 1 2 3 4 5 6 7 8 9 10 ... n
FormulaSheet
Amongst other columns, I have the following columns
InitLayer LiqdLayer InstrClass Tid SettleDate InitPrice LiqdPrice Position
I also have the layer numbers in columns to the right of the entire data set, like this:
1 2 3 4 5 ... n
What I need to do is fill in the proper price changes in these columns based on some logic in the dataset by looking up the prices on the source sheet.
In psuedo-formula, this is what I need to happen for each layer column in the FormulaSheet
If Layer < InitLayer OR Layer > LiqdLayer Then Return "-"
ElseIf Layer = InitLayer Then (Layered Price - InitPrice) * Position
where Layered Price is obtained by finding the Intersect of the LayerNumber
Column and Tid Row in the SourceSheet
ElseIf Layer = LiqdLayer Then Previous Layered Price * Position
where Previous Layered Price is obtained by finding the Intersect of the Previous
LayerNumber Column and Tid Row in the SourceSheet
Else (LayeredPrice - Previous Layered Price) * 6
where Layered Price and Previous Layered Price are defined as above
End If
I did come up with this formula, which works well on small data sets, but its toooooooooo big and nasty for large data sets, or just too big and nasty period!
=IF(OR(CH$3<$AT6,CH$3>$AU6),"-",IF($AT6=CH$3,(HLOOKUP(CH$3,layered_prices,RIGHT(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4),LEN(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4))-1)-1,FALSE)-$AV6)*$C6,IF($AU6=CH$3,($AW6-HLOOKUP(CG$3,layered_prices,RIGHT(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4),LEN(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4))-1)-1,FALSE))*$C6,(HLOOKUP(CH$3,layered_prices,RIGHT(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4),LEN(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4))-1)-1,FALSE)-HLOOKUP(CG$3,layered_prices,RIGHT(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4),LEN(ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4))-1)-1,FALSE))*$C6)))
Formula Key
CH = Layer Number
CG = Previous Layer Number
AT = InitLayer
AU = LiqdLayer
AX = InstrClass (used to find a separate lookup for Currencies)
T = Tid
G = SettleDate (used to find a separate lookup for Currencies)
AV = InitPrice
AW = LiqPrice
C = Position
layered_prices = named range for the range of prices under the layer columns in SourceSheet
layered_tid = named range for tid rows in SourceSheet
layered_curtid = named range for currency tid rows in Source Sheet (just a separte lookup if InstrType = Currency, formula the same
Are there any other formulas, or combination of formulas that will allow me to get what I am seeking in a more efficient manner than the monstrosity I have created?
I agree with Kharoof's comment. You should break this formula into several columns. From my count, you need 4 more columns. The benefits are two-fold: 1) Your formula gets much shorter because you're not repeating the same function over and over and 2) You save memory because Excel will calculate it once instead of several times.
For instance, you call the exact same ADDRESS function four times. Excel doesn't "remember" what it was when evaluating a formula and so it calculates it anew each time. If you put it in it's own cell, then Excel will evaluate the cell before any cells that depend on it and store it as a value instead of the formula. When other cells reference it, Excel will provide the pre-evaluated result.
First, here's what your final formula should be: (The names in [brackets] indicate that a helper column fits there. It'll be some cell reference like CI$3 but I wasn't sure where you'd want to put it. You'll have to update those references based on where you add these columns.)
=IF(OR(CH$3<$AT6,CH$3>$AU6),"-",IF($AT6=CH$3,([LayerNumber]-$AV6)*$C6,IF($AU6=CH$3,($AW6-[PreviousLayerNumber])*$C6,([LayerNumber]-[PreviousLayerNumber])*$C6)))
And here are the four helper columns:
[ADDRESS] = ADDRESS(MATCH(IF($AX6="CUR",$T6 & " " & $G6,$T6),IF($AX6="CUR",layered_curtid,layered_tid),1),1,4)
[RIGHT] = RIGHT([ADDRESS],LEN([ADDRESS])-1)
[LayerNumber] = HLOOKUP(CH$3,layered_prices,[RIGHT]-1,FALSE)
[PreviousLayerNumber] = HLOOKUP(CG$3,layered_prices,[RIGHT]-1,FALSE)
By splitting it up, each step of the formula is easier to follow / debug as well as faster to process for Excel. If you want some quantitative improvement, the five formulas combined will be around 70% shorter than the single formula you have right now.