Modify variable using a sub/function - vba

I am trying to add a + sign in front of certain variables if they are positive.
ex:
Sub mySub()
Dim cash As Variant
End Sub
It works well if I do:
Dim plus As String
plus = "+"
If cash > 0 Then
cash = plus & cash
Else
cash= cash
End If
But I was looking for a sub or function that would take all my variables and add a + sign in front of them if they are positive.
sub NewSub(i As Variant)
If i > 0 Then
i = plus & i
Else
i = i
End If
End sub
But it doesn't seem to work as it doesn't show me anything (I then display my variables in a cell in excel). And a function doesn't work either.
Any ideas on how to create a sub/function to do that? Can I loop through my variables in any way ?

First off, start using Option Explicit which forces you to explicitly declare every variable and will catch mismatch errors in the VBA editor and not at runtime.
Next, if you are going to change a numerical variable to a string by prefacing a 'plus' sign onto the left end then the original variable will have to be a variant type. If you want to pass a parameter into a sub procedure and have the sub change it then the parameter must be ByRef.
Alternately, you could push the variable into a function and return the new value.
Option Explicit
Sub main()
Dim cash As Variant, cash2 As Variant
cash = 10
Debug.Print cash '10 (as number)
AddPlus cash
Debug.Print cash '+10 (as string)
cash = 10
Debug.Print cash '10 (as number)
cash = udfAddPlus(cash)
Debug.Print cash '+10 (as string)
End Sub
Sub AddPlus(ByRef i As Variant)
If i > 0 Then
i = "+" & i
Else
i = i
End If
End Sub
Function udfAddPlus(i As Variant)
If i > 0 Then
udfAddPlus = "+" & i
Else
udfAddPlus = i
End If
End Function
The Debug.Print command sends output to the VBE's Immediate window.

Related

Compiling error in returning a value

I have a problem:
Public Sub ChangeRow(Column As String, Value As String, id As Integer)
For i = 4 To 15
For Each rw In Worksheets(i).Rows
If Worksheets(i).Range("A" & rw.row).Value = id Then
Dim row As Integer
**row = getRow(id, i)**
MsgBox (row)
If Worksheets(i).Range(Column & rw.row).Value <> Value Then
Worksheets(i).Range(Column & rw.row) = Value
End If
Exit For
End If
Next rw
Next i
End Sub
Function getRow(id As Integer, Sheet As Integer) As Integer
For Each rw In Worksheets(Sheet).Rows
If Worksheets(Sheet).Range("A" & rw.row).Value = id Then
getRow = rw.row
End If
Next rw
End Function
Change Row Works fine... its just when I add 'row = getRow(id, i)' to the mix it throws a ByRef mismatch error??
This is a great example as to why using Option Explicit is a great practice.
Add Option Explicit to the very top of your worksheet module, outside your macro.
This forces you to declare all variables used in your subs, and also can help catch typos in your variable names.
This would catch that i is not declared. What's therefore happening is VBA/Excel by default will set i to be Variant.
Then, when you hit row = getRow(id, i), you're basically passing row = getRow([integer], [Variant]). But that sub is expecting getRow([integer],[integer])...hence your Type Mismatch error.
So, as mentioned, just do Dim i as Integer at the top of the ChangeRow sub.
(or, for long run VBA use Long instead of Integer).
In the function, you need to declare "Sheet" as Worksheet object
Function getRow(id As Integer, Sheet As Worksheet) As Integer

Find and Replace for Named Ranges

For some reason the Replace Function I have in this Macro doesn't if i change the value of the for YTD and YTG to ",9," and ",12," the Macro won't work. Here is what I have.
Sub Rename()
Dim YTG As String
Dim YTD As String
Dim Shiftold As String
Dim Shitnew As String
i = 1
YTG = "<>YTG"
YTD = "YTD"
Shiftold = "0,0,1"
ShiftNew = "0,12,1"
For Each Name In ActiveWorkbook.Names
If InStr(1, Names(i).RefersTo, YTG) > 0 Then
Names(i).RefersTo = Replace(Names(i).RefersTo, YTG, YTD)
End If
If InStr(1, Names(i).RefersTo, Shiftold) > 0 Then
Names(i).RefersTo = Replace(Names(i).RefersTo, Shiftold, ShiftNew)
End If
i = i + 1
Next Name
End Sub
Any thoughts on why the macro doesn't work anymore?
I strongly recommend using Option Explicit to force variable declarations, which will catch typos and (most) missing declarations.
Rename is a terrible choice of macro name (like Name is a bad choice of variable name) because it overlaps with common methods etc. and in this case gives little idea of what is actually going to be affected.
As observed in comments you weren't actually using the For Each loop variable. This makes it fairly pointless , but actually would be a much better way of accessing elements of the collection.
Below is my suggested improvements; notice that the comments are talking about how I've changed it, so you should actually add some comments describing real purpose rather than using these.
Sub UpdateNamedRanges() ' specific macro title
Dim YTG As String
Dim YTD As String
Dim ShiftOld As String
Dim ShiftNew As String ' corrected
Dim AName As Name 'declared
YTG = "<>YTG"
YTD = "YTD"
ShiftOld = "0,0,1"
ShiftNew = "0,12,1"
For Each AName In ActiveWorkbook.Names
' use loop variable
If InStr(1, AName.RefersTo, YTG) > 0 Then
AName.RefersTo = Replace(AName.RefersTo, YTG, YTD)
End If
If InStr(1, AName.RefersTo, ShiftOld) > 0 Then
AName.RefersTo = Replace(AName.RefersTo, ShiftOld, ShiftNew)
End If
Next AName
End Sub

how to call function to modify a variable vba

