I have tried searching for the answer for the past couple hours but cannot seem to find what I am looking for - mind you, I am really new to VBA and macros.
What I am trying to do is to add one to what my formula is referencing to every time the macro is run. For example:
Currently the formula is =100*((Q$179/Q$167)-1)
When run, I would like it to be =100*((Q$180/Q$168)-1)
I have to do this for multiple formulas. Is there an easy way to do this with a macro?
Thank you so much in advance
Personally I'd use RegEx to achieve this. The following will increment every cells row by 1 in your formula. This should also work on columns other then Q as well
Option Explicit
Sub IncrementFormula()
Dim tmpFormula As String
Dim RegEx As Object
Dim Match, SubMatch
tmpFormula = "=100*((Q$179/Q$167)-1)"
Set RegEx = CreateObject("vbscript.regexp")
With RegEx
.Global = True
.Pattern = "(?:[A-Z]{1,})(?:\$|)([0-9]{1,})"
If .test(tmpFormula) Then
For Each Match In .Execute(tmpFormula)
For Each SubMatch In Match.submatches
tmpFormula = Replace(tmpFormula, Match, Replace(Match, SubMatch, vbNullString) & CLng(SubMatch) + 1)
Next SubMatch
Next Match
End If
End With
MsgBox tmpFormula
End Sub
Using your formula above it will output =100*((Q$180/Q$168)-1)
If you want it to persist when the workbook is closed, you'll need to store your number somewhere in a worksheet. Say it's in Cell(1,1) of Sheets(2), you could use
dim incVal as integer
incVal = cint(Sheets(2).Cells(1,1))+1
Sheets(2).Cells(1,1) = incVal
dim formula as string
formula = "=100*((Q$" & incVal & "/Q$" & (incVal-12) & ")-1)"
Then just set the desired Cell's formula to that formula. Hope this helps.
A different approach is to use OFFSET in your formula
Assuming the formula is in Q185
then:
=100*((Q$180/Q$168)-1)
Becomes:
=100*((OFFSET(Q185,-5,0)/OFFSET(Q185,-17,0)-1))
As you insert rows at the bottom of the table (and above the formulas), the formula and the cell it refers to will also move down
Related
My file has two identical Worksheets for users to input two different sets of assumption variables, called "InputA" and "InputB". I want to quickly switch which Input sheet is feeding into the other sheets of the model.
Using Find and Replace took over 5 minutes, and there were over 350,000 references to "InputA".
I tried the following macro, which takes an instant to run, but unfortunately also changes all references in the workbook, effectively keeping everything referenced to the original input sheet.
Sheets("InputA").Name = "temp"
Sheets("InputB").Name = "InputA"
Sheets("temp").Name = "InputB"
Is there a way to execute the macro but prevent any references to worksheets from changing to the new sheet name, basically freezing everything except the sheet name change? Or perhaps any other solution that will work quickly? I don't want to go through 350,000 instances and rewrite using INDIRECT(), as that is the only other solution I've seen, because my references are complex and nested and that will take an age.
Thanks.
Assuming that your 2 Input-Sheets have the same structure, I would suggest the following:
Create a named range on Workbook-level, name it for example InputData. This range should contain all data from InputA.
Create a helper-sheet and name it Input - you can later set it to hidden.
Mark the range in the new sheet that is exactly the size of the Input-Data-Range and enter the formula =InputData as Array-formula. You can do so by entering Ctrl+Shift+Enter. The formula should have curly brackets and the sheet should now display the data of InputA.
Change all you formulas to refer to the helper Sheet Input instead of InputA.
Create a macro:
Sub switchInput()
Const sheetAName = "InputA"
Const sheetBName = "InputB"
With ThisWorkbook.Names("inputData")
If InStr(.RefersTo, sheetAName) > 0 Then
.RefersTo = Replace(.RefersTo, sheetAName, sheetBName)
Else
.RefersTo = Replace(.RefersTo, sheetBName, sheetAName)
End If
End With
End Sub
This routine will just change the definition of the named range to point either to the first or second input sheet. As a consequence, the helper sheet will show the data of the selected Input-Sheet. All your formulas itself stays unchanged.
As stated in the comments, you could take the approach recommended by Damian and use a conditional in all relevant cells. The generic conditional would be
=IF(A1="InputA",FORMULA INPUTA,FORMULA INPUTB)
This formula makes A1 the cell that decides which input to pull. This will make changing the around 350.000 output formulas in your workbook the bottleneck, the following macro takes care of changing all the formulas to conatin the conditional:
Sub changeFormulas()
Dim rng As Range, cll As Range
Set rng = shOutput.Range("A2:C10") 'Your relevant worksheet and range here
Dim aStr As String, bStr As String, formulaStr As String
aStr = "InputA"
bStr = "InputB"
For Each cll In rng
If cll.HasFormula And InStr(1, cll.Formula, aStr, 1) Then
formulaStr = Right(cll.Formula, Len(cll.Formula) - 1)
cll.Formula = "=IF(A1=" & Chr(34) & aStr & Chr(34) & "," & formulaStr & "," & Replace(formulaStr, aStr, bStr) & ")" 'Change A1 to the reference most suited for your case
End If
Next cll
End Sub
This might take a bit of time, since it has to access all the relevant cells one by one, but it will only have to run once.
To explain: This macro will go through all the cells in your range rng specified at the top. If a cell has a formula in it and the formula contains "InputA" it will change that formula to fit the generic conditional (with the cells own formula of course). Chr(34) is the quotation mark ", I find using Chr(34) more readable than multiple quotation marks """, but that is just preference.
I have a formula in A1 that is fed with data from a different workbook. It's only a reference to this other workbook, there aren't any calculations.
This second workbook is updated on a monthly basis so the cell I'm interested in referring to is offset one cell to the right each month.
How can I write a macro that tells my current formula in A1 to use the same formula but moving it one place to the right? It'd be something like: [Book1]Sheet1!C15 to [Book1]Sheet1!D15. Thanks!
Use Range.Precedents to get the cells a particular Range depends on.
'get the cell:
Dim theCell As Range
Set theCell = ActiveSheet.Range("A1")
'get its first "precedent" Range:
Dim precedent As Range
Set precedent = theCell.Precedents(1)
'rewrite the formula, offsetting the precedent by 1 column:
theCell.Formula = "=" & precedent.Offset(ColumnOffset:=1).Address(External:=True)
Obviously this makes a lot of assumptions and will need to be adjusted to your specific needs, but you don't need to parse any formulas to offset its precedent cells when you're looking at a formula that's simply =SomeCellAddress.
First put this small UDF in a standard module:
Public Function NextCellOver(s As String) As String
arr = Split(s, "!")
addy = Range(arr(1)).Offset(0, 1).Address
NextCellOver = arr(0) & "!" & addy
End Function
It will accept a string that ends with something like !Z99 and return a string ending with !AA99. (Basically one column to the right.)
Then enter:
Sub marine()
With Range("A1")
.Formula = NextCellOver(.Formula)
End With
Application.CalculateFullRebuild
End Sub
To apply this to the cell in question.
I would like to swap selected cell ranges within the same column without having automatically adjusted attached formulas in other columns. Those cell ranges will almost always be of unequal size.
I found a VBA code which does it for two selected cells, but im afraid that this wont help me much.
Sub SwapCells()
Dim sHolder As String
If Selection.Cells.Count = 2 Then
With Selection
sHolder = .Cells(1).Formula
If .Areas.Count = 2 Then ' Cells selected using Ctrl key
.Areas(1).Formula = .Areas(2).Formula
.Areas(2).Formula = sHolder
Else ' Adjacent cells are selected
.Cells(1).Formula = .Cells(2).Formula
.Cells(2).Formula = sHolder
End If
End With
Else
MsgBox "Select only TWO cells to swap", vbCritical
End If
End Sub
I know that another option would be to hold 'shift' when moving the cell ranges (works perfectly fine), but then all the attached formulas will change their reference which I dont want (e.g. if I have a formula referring to cell A1, and im swapping A1 somewhere, the formula will refer to A1's new position, but I want the formula to still refer to A1).
I think another option would be to use INDIRECT("G" & ROW()) to fix it, but since its a quite resource-intensive formula, Id love to see an alternative.
On top of that, the latter two options would not allow me to use tables (which Id prefer for other reasons) because you cant swap cells in tables. This is why Id strongly prefer a VBA option.
I hope you can help me, thank you! Maybe it is only necessary to adjust the VBA code a little.
Kind regards,
Marco
EDIT: If it is significantly easier to swap two equal cell ranges (e.g. encompassing 5 cells each), then it would also be a good solution.
Sub SwapTwoSelectedRanges()
Dim initialRng As Range
Set initialRng = Selection
If initialRng.Areas.Count <> 2 Then
Debug.Print "Select 2 areas!"
Exit Sub
End If
If initialRng.Areas(1).Cells.Count <> initialRng.Areas(2).Cells.Count Then
Debug.Print "The cells should be the same number!"
Exit Sub
End If
Dim intermediateRng As Variant
intermediateRng = initialRng.Areas(1).Cells.Value2
initialRng.Areas(1).Cells.Value2 = initialRng.Areas(2).Cells.Value2
initialRng.Areas(2).Cells.Value2 = intermediateRng
End Sub
Swaping two values is considered an easy task, if you are using an intermediate value. With the ranges, there are two important checks to perform, before swapping them:
Are the selected areas exactly 2;
Is the number of cells equal in every area;
Then with an intermediateRng as a 3. variable, the swap is made;
This would only work, if the Areas are per column. If the selection is made per row, then the results would not be as expected;
Concerning the swaping of the colors, if the colors of all the cells per area are exactly the same, this would work:
Dim intermediateRng As Variant
Dim intermediateClr As Variant
intermediateRng = initialRng.Areas(1).Cells.Value2
intermediateClr = initialRng.Areas(1).Cells.Interior.Color
With initialRng
.Areas(1).Cells.Value2 = .Areas(2).Cells.Value2
.Areas(1).Cells.Interior.Color = .Areas(2).Cells.Interior.Color
.Areas(2).Cells.Value2 = intermediateRng
.Areas(2).Cells.Interior.Color = intermediateClr
End With
However, if the colors of the cells per Area are not the same, then the easiest way is to copy+paste the first range to a separate range and work from there.
First time posting for me and hoping to get some help with VBA for selective hardcoding.
I currently have a column into which a formula is set which returns either blank or a variety of text strings (the status of our company's orders).
I need to make a macro that looks into all the cells of that column and copy/pastes as value into that same cell only if the formula in that cell returns text string "Received". It should not affect the other cells where the formula is returning either blank or a different text string.
Would really appreciate your help. Please let me know if you need more info.
Thanks in advance,
Olivier
Put the following in the VBA project of your workbook:
Option Compare Text
Sub replaceThem()
Dim r As Range
Dim c
Set r = Range("B1:B3") ' use the actual range here
For Each c In r
If c.Value = "Received" Then c.Formula = "Received"
Next
End Sub
This will do what you asked. c.Value returns the value of the formula in the cell c, c.Formula replaces the formula. The Option Compare Text makes the comparison case-insensitive.
I'm surprised I can't find this anywhere which makes me think I must be going about this incorrectly. I want to be able to include a series of values within sumifs so it performs like a loop for each value in the conditional (without having to write a " +sumifs(....) for each value. Here's an example of what I have so far that is not working
`=SUMIFS(Sum,Range1,Criteria1, '[Stores.xlsx]Sheet1'!$H:$H, "Store #"&Regions!$T:$T&"*")`
So I'm trying to pass every value in Regions!T:T as a criteria.
For example "Store #150 Los Angeles" and "Store #155 San Diego" would both need to be passed through the argument. Currently the formula just returns the first item it matches and doesn't continue to the next item.
I hope that makes sense. Please ask if you need more clarity.
I think the easiest is to start an "intermediate" column next to the T column, do a sumifs for each of the rows of that column, and then sum that column into another cell. Tables or even just array sums may also be helpful here.
I came up with the following in VBA, but I cannot test it completely:
Option Explicit
Function SumSumIfs(ByVal inp As Range) As Integer
Dim i As Integer
Dim QBData As Worksheet
Dim Stores As Worksheet
Set QBData = Workbooks.Open("QBData.xlsx").Sheets("Sheet1")
Set Stores = Workbooks.Open("Stores.xlsx").Sheets("Sheet1")
Dim QBRange1, QBRange2, SalesRange As Range
Set QBRange1 = QBData.Range("H1:H" & Range("H1").End(xlDown).Row)
Set QBRange2 = QBData.Range("I1:I" & Range("I1").End(xlDown).Row)
Set SalesRange = QBData.Range("H1:H" & QBData.Range("H1").End(xlDown).Row)
For i = 1 To inp.End(xlDown).Row
SumSumIfs = SumSumIfs + Application.WorksheetFunction.SumIfs(QBRange1, QBRange2, _
"=" & Stores.Cells(16, 5), StoreRange3, "=" & inp.Cells(i, 19))
Next i
End Function
Again, I'm certain there's a way to do this looping with the formula, but searching around, it was not evident to me.