Looping a Cell Reference in a match function - vba

I'm pretty new to VBA and was wondering why my formula doesnt work?
I'm trying to loop the cells Sheets("Summary").Cells(11 + X, 13) in my match function but it doesn't seem to work. Am I doing something wrong?
Sub Reset()
Dim X As Integer
For X = 0 To 19
Sheets("Summary").Cells(11 + X, 13).Select
Selection.Formula = "=INDEX(YMAX!$A:$W,MATCH(Summary!$J$4&"" ""&Summary!$J$5&"" ""&11,YMAX!$B:$B,0),MATCH(sheets("Summary").cells(11 + x,9),YMAX!$1:$1,0))"
Next X
End Sub

It looks like you have the following errors
&'s are in the wrong places (should be outside of the quotes when concatenating text, inside the quotes when concatenating cell references)
Variables (i.e. your reference to a cell on the "Summary" sheet) do
not need to be in quotes when building a string in VBA
(not an error, per say) You don't need to do .Select as you can
set the formula of the cell directly.
Update your code with the following
Sub Reset()
Dim X As Integer
For X = 0 To 19
' Broken up on several lines for clarity
Sheets("Summary").Cells(11 + X, 13).Formula =
"=INDEX(YMAX!$A:$W,MATCH(Summary!$J$4" & " " & _
"Summary!$J$5" & " " & _
"11,YMAX!$B:$B,0),MATCH(" & _
Sheets("Summary").Cells(11 + X,9).Address & _
",YMAX!$1:$1,0))"
Next X
End Sub
I'm not sure if that's the formula you want exactly, but it's what i interpreted given your current code. Let me know if that works for you.

Related

Provide the dynamic range of values to Sub VBA Excel

I am trying to pass the line column value to the sub that will call a function to calculate some logic.
Before writing a sub I just defined a function and passed values manually to the function and dragging to the columns I needed.
But now I wanted to create something that will auto apply formula to a range of the column.
This is the code I am trying to do, maybe its not the best way but open for suggestions.
Function addDiscount(Qty, Price)
'
' addDiscount Macro
' adds Discount to given price and quantity
'
' Keyboard Shortcut: Ctrl+h
If (Qty >= 10 Or Price >= 200) Then
addDiscount = 30 * 0.01
Else
addDiscount = 0
End If
addDiscount = Application.Round(addDiscount, 2)
End Function
Sub insertAddDiscount()
Sheets("Sheet1").Select
Range("F9:F30").Select
Dim i As Integer
For i = 9 To 30
Selection.Formula = "=addDIscount($G$i,$E$i)"
Selection.Columns.AutoFit
Next i
End Sub
Since you are using a variable i inside the formula, you need to take it outside the ".
replace your line:
Selection.Formula = "=addDIscount($G$i,$E$i)"
with:
Selection.Formula = "=addDIscount($G" & i & ",$E" & i & ")"
However, let me suggest a solution, where you don't need to rely on Selecting the Range, and later on use Selection, but rely on fully qualified Range object (this will also shorten your code's run-time).
Sub insertAddDiscount()
Dim i As Long
With Sheets("Sheet1")
For i = 9 To 30
.Range("F" & i).Formula = "=addDIscount($G" & i & ",$E" & i & ")"
Next i
.Range("F9:F30").Columns.AutoFit
End With
End Sub

Convert sub to function to use it as a formula