What my code does is it takes a value froma nother workbook and copies and paste it in another workbook. What I want now however is a something that would remove the last 3 figures to have a final number in thousand dollars instead of dollars.
It would work fine if I were to build my function around my "DSA.Worksheet.Value..." but it would take time because I have quite a few of those.
When I call my function on the DSA.worksheet cell it doesnt work, even if I call it like that:
Call number_right (DSA.Worksheets(1).Range("G10").Value = Application.WorksheetFunction.Round (hello.Worksheets("getthis"").Range("I72").Value, -3)
Sub test()
DSA.Worksheets(1).Range("G10").Value = Application.WorksheetFunction.Round(hello.Worksheets("getthis").Range("I72").Value, -3)
DSA.Worksheets(1).Range("G10").Value = Call number_right(DSA.Worksheets(1).Range("G10").Value)
End Sub
Function number_right(n As Variant) As Variant
If Len(n) > 3 Then n = Left((n), Len(n) - 3)
End Function
Maybe this:
Sub test()
Dim DSA As Workbook
Dim rngValue As Range
Set DSA = ActiveWorkbook 'May need to change this line if DSA is not already open
Set rngValue = DSA.Worksheets(1).Range("G10")
Set rngValue = Application.WorksheetFunction.Round(hello.Worksheets("getthis").Range("I72").Value, -3)
If Len(rngValue) > 3 Then
Set rngValue = number_right(rngValue)
End If
End Sub
Function number_right(n As Range) As Variant
number_right = Left(n, Len(n) - 3)
End Function

Why is assigning the Value property of cell causing code to end aburptly?

Private Sub FillRow(programCell As Range, storedProgramCell As Range)
Dim counter As Integer
For counter = 3 To 9
Dim cellOffset As Integer
cellOffset = counter - 3
Dim currentStoredCell As Range
Set currentStoredCell = storedProgramCell.Offset(0, cellOffset)
Dim value As String
value = currentStoredCell.value
Dim currentTargetCell As Range
Set currentTargetCell = programCell.Offset(0, cellOffset)
MsgBox currentStoredCell.value 'Works correctly, prints correct value
currentTargetCell.value = value
Next counter
End Sub
The line:
currentTargetCell.value = value
causes the code to stop executing, with no error.
I added the expression to my watch list, then stepped through the routine. The expression was seen as a Boolean:
This makes me think the expression is being viewed as a comparison, and the program abruptly ends since the returned Boolean is not being stored or used anywhere. I wouldn't doubt if I were wrong though.
I'm new to VBA, struggling to debug my program, so please forgive me if this is a petty mistake. I couldn't find any sources online that explains this problem.
Replace your subroutine with following code:
Private Sub FillRow(Dst As Range, Src As Range)
Dim x As Integer
Dim v As Variant
Dim Srcx As Range
Dim Dstx As Range
Debug.Print "FillRow"
Debug.Print Src.Address
Debug.Print Dst.Address
Debug.Print "Loop"
For x = 0 To 6
Debug.Print x
Set Srcx = Src.Offset(0, x)
Debug.Print Srcx.Address
v = Srcx.Value
Debug.Print TypeName(v)
Set Dstx = Dst.Offset(0, x)
Debug.Print Dstx.Address
Dstx.Value = v
Next
Debug.Print "Completed"
End Sub
Run and post in your question Immediate window output.
Value is a reserved word, even if vba does not raise an error on this name, you should not use it. Name it something else. Also, try setting it as a variant.

inputBox Excel VBA Integer problems

I am new to VBA and I'm trying to create a macro that from a inputBox accepts a number between 0 and 1000 and converts it to hexadecimal. Well it works, but I am struggling to keep the program accepting that range ( 0 - 1000). This is what happens:
If I input -1 it throws a error;
If I input -1001 it throws a FFFFFFFC17;
If I input any value above 1000 it doesn't throw a MsgBox (I am not familiar with causing error on excel for now).
I've done first like this:
Sub DecToHex()
Dim inputDec As Integer
Dim outputHex As String
inputDec = InputBox("Decimal?")
If inputDec <= 1000 And inputDec >= 0 Then
outputHex = Application.WorksheetFunction.Dec2Hex(inputDec)
MsgBox ("Hex: " + outputHex)
Else
MsgBox ("Error! Please define decimal. It must be larger than zero and less than 1001")
inputDec = InputBox("Decimal?")
outputHex = Application.WorksheetFunction.Dec2Hex(inputDec)
MsgBox ("Hex: " + outputHex)
End If
End Sub
But then I thought well inputBox gives me input as string, so maybe I should accept values as string, so I changed:
Dim inputDec As Integer
'Changed to
Dim inputDec As String
Which still did a poorly control on variables ( ie. it accepts -1200, as also 1200 ). So can you point out what am I doing wrong? Maybe it's the Worksheet Function I'm not reading well. I know it's newbie mistake but it's important for me to understand how to control these input variables from inputBox.
You need to declare the inputDec As Variant
You need to Handle the Cancel Button
You need to put the code in a loop so that when user enters an invalid number, the inputbox can pop up again.
You need to use Application.InputBox with Type:=1 so that only numbers can be accepted.
Try this
Sub DecToHex()
Dim inputDec As Variant
Dim outputHex As String
Do
inputDec = Application.InputBox("Decimal?", Type:=1)
'~~> Handle Cancel
If inputDec = "False" Then Exit Do
If inputDec <= 1000 And inputDec >= 0 Then
outputHex = Application.WorksheetFunction.Dec2Hex(inputDec)
MsgBox ("Hex: " + outputHex)
Exit Do '<~~ Exit the loop
Else
MsgBox ("Error! Please define decimal. It must be larger than zero and less than 1001")
End If
Loop
End Sub