I want to use more than 30 arguments in sum formula for excel, I am using below formula:-
=SUM(IF(AND(ISNUMBER($F$73),ISNUMBER($J$73)),PRODUCT($F$73,$J$73/100),0),IF(AND(ISNUMBER($G$74),ISNUMBER($J$74)),PRODUCT($G$74,$J$74),0))
Above formula will work fine for 30 argumnets, but for more than 30 argumemts excel will return error(#VALUE)
You could batch smaller groups into sub-SUMs like this:
=SUM(SUM(1,2), SUM(3,4),...).
This version will be shorter, you can add as many as you want, only limit will be maximum formula length
=(COUNT(F73,J73)=2)*(F73*J73/100)+(COUNT(G74,J74)=2)*(G74*J74/100)
Perhaps you could save further characters in your formula like this:
=(N(F73)*N(J73)+N(G74)*N(J74))/100
which could be extended much further, but for clarity, I'd suggest writing a custom UDF:
=SPRange((F73,G74),(J73,J74))%
SPRange would work like SUMPRODUCT but operates on multiple ranges:
Function SPRange(Range1 As Range, Range2 As Range) As Double
Dim i As Long, n As Long, rng As Range, Arr() As Double
n = Range1.Count
ReDim Arr(1 To n) As Double
i = 1
For Each rng In Range1
If IsNumeric(rng.Value2) Then Arr(i) = rng.Value2
i = i + 1
Next rng
i = 1
SPRange = 0
For Each rng In Range2
If IsNumeric(rng.Value2) Then SPRange = SPRange + Arr(i) * rng.Value2
i = i + 1
Next rng
End Function
If you want to extend to many ranges you can define names through code:
names.Add "Range1",Range("f73,g74")
names.Add "Range2",Range("j73,j74")
which allows for over 1000 cells in my tests. Then just enter the formula as:
=SPRange(Range1,Range2)%
Related
want to split a time range into 1 hour intervals
split the given time range into 1 hour intervals from cell A2 and A3, the time range will be changed a serval time and on a (Macro) click it should split the given time range into 1 hour intervals.
Create an Hourly Sequence
Sub CreateHourlySequence()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim dt1: dt1 = ws.Range("A2").Value
Dim dt2: dt2 = ws.Range("A3").Value
Dim dfCell As Range: Set dfCell = ws.Range("C2")
dfCell.Resize(ws.Rows.Count - dfCell.Row + 1).ClearContents
Select Case False
Case IsDate(dt1), IsDate(dt2): Exit Sub
End Select
Dim dtDiff As Long: dtDiff = DateDiff("h", dt1, dt2)
Dim dtStart As Date, dStep As Long
Select Case dtDiff
Case Is > 0: dtStart = dt1: dStep = 1
Case Is < 0: dtStart = dt2: dStep = -1
End Select
Dim rCount As Long: rCount = Abs(dtDiff) + 1
Dim Data() As Date: ReDim Data(1 To rCount, 1 To 1)
Dim d As Long, r As Long
If dStep = 0 Then
Data(1, 1) = dtStart
Else
For d = 0 To dtDiff Step dStep
r = r + 1
Data(r, 1) = DateAdd("h", d, dtStart)
Next d
End If
dfCell.Resize(rCount).Value = Data
End Sub
If you are ok with a non-VBA solution, then you have some options.
Option 1: SEQUENCE
For the Excel version listed here, you could use the SEQUENCE function as suggested by chris neilsen.
Example:
Let's assume that your data starts at A1 like this:
Then, in C2, you could have :
=SEQUENCE((A3-A2)/VALUE("01:00:00")+1,1,A2,VALUE("01:00:00"))
Note that VALUE("01:00:00") represents 1 hour (but you could also use TIME(1,,) as suggested by Mayukh Bhattacharya).
Option 2: Dynamic Array Formula
You have an Excel version listed here, you can use a dynamic array formula .
Example:
Making the same assumptions as option 1, for where the data is, you could use a formula like this one:
=(ROW(INDIRECT("1:"&(A3-A2)/VALUE("01:00:00")+1))-1)*VALUE("01:00:00")+A2
Explanations:
Using the INDIRECT function inside the ROW function is a neat trick to get an array with consecutive values. For instance, INDIRECT("1:9") return the array containing rows 1 to 9 and passing it to ROW will return the array as a column like this {1;2;3;4;5;6;7;8;9} (we get only one element per row).
Since we don't know in advance how many steps we will take we calculate the number of elements using (A3-A2)/VALUE("01:00:00")+1 and concatenate it to "1:" to get the range of size that we need.
When we have the sequential array, we just need to make sure it starts by zero, which is why we remove 1 to all elements of the array like this:
ROW(INDIRECT("1:"&(A3-A2)/VALUE("01:00:00")+1))-1
Finally, we multiply each element of the array by the value corresponding to 1 hour and add the starting point in A2.
Option 3: Old array formula
Same idea as option 3 but using the old array formula explained here. Basically, you'll have to use Ctrl+Shift+Enter.
A Simple Solution given your example (to clear cells it is your job :-)
It would be better to write to an array but as example it should be ok.
Option Explicit
Sub TimeToHour()
Dim startTime As Double, endTime As Double, i As Double, z As Double
startTime = Range("a2")
endTime = Range("a3")
Columns(3).NumberFormat = Range("a2").NumberFormat ' Column C
z = 2
For i = startTime To endTime Step 1 / 24
Cells(z, 3) = i ' write to column c starting in row 2
z = z + 1
Next
End Sub
I am trying to find a formula in an Excel VBA that will count the number of numbers in a range of cells. I have tried a formula but it only counts the number of numbers within a single cell. Can anyone help me with this?
=IF(A1="",0,LEN(A1)-LEN(SUBSTITUTE(A1," ",""))+1)
Paste this code in a module
Function GetNumbCount(Rng As Range) As Long
Dim aCell As Range
Dim MYAr As Variant
Dim n As Long, i As Long
For Each aCell In Rng
If InStr(1, aCell.Value, " ") Then
MYAr = Split(aCell.Value, " ")
For i = LBound(MYAr) To UBound(MYAr)
If IsNumeric(Trim(MYAr(i))) Then n = n + 1
Next i
Else
If IsNumeric(Trim(aCell.Value)) Then n = n + 1
End If
Next
GetNumbCount = n
End Function
And then use it in a worksheet
Syntax
GetNumbCount(Range)
ScreenShot
Cannot understand what is needed and why, but to sum the results of the given formula in the range A1:B2, you could use an array formula like this:
{=SUM(IF(A1:B2="",0,LEN(A1:B2)-LEN(SUBSTITUTE(A1:B2," ",""))+1))}
Use Ctrl+Shift+Enter to enter the formula as an array one
IS there a quick way to do this?
For example I have two rows
A B
34 5
56 6
34 3
25 2
I want to do the following calculation down the rows.
A1 = A1+B1
A2 = A2+B2
A3 = A3+B3
..
..
Now I can do this with a macro that loops though the rows like
for x = 1 to 500
sheet1.cells(x,1).vlaue = sheet1.cells(x,1).vlaue + sheet1.cells(x,2).vlaue
next x
but it there a more efficient way by using ranges or something where it can be carried out as a single step?
Cheers
A quick way is to enter from the immediate window:
[a1:a500]=[a1:a500+b1:b500]
The square brackets are a shortcut for the Evaluate function
I can think of two options:
Option 1
Use Copy, Paste Special, Add
Sub AddStep()
Dim rng1 As Range, rng2 As Range
Set rng1 = Range([A1], [A1].End(xlDown))
Set rng2 = rng1.Offset(, 1)
rng2.Copy
rng1.PasteSpecial xlPasteValues, xlPasteSpecialOperationAdd
Application.CutCopyMode = False
End Sub
Option 2
Loop over a variant array. This is much faster than looping over a range
Sub AddStep()
Dim rng As Range
Dim dat As Variant
Dim i As Long
Set rng = Range([B1], [A1].End(xlDown))
dat = rng
For i = LBound(dat, 1) To UBound(dat, 1)
dat(i, 1) = dat(i, 1) + dat(i, 2)
Next
rng = dat
End Sub
Option 1 is faster, but in some circumstances it is best to avoid the clipboard.
Tested on a sample of 1,000,000 rows:
Option 1 - 435ms
Option 2 - 2589ms
The way I would do it is create a formula for the first row, then just select all the rows after and press Ctrl+D. Isn't this what you are after? Unless I misunderstood the question.
I'm trying to create a function that various users can perform the function below on a column that will have a different number of rows for each user, but I can't figure out how to allow for the variability in the number of rows.
Function MKT(ByVal Temperatures As Variant) As Double
Dim Sum As Double
Dim TemperatureCount As Long
Dim GasConst As Double
Dim DeltaH As Double
Dim Conv As Double
Conv = 273.15
GasConst = 8.314472
DeltaH = 10 * GasConst
Sum = 0
For TemperatureCount = Temperatures.Cells.Count To 1 Step -1
Sum = Sum + Exp(-DeltaH / (GasConst * (Temperatures(TemperatureCount) + Conv)))
Next TemperatureCount
MKT = (DeltaH / GasConst) / (-Log(Sum / Temperatures.Cells.Count)) - Conv
End Function
The function works if I set the cell value to =MKT(A1:A32557), but not if I do =MKT(A:A).
I think this could help, but I'm not sure how to implement it:
Dim lastRow As Long
lastRow = ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell).Row
Also, is it possible to offset the values by 1 row to account for a header?
or to avoid hard-coding the row limit use
with sheet1
lastRowColA = .Range("A" & .Rows.Count).End(xlUp).row
Set rng = .range("A2:A" & lastRowColA)
end with
lastRowColA = Range("A65536").End(xlUp).Row
For Each cell In Range("a2:a" & lastRowColA)
'MsgBox cell
Next cell
You can create a subset of Temperatures by intersecting with the Used Range
Dim SubRange as Range
Set SubRange=Intersect(Temperatures,Temperatures.Parent.UsedRange)
or as nick and osknows have posted you can use .End(xlUp), but note that this ignores hidden cells
Function SubRange(theRange As Range) As Range
Dim LastRow As Long
LastRow = theRange.Parent.Cells(theRange.Parent.Rows.Count, theRange.Column).End(xlUp).Row
Set SubRange = theRange.Resize(LastRow - theRange.Row + 1)
End Function
I am trying to get the earliest start date (min) and the furthest end date (max) based on criteria in a source column. I have created several functions based on a solution I found on the internet. I have also tried an array formula solution without using VBA. Neither of the approaches have worked. I have found similar questions/answers on SO but none that correctly apply to my situation.
In my example below I have a Task worksheet and an Export worksheet. The Export worksheet is the source data. In the Task worksheet I am trying to enter a formula that finds the minimum start date. Each Task ID can have several dates so I am trying to find the lowest and highest start dates for each of the tasks. I originally tried using an array formula but ran into the same problem which is that sometimes the formula produces the correct answer and sometimes it gives an incorrect answer and I cannot locate the source of the issue. Any help is much appreciated!
VBA Functions:
Function getmaxvalue(Maximum_range As Range)
Dim i As Double
For Each cell In Maximum_range
If cell.Value > i Then
i = cell.Value
End If
Next
getmaxvalue = i
End Function
Function getminvalue(Minimum_range As Range)
Dim i As Double
i = getmaxvalue(Minimum_range)
For Each cell In Minimum_range
If cell.Value < i Then
i = cell.Value
End If
Next
getminvalue = i
End Function
Function GetMinIf(SearchRange As Range, SearchValue As String, MinRange As Range)
Dim Position As Double
Position = 1
Dim getminvalue As Double
getminvalue = MinRange.Rows(1).Value
For Each cell In SearchRange
If LCase(SearchValue) = LCase(cell.Value) And MinRange.Rows(Position).Value < getminvalue Then
getminvalue = MinRange.Rows(Position).Value
End If
Position = Position + 1
Next
GetMinIf = getminvalue
End Function
Function GetMaxIf(SearchRange As Range, SearchValue As String, MaxRange As Range)
Dim Position As Double
Position = 1
Dim getmaxvalue As Double
For Each cell In SearchRange
If LCase(SearchValue) = LCase(cell.Value) And MaxRange.Rows(Position).Value > getmaxvalue Then
getmaxvalue = MaxRange.Rows(Position).Value
End If
Position = Position + 1
Next
GetMaxIf = getmaxvalue
End Function
The issue is that you are trying to equate positions incorrectly. Use this for the MinIf, it no longer needs the secondary function:
Function GetMinIf(SearchRange As Range, SearchValue As String, MinRange As Range)
Dim srArr As Variant
srArr = Intersect(SearchRange.Parent.UsedRange, SearchRange).Value
Dim mrArray As Variant
mrarr = Intersect(MinRange.Parent.UsedRange, MinRange).Value
Dim minTemp As Double
minTemp = 9999999999#
Dim i As Long
For i = 1 To UBound(srArr, 1)
If LCase(SearchValue) = LCase(srArr(i, 1)) And mrarr(i, 1) < minTemp Then
minTemp = mrarr(i, 1)
End If
Next i
GetMinIf = minTemp
End Function
Max:
Function GetMaxIf(SearchRange As Range, SearchValue As String, MaxRange As Range)
Dim srArr As Variant
srArr = Intersect(SearchRange.Parent.UsedRange, SearchRange).Value
Dim mrArray As Variant
mrarr = Intersect(MaxRange.Parent.UsedRange, MaxRange).Value
Dim maxTemp As Double
maxTemp = 0
Dim i As Long
For i = 1 To UBound(srArr, 1)
If LCase(SearchValue) = LCase(srArr(i, 1)) And mrarr(i, 1) > maxTemp Then
maxTemp = mrarr(i, 1)
End If
Next i
GetMaxIf = maxTemp
End Function
As far as formula go IF you have OFFICE 365 then use MINIFS
=MINIFS(Export!F:F,Export!A:A,A2)
=MAXIFS(Export!G:G,Export!A:A,A2)
If not use AGGREGATE:
=AGGREGATE(15,7,Export!$F$2:F$26/(Export!$A$2:A$26=A2),1)
=AGGREGATE(14,7,Export!$G$2:G$26/(Export!$A$2:A$26=A2),1)
I was trying to use Scott's method as part of a macro to transform an invoice. However, the rows of the invoice fluctuate every month and could grow to as many as a million in the future. Anyway, the bottomline is that I had to write the formula in a way where I could make the last row dynamic, which made the macro go from taking 10-15 minutes (by hardcoding a static last row like 1048576 to run to ~ 1 minute to run. I reference this thread to get the idea for the MINIFS workaround and another thread to figure out how to do a dynamic last row. Make vba excel function dynamic with the reference cells
I'm sure there are other methods, perhaps using offset, etc. but I tried other methods and this one was pretty quick. Anyone can use this VBA formula if they do the following:
15 to 14 to do a maxifs, keep as is for minifs
change the relevant rows and columns in Cells(rows, columns) format below.
The True/False parameters passed to .Address() will lock/unlock the rows/columns respectively (i.e. add a $ in front if True).
Change the last row
First, get the last row
Dim LastRow As Long
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
Second, here is the dynamic minifs
Range("F2").Formula = "=AGGREGATE(15,7," & Range(Cells(2, 6), Cells(LastRow, 6)).Address(True, True) & "/(" & Range(Cells(2, 1), Cells(LastRow, 1)).Address(True, True) & "=" & Range(Cells(2, 1), Cells(2, 1)).Address(False, True) & "),1)"
Third, autofill down.
Range("F2").AutoFill Destination:=Range("F2:F" & LastRow)