The below code is working fine with me. I need your help and support to make it a function so I can for example write in any cell
=adj() or =adj(A1) and the formula will apply,
Sub adj()
Dim i, j As Integer
Sheet1.Select
With Sheet1
j = Range(ActiveCell.Offset(0, -2), ActiveCell.Offset(0, -2)).Value
For i = 1 To j
ActiveCell.Formula = "=" & Range(ActiveCell.Offset(0, -1), ActiveCell.Offset(0, -1)) & i & "))" & "&char(10)"
Next i
End With
End Sub
It's hard for me to definitively understand what you're trying to do here. I think you're trying to concatenate x number of cells with a separator field.
So, I would do the following changes... obviously you can change accordingly.
Declare the Inputs as variants. If you don't and get a type mismatch the function wont call in debug. This also gives you the opportunity to deal with the Inputs.
Put in an error handler to prevent unwanted debug w.r.t. a failure.
You can't use Evaluate that way. I think you're trying to get an Evaluation of cells like A1 etc.
The function has to be called from a Cell on a sheet since it uses Application.Caller. You shouldn't really need this for the function to work, but I put in in there in case you are calling via F9 calculation.
You can also put in the line if you want the calculation to occur every time you recalculate. However this should be used with caution since you can get some unwanted calculation side effects with some spreadsheets when using this.
Application.Volatile
Public Function Adj(ByVal x As Variant, ByVal y As Variant) As String
On Error GoTo ErrHandler
Dim sSeparator As String, sCol As String
Dim i As Integer
'Get the column reference.
sCol = Split(Columns(y).Address(False, False), ":")(1)
'Activate the sheet.
Application.Caller.Parent.Select
sSeparator = Chr(10)
For i = 1 To x
Adj = Adj & Evaluate(sCol & i) & sSeparator
Next
'Remove the last seperator...
Adj = Left(Adj, Len(Adj) - 1)
Exit Function
ErrHandler:
'Maybe do something with the error return value message here..
'Although this is a string, Excel will implicitly convert to an error.
Adj = "#VALUE!"
End Function
If you wanted to pass change to a formula, and pass in the range, you would use something like:
Public Function Func(Byval MyRange as range) as variant
In this case, you're not specifying a return value so it will be ignored.
Public Function Func(Byval MyRange as range) as variant
Dim i, j As Integer
With MyRange.parent
j = .Range(MyRange.Offset(0, -2), MyRange.Offset(0, -2)).Value
For i = 1 To j
MyRange.Formula = "=" & .Range(MyRange.Offset(0, -1), MyRange.Offset(0, -1)) & i & "))" & "&char(10)"
Next i
End With
End Sub
It would be something like that..

Calculate standard deviation of same text values in same column

I am trying to write a macro in Excel to calculate the standard deviation of same text in column A taking the values from column B and giving the results in column C:
I did it manually by putting the equation=STDEV.S(A2;A3;A4;A16)for "aaa". But I need to do this automatically because I am doing another calculation and procedures which are completing by macros. Here is my code:
Option Explicit
Sub Main()
CollectArray "A", "D"
DoSum "D", "E", "A", "B"
End Sub
' collect array from a specific column and print it to a new one without duplicates
' params:
' fromColumn - this is the column you need to remove duplicates from
' toColumn - this will reprint the array without the duplicates
Sub CollectArray(fromColumn As String, toColumn As String)
ReDim arr(0) As String
Dim i As Long
For i = 1 To Range(fromColumn & Rows.Count).End(xlUp).Row
arr(UBound(arr)) = Range(fromColumn & i)
ReDim Preserve arr(UBound(arr) + 1)
Next i
ReDim Preserve arr(UBound(arr) - 1)
RemoveDuplicate arr
Range(toColumn & "1:" & toColumn & Range(toColumn & Rows.Count).End(xlUp).Row).ClearContents
For i = LBound(arr) To UBound(arr)
Range(toColumn & i + 1) = arr(i)
Next i
End Sub
' sums up values from one column against the other column
' params:
' fromColumn - this is the column with string to match against
' toColumn - this is where the SUM will be printed to
' originalColumn - this is the original column including duplicate
' valueColumn - this is the column with the values to sum
Private Sub DoSum(fromColumn As String, toColumn As String, originalColumn As String, valueColumn As String)
Range(toColumn & "1:" & toColumn & Range(toColumn & Rows.Count).End(xlUp).Row).ClearContents
Dim i As Long
For i = 1 To Range(fromColumn & Rows.Count).End(xlUp).Row
Range(toColumn & i) = WorksheetFunction.SumIf(Range(originalColumn & ":" & originalColumn), Range(fromColumn & i), Range(valueColumn & ":" & valueColumn))
Next i
End Sub
Private Sub RemoveDuplicate(ByRef StringArray() As String)
Dim lowBound$, UpBound&, A&, B&, cur&, tempArray() As String
If (Not StringArray) = True Then Exit Sub
lowBound = LBound(StringArray): UpBound = UBound(StringArray)
ReDim tempArray(lowBound To UpBound)
cur = lowBound: tempArray(cur) = StringArray(lowBound)
For A = lowBound + 1 To UpBound
For B = lowBound To cur
If LenB(tempArray(B)) = LenB(StringArray(A)) Then
If InStrB(1, StringArray(A), tempArray(B), vbBinaryCompare) = 1 Then Exit For
End If
Next B
If B > cur Then cur = B
tempArray(cur) = StringArray(A)
Next A
ReDim Preserve tempArray(lowBound To cur): StringArray = tempArray
End Sub
It would be nice if someone could please give me an idea or solution. The above code is for calculating the summation of same text values. Is there any way to modify my code to calculate the standard deviation?
I went in a different direction and provided a pseudo-STDEV.S.IF to be used much like the COUNTIF or AVERAGEIF function.
Function STDEV_S_IF(rAs As Range, rA As Range, rBs As Range)
Dim a As Long, sFRM As String
sFRM = "STDEV.s("
Set rBs = rBs(1).Resize(rAs.Rows.Count, 1)
For a = 1 To rAs.Rows.Count
If rAs(a).Value2 = rA.Value2 Then
sFRM = sFRM & rBs(a).Value2 & Chr(44)
End If
Next a
sFRM = Left(sFRM, Len(sFRM) - 1) & Chr(41)
STDEV_S_IF = Application.Evaluate(sFRM)
End Function
Syntax: STDEV_S_IF(<criteria range>, <criteria>, <stdev.s values>)
In your sample, the formula in C2 would be,
=STDEV_S_IF(A$2:A$20, A2, B$2:B$20)
Fill down as necessary.
    
Here is a formula and VBA route that gives you the STDEV.S for each set of items.
Picture shows the various ranges and results. My input is the same as yours, but I accidentally sorted it at one point so they don't line up.
Some notes
ARRAY is the actual answer you want. NON-ARRAY showing for later.
I included the PivotTable to test the accuracy of the method.
VBA is the same answer as ARRAY calculated as a UDF which could be used elsewhere in your VBA.
Formula in cell D3 is an array formula entered with CTRL+SHIFT+ENTER. That same formula is in E3 without the array entry. Both have been copied down to the end of the data.
=STDEV.S(IF(B3=$B$3:$B$21,$C$3:$C$21))
Since it seems you need a VBA version of this, you can use the same formula in VBA and just wrap it in Application.Evaluate. This is pretty much how #Jeeped gets an answer, converting the range to values which meet the criteria.
VBA Code uses Evaluate to process a formula string built from the ranges given as input.
Public Function STDEV_S_IF(rng_criteria As Range, rng_criterion As Range, rng_values As Range) As Variant
Dim str_frm As String
'formula to reproduce
'=STDEV.S(IF(B3=$B$3:$B$21,$C$3:$C$21))
str_frm = "STDEV.S(IF(" & _
rng_criterion.Address & "=" & _
rng_criteria.Address & "," & _
rng_values.Address & "))"
'if you have more than one sheet, be sure it evalutes in the right context
'or add the sheet name to the references above
'single sheet works fine with just Application.Evaluate
'STDEV_S_IF = Application.Evaluate(str_frm)
STDEV_S_IF = Sheets("Sheet2").Evaluate(str_frm)
End Function
The formula in F3 is the VBA UDF of the same formula as above, it is entered as a normal formula (although entering as an array does not affect anything) and is copied down to the end.
=STDEV_S_IF($B$3:$B$21,B3,$C$3:$C$21)
It is worth noting that .Evaluate processes this correctly as an array formula. You can compare this against the NON-ARRAY column included in the output. I am not certain how Excel knows to treat it this way. There was previously a fairly extended conversion about how Evaluate process array formulas and determines the output. This is tangentially related to that conversation.
And for completeness, here is the test of the Sub side of things. I am running this code in a module with a sheet other than Sheet2 active. This emphasizes the ability of using Sheets("Sheets2").Evaluate for a multi-sheet workbook since my Range call is technically misqualified. Console output is included.
Sub test()
Debug.Print STDEV_S_IF(Range("B3:B21"), Range("B3"), Range("C3:C21"))
'correctly returns 206.301357242263
End Sub

VBA CountA / countif with addition

hi Guys just quick question. I am trying to do a Counta / Countif formula in VBA excel. The code I'm working with is the following.
Sub To_open
Dim x as Range
x =COUNTA('Tier 2'!C2:C1000)+COUNTA('Tier 3'!C2:C1000)+COUNTA('Tier 4'!C2:C1000)+COUNTA('Tier 5'!C2:C1000)
If x = 0 then
msgbox " No impact "
End If
End sub
The code is suppose to count and add the rows in coloumn C with values, in the the 4 sheets and if it equals zero then show the message box.
One way to do this is using the Evaluate function:
Sub To_open()
Dim n As Long
n = Evaluate("COUNTA('Tier 2'!C2:C1000) + " & _
"COUNTA('Tier 3'!C2:C1000) + " & _
"COUNTA('Tier 4'!C2:C1000) + " & _
"COUNTA('Tier 5'!C2:C1000)")
MsgBox n
End Sub
Try
x =Application.WorksheetFunction.COUNTA('Tier 2'!C2:C1000)+COUNTA('Tier 3'!C2:C1000)+COUNTA('Tier 4'!C2:C1000)+COUNTA('Tier 5'!C2:C1000))

VBA for Excel - building .Formula with contents containing quotation marks

I'm coding a project in VBA for Excel, which loops to a file, matches the 'code' for each of the quantities, and then feeds all the matches for that code to a user defined function, which goes onto the Excel sheet.
I can read the info, sort it so postdata(nr_of_datafield, nr_of_item) returns me the row in the source sheet on which the value is listed. Based on this, I need to create (through .Formula) a syntax like this:
=formul(raming!J104) (if there's only one occurence)
=formul(raming!J104;"+";raming!J108) (etc., always adding the same extra if there's multiple occurences)
=formul(raming!J104;"+";raming!J108;"+";raming!312;"+";raming!J403) etcetera, with always needing to get the previous values from what's already in Cells.Formula.
Based on this check:
Code:
Right(Workbooks(meetstaatfile).Sheets("HOR_raming").Cells(lusteller12, 9 + CInt(postdata(3, eerstepositie)) * 3).Formula, 2) = "()"
I can detect if there is already any contents added here. If not (meaning the check for () ending is positive), I replace with this:
Code:
Workbooks(meetstaatfile).Sheets("HOR_raming").Cells(lusteller12, 9 + postdata(3, eerstepositie) * 3).Formula = "=formul('raming'!J" & postdata(2, eerstepositie) & ")"
To create a formula that looks like: =formul(raming!J104)
(the '104' in this example is the output from postdata(2,eerstepositie)
However, if it doesn't trigger for the () ending, there already is a value, and I need to extend the formula to something like this: =formul(raming!J104;"+";raming!J108)
I've been trying to figure out how to do this by replacing ')' with the block I want added, but I cannot get it to work to input the quotation marks. ('formul' is very similar to concatenating text).
How can I make a variation of the codeline above that lets me alter the cell input? Either by a Replace() like I was trying, or reading what's between the formul() brackets and rebuilding the formula?
If you need to have quotation marks as content within a string literal in VBA, you have to double them. See: http://msdn.microsoft.com/en-us/library/ms234766.aspx
.Formula = "=formul('raming'!J" & 104 & ",""+""," & "'raming'!J" & 108 & ")"
Or with your postdata:
.Formula = "=formul('raming'!J" & postdata(2, eerstepositie) & ",""+""," & "'raming'!J" & postdata(2, whatevergets108) & ")"
I don't know, whether I have understood it right, but if you need to concatenate the formula in dependence of the contents of an array, then this can be achieved like so:
Sub test()
'one occurrence
postdata = [{0;104}]
sFormulaString = getFormulaString(postdata, 2)
MsgBox sFormulaString
'two occurrences
postdata = [{0,0;104,108}]
sFormulaString = getFormulaString(postdata, 2)
MsgBox sFormulaString
'three occurrences
postdata = [{0,0,0;104,108,312}]
sFormulaString = getFormulaString(postdata, 2)
MsgBox sFormulaString
End Sub
Function getFormulaString(postdata As Variant, nr_of_datafield As Long) As String
sFormula = "=formul("
For i = LBound(postdata, 2) To UBound(postdata, 2)
sFormula = sFormula & "'raming'!J" & postdata(nr_of_datafield, i) & ",""+"","
Next
sFormula = Left(sFormula, Len(sFormula) - 5) & ")"
getFormulaString = sFormula
End Function
Hm, or is the real need, to append new formula parts into an existing formula? If so, the following code will append a new part into the Formula in A1 every time it runs.
Sub test2()
postdata = [{0;104}]
sFormula = Range("A1").Formula
If sFormula = "" Then sFormula = "=formul("
If Right(sFormula, 1) = ")" Then sFormula = Left(sFormula, Len(sFormula) - 1) & ",""+"","
sFormula = sFormula & "'raming'!J" & postdata(2, 1) & ")"
Range("A1").Formula = sFormula
End Sub
Greetings
Axel