I am trying to create a hyperlink from one worksheet(Home) to another worksheet(Events details), finding the target cell by the value.
"Home":
A
[MyLink]
"Events details"
A
["hello there"]
So "MyLink" should point to "hello there" independent of the position of "hello there" in A column.
What I tried: I could create a hyper link to a specific cell with
HYPERLINK("#'Events details'!A2")
I could find a position of "hello there" using:
=MATCH("hello there", 'Events details'!$A:$A, 0)
so I tried putting it together
HYPERLINK("#'Events details'!MATCH("hello there", 'Events details'!$A:$A, 0)")
but it does not seems to work
Try this:
=HYPERLINK("#'Events details'!A"&MATCH("hello there",'Events details'!A:A,),"DisplayText")
The problem with the formula that you tried to use is that excel does not evaluate text strings (stuff between quotes). What I mean by that, can be seen below:
=HYPERLINK("#'Events details'!MATCH("hello there", 'Events details'!$A:$A, 0)")
^------------------------^ ^----------------------------^
Excel first interprets the first part above as text "#'Events details'!MATCH(", followed by a command supposedly called hello there and then another text ", 'Events details'!$A:$A, 0)" because of the way the quotes work.
But this, as you have seen will not give you anything useful.
What you will have to do is concatenate the sheetname, and the cell address (which has the column name and row number). Since you already know that the column name is A, you know that the full cell address should be something like "#'Events details'!A_" where the underscore here is meant to be some number you will retrieve using MATCH. For this first part, your HYPERLINK function becomes:
=HYPERLINK("#'Events details'!A" )
Since MATCH returns the row number, you will have to put it afterwards (notice that the whole MATCH function is outside any quotes):
=HYPERLINK("#'Events details'!A"MATCH("hello there", 'Events details'!$A:$A, 0))
But wait! Excel doesn't understand that the two 'parts' above should become one, so you have to use something to concatenate the two parts, the shorter way being by using the &:
=HYPERLINK("#'Events details'!A"&MATCH("hello there", 'Events details'!$A:$A, 0))
Or you can use the longer function:
=HYPERLINK(CONCATENATE("#'Events details'!A",MATCH("hello there", 'Events details'!$A:$A, 0)))
And of course, you can use the optional second input of the HYPERLINK function to give the link a 'friendly name' as Excel calls it.
Related
I am trying to extract the text from a topdown analysis in SAP using VBA. I basically need to move down the column, evaluate if the extracted text contains a certain character string, then extract the cell adjacent to it.
First: Does anyone know how to change this line of code (the script for that specific cell) so that I can extract its text to a variable rather than just select it?
Second: Does anyone know how to make this line variable using i as the variable?
session.findById("wnd[0]/usr/cntlCONTAINER/shellcont/shell/shellcont[1]/shell[1]").selectItem " 5", "C 35"
In similar cases, I proceeded as follows:
myRow = right(space(10) & cstr(i) , 11)
myText = session.findById("wnd[0]/usr/cntlCONTAINER/shellcont/shell/shellcont[1]/shell[1]").getItemText(myRow ,"C 35")
Regards,
ScriptMan
In Microsoft Excel file, I have a text in rows that appears like this:
1. Rc8 {[%emt 0:00:05]} Rxc8 {[%emt 0:00:01]} 2. Rxc8 {[%emt 0:00:01]} Qxc8 {} 3. Qe7# 1-0
I need to remove any text appearing within the flower brackets { and }, including the brackets themselves.
In the above example, there are three instances of such flower brackets. But some rows might have more than that.
I tried =MID(LEFT(A2,FIND("}",A2)-1),FIND("{",A2)+1,LEN(A2))
This outputs to: {[%emt 0:00:05]}. As you see this is the very first instance of text between those flower brackets.
And if we use this to within SUBSTITUTE like this: =SUBSTITUTE(A2,MID(LEFT(A2,FIND("}",A2)),FIND("{",A2),LEN(A2)),"")
I get an output like this:
1. Rc8 Rxc8 {[%emt 0:00:01]} 2. Rxc8 {[%emt 0:00:01]} Qxc8 {} 3. Qe7# 1-0
If you have noticed, only one instance is removed. How do I make it work for all instances? thanks.
Highlight everything
Go to replace
enter {*} in text to replace
leave replace with blank
This should replace all flower brackets and anything in between them
It is not that easy without VBA, but there is still a way.
Either (as suggested by yu_ominae) just use a formula like this and auto-fill it:
=IFERROR(SUBSTITUTE(A2,MID(LEFT(A2,FIND("}",A2)),FIND("{",A2),LEN(A2)),""),A2)
Another way would be iterative calculations (go to options -> formulas -> check the "enable iterative calculations" button)
To do it now in one cell, you need 1 helper-cell (for my example we will use C1) and the use a formula like this in B2 and auto-fill down:
=IF($C$1,A2,IFERROR(SUBSTITUTE(B2,MID(LEFT(B2,FIND("}",B2)),FIND("{",B2),LEN(B2)),""),B2))
Put "1" in C1 and all formulas in B:B will show the values of A:A. Now go to C1 and hit the del-key several times (you will see the "{}"-parts disappearing) till all looks like you want it.
EDIT: To do it via VBA but without regex you can simply put this into a module:
Public Function DELBRC(ByVal str As String) As String
While InStr(str, "{") > 0 And InStr(str, "}") > InStr(str, "{")
str = Left(str, InStr(str, "{") - 1) & Mid(str, InStr(str, "}") + 1)
Wend
DELBRC = Trim(str)
End Function
and then in the worksheet directly use:
=DELBRC(A2)
If you still have any questions, just ask ;)
Try a user defined function. In VBA create a reference to "Microsoft VBScript Regular Expressions 5.5. Then add this code in a module.
Function RemoveTags(ByVal Value As String) As String
Dim rx As New RegExp
rx.Global = True
rx.Pattern = " ?{.*?}"
RemoveTags = Trim(rx.Replace(Value, ""))
End Function
On the worksheet in the cell enter: =RemoveTags(A1) or whatever the address is where you want to remove text.
If you want to test it in VBA:
Sub test()
Dim a As String
a = "Rc8 {[%emt 0:00:05]} Rxc8 {[%emt 0:00:01]}"
Debug.Print RemoveTags(a)
End Sub
Outputs "Rc8 Rxc8"
In Excel 2007, how do I add a description and parameter hints to a user-defined function? When I start typing a function invocation for a built-in function, Excel shows a description and parameter list--a tooltip. I'd like to do the same for the functions I define.
Not just for the formula insert wizard, but in the formula box, so if I key "=myFun(", at the "(" the tooltip pops up just like it does for "=average("
There's no help in VBA Help, none on MSDN and none on any of the Excel and VBA dedicated forums I can find, so this is clearly a long shot.
Not a tooltip solution but an adequate workaround:
Start typing the UDF =MyUDF( then press CTRL + Shift + A and your function parameters will be displayed. So long as those parameters have meaningful names you at-least have a viable prompt
For example, this:
=MyUDF( + CTRL + Shift + A
Turns into this:
=MyUDF(sPath, sFileName)
Professional Excel Development by
Stephen Bullen describes how to
register UDFs, which allows a
description to appear in the Function
Arguments dialog:
Function IFERROR(ByRef ToEvaluate As Variant, ByRef Default As Variant) As Variant
If IsError(ToEvaluate) Then
IFERROR = Default
Else
IFERROR = ToEvaluate
End If
End Function
Sub RegisterUDF()
Dim s As String
s = "Provides a shortcut replacement for the common worksheet construct" & vbLf _
& "IF(ISERROR(<expression>), <default>, <expression>)"
Application.MacroOptions macro:="IFERROR", Description:=s, Category:=9
End Sub
Sub UnregisterUDF()
Application.MacroOptions Macro:="IFERROR", Description:=Empty, Category:=Empty
End Sub
From: http://www.ozgrid.com/forum/showthread.php?t=78123&page=1
To show the Function Arguments dialog, type the function name and press CtrlA. Alternatively, click the "fx" symbol in the formula bar:
I know you've accepted an answer for this, but there's now a solution that lets you get an intellisense style completion box pop up like for the other excel functions, via an Excel-DNA add in, or by registering an intellisense server inside your own add in. See here.
Now, i prefer the C# way of doing it - it's much simpler, as inside Excel-DNA, any class that implements IExcelAddin is picked up by the addin framework and has AutoOpen() and AutoClose() run when you open/close the add in. So you just need this:
namespace MyNameSpace {
public class Intellisense : IExcelAddIn {
public void AutoClose() {
}
public void AutoOpen() {
IntelliSenseServer.Register();
}
}
}
and then (and this is just taken from the github page), you just need to use the ExcelDNA annotations on your functions:
[ExcelFunction(Description = "A useful test function that adds two numbers, and returns the sum.")]
public static double AddThem(
[ExcelArgument(Name = "Augend", Description = "is the first number, to which will be added")]
double v1,
[ExcelArgument(Name = "Addend", Description = "is the second number that will be added")]
double v2)
{
return v1 + v2;
}
which are annotated using the ExcelDNA annotations, the intellisense server will pick up the argument names and descriptions.
There are examples for using it with just VBA too, but i'm not too into my VBA, so i don't use those parts.
Also you can use, this Macro to assign Descriptions to arguments and the UDF:
Private Sub RegisterMyFunction()
Application.MacroOptions _
Macro:="SampleFunction", _ '' Your UDF name
Description:="calculates a result based on provided inputs", _
Category:="My UDF Category", _ '' Or use numbers, a list in the link below
ArgumentDescriptions:=Array( _ '' One by each argument
"is the first argument. tell the user what it does", _
"is the second argument. tell the user what it does")
End Sub
Credits to Kendall and the original post here.
For the UDF Categories
I just create a "help" version of the function. Shows up right below the function in autocomplete - the user can select it instead in an adjacent cell for instructions.
Public Function Foo(param1 as range, param2 as string) As String
Foo = "Hello world"
End Function
Public Function Foo_Help() as String
Foo_Help = "The Foo function was designed to return the Foo value for a specified range a cells given a specified constant." & CHR(10) & "Parameters:" & CHR(10)
& " param1 as Range : Specifies the range of cells the Foo function should operate on." & CHR(10)
&" param2 as String : Specifies the constant the function should use to calculate Foo"
&" contact the Foo master at master#foo.com for more information."
END FUNCTION
The carriage returns improve readability with wordwrap on. 2 birds with one stone, now the function has some documentation.
#will's method is the best. Just add few lines about the details for the people didn't use ExcelDNA before like me.
Download Excel-DNA IntelliSense from https://github.com/Excel-DNA/IntelliSense/releases
There are two version, one is for 64, check your Excel version. For my case, I'm using 64 version.
Open Excel/Developer/Add-Ins/Browse and select ExcelDna.IntelliSense64.xll.
Insert a new sheet, change name to "IntelliSense", add function description, as https://github.com/Excel-DNA/IntelliSense/wiki/Getting-Started
Then enjoy! :)
Unfortunately there is no way to add Tooltips for UDF Arguments.
To extend Remou's reply you can find a fuller but more complex approach to descriptions for the Function Wizard at
http://www.jkp-ads.com/Articles/RegisterUDF00.asp
I tried #ScottK's approach, first as a side feature of my functional UDF, then as a standalone _Help suffix version when I ran into trouble (see below). In hindsight, the latter approach is better anyway--more obvious to a user attentive enough to see a tool tip, and it doesn't clutter up the functional code.
I figured if an inattentive user just typed the function name and closed the parentheses while he thought it over, help would appear and he would be on his way. But dumping a bunch of text into a single cell that I cannot format didn't seem like a good idea. Instead, When the function is entered in a cell with no arguments i.e.
= interpolateLinear()
or
= interpolateLinear_Help()
a msgBox opens with the help text. A msgBox is limited to ~1000 characters, maybe it's 1024. But that's enough (barely 8^/) for my overly tricked out interpolation function. If it's not, you can always open a user form and go to town.
The first time the message box opened, it looked like success. But there are a couple of problems. First of course, the user has to know to enter the function with no arguments (+1 for the _Help suffix UDF).
The big problem is, the msgBox reopens several times in succession, spontaneously while working in unrelated parts of the workbook. Needless to say, it's very annoying. Sometimes it goes on until I get a circular reference warning. Go figure. If a UDF could change the cell formula, I would have done that to shut it up.
I don't know why Excel feels the need recalculate the formula over and over; neither the _Help standalone, nor the full up version (in help mode) has precedents or dependents. There's not an application.volatile statement anywhere. Of course the function returns a value to the calling cell. Maybe that triggers the recalc? But that's what UDFs do. I don't think you can not return a value.
Since you can't modify a worksheet formula from a UDF, I tried to return a specific string --a value --to the calling cell (the only one you can change the value of from a UDF), figuring I would inspect the cell value using application.caller on the next cycle, spot my string, and know not to re-display the help message. Seemed like a good idea at the time--didn't work. Maybe I did something stupid in my sleep-deprived state. I still like the idea. I'll update this when (if) I fix the problem. My quick fix was to add a line on the help box: "Seek help only in an emergency. Delete the offending formula to end the misery.
In the meantime, I tried the Application.MacroOptions approach. Pretty easy, and it looks professional. Just one problem to work out. I'll post a separate answer on that approach later.
A lot of dancing around the answer. You can add the UDF context help, but you have to export the Module and edit the contents in a text editor, then re-import it to VBA. Here's the example from Chip Pearson: Adding Code Attributes
I need help in writing a macro for LibreOffice Calc 3.6.2.2
What I'm trying to do is pass a cell number to function, the function then analyzes the cell's contents (a text string) and return a value based on its content.
My current code:
Function mColor2(mCellAdd)
Dim l(5) as String 'declare list of variables
l(0)="red"
l(1)="blue"
l(2)="yellow"
l(3)="green"
for i=LBound(l) To UBound(l) 'cycle from start to end of list
If InStr(mCellAdd,l(i))<>0 Then
mColor2=l(i)
Else
mColor2="not known"
End If
Next
End Function
But I get only "not known" returned.
I think it's because I don't handle values returned from InStr() properly.
Actually I'm not sure I'm using the right function since I only need to check if the cell's content includes my substring or not...
A screenshot of the results:
Let's say the input string (assuming it is passed as a string?) is "my blue dog". Then your function will run through the loop 5 times (should that be 4?). First it looks for "red", which isn't found, so mColor2 is set to "not known". Then it looks for "blue", which is found, so mColor2 is set to "blue". Then it looks for "yellow", and mColor2 is set back to "not known". Same for "green", and then I don't know what it will look for when i is 4.
In any case, you don't want to reset mColor2 to "not known", just get rid of your Else.
Have you decided what to do if the input string contains more than one of the strings you're looking for? Do you want to exit as soon as you find "red", or continue and remember the last one found?
i want to create the "cases" formula for excel to simulate Select case behavior (with multiple arguments and else optional).
If A1 and A2 are excel cells, this is the goal:
A1 Case: A2 Formula: A2 Result
5 cases({A1>5,"greather than 5"}, {A1<5, "less than 5"},{else,"equal to 5"}) equal to 5
Hi cases({A1="","there is nothing"},{else,A1}) Hi
1024 cases({5<A1<=10,10},{11<=A1<100,100},{A1>100,1000}) 1000
12 cases({A1=1 to 9, "digit"}, {A1=11|22|33|44|55|66|77|88|99, "11 multiple"}) (empty)
60 cases({A1=1 to 49|51 to 99,"not 50"}) not 50
If it could, It must accept excel formulas or vba code, to make an operation over the cell before take a case, i.g.
cases({len(A1)<7, "too short"},{else,"good length"})
If it could, it must accept to or more cells to evaluate, i.g.
if A2=A3=A4=A5=1 and A1=2, A6="one", A7="two"
cases(A1!=A2|A3|A4|A5, A6}, {else,A7}) will produce "two"
By the way, | means or, != means different
Any help?
I'm grateful.
What I could write was this:
Public Function arr(ParamArray args()) 'Your function, thanks
arr = args
End Function
Public Function cases(arg, arg2) 'I don't know how to do it better
With Application.WorksheetFunction
cases = .Choose(.Match(True, arg, 0), arg2)
End With
End Function
I call the function in this way
=cases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))
And i can't get the goal, it just works for the first condition, A1>5.
I fixed it using a for, but i think it's not elegant like your suggestion:
Function selectCases(cases, actions)
For i = 1 To UBound(cases)
If cases(i) = True Then
selectCases = actions(i)
Exit Function
End If
Next
End Function
When i call the function:
=selectCases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))
It works.
Thanks for all.
After work a little, finally i get a excel select case, closer what i want at first.
Function cases(ParamArray casesList())
'Check all arguments in list by pairs (case, action),
'case is 2n element
'action is 2n+1 element
'if 2n element is not a test or case, then it's like the "otherwise action"
For i = 0 To UBound(casesList) Step 2
'if case checks
If casesList(i) = True Then
'then take action
cases = casesList(i + 1)
Exit Function
ElseIf casesList(i) <> False Then
'when the element is not a case (a boolean value),
'then take the element.
'It works like else sentence
cases = casesList(i)
Exit Function
End If
Next
End Function
When A1=5 and I call:
=cases(A1>5, "gt 5",A1<5, "lt 5","eq 5")
It can be read in this way: When A1 greater than 5, then choose "gt 5", but when A1 less than 5, then choose "lt 5", otherwise choose "eq 5". After run it, It matches with "eq 5"
Thank you, it was exciting and truly educative!
O.K., there's no way at all to do exactly what you want. You can't use anything other than Excel syntax within a formula, so stuff like 'A1 = 1 to 9' is just impossible.
You could write a pretty elaborate VBA routine that took strings or something and parsed them, but that really amounts to designing and implementing a complete little language. And your "code" wouldn't play well with Excel. For example, if you called something like
=cases("{A1="""",""there is nothing""},{else,A1}")
(note the escaped quotes), Excel wouldn't update your A1 reference when it moved or the formula got copied. So let's discard the whole "syntax" option.
However, it turns out you can get much of the behavior I think you actually want with regular Excel formulas plus one tiny VBA UDF. First the UDF:
Public Function arr(ParamArray args())
arr = args
End Function
This lets us create an array from a set of arguments. Since the arguments can be expressions instead of just constants, we can call it from a formula like this:
=arr(A1=42, A1=99)
and get back an array of boolean values.
With that small UDF, you can now use regular formulas to "select cases". They would look like this:
=CHOOSE(MATCH(TRUE, arr(A1>5, A1<5, A1=5), 0), "gt 5", "lt 5", "eq 5")
What's going on is that 'arr' returns a boolean array, 'MATCH' finds the position of the first TRUE, and 'CHOOSE' returns the corresponding "case".
You can emulate an "else" clause by wrapping the whole thing in 'IFERROR':
=IFERROR(CHOOSE(MATCH(TRUE, arr(A1>5, A1<5), 0), "gt 5", "lt 5"), "eq 5")
If that is too verbose for you, you can always write another VBA UDF that would bring the MATCH, CHOOSE, etc. inside, and call it like this:
=cases(arr(A1>5, A1<5, A1=5), "gt 5", "lt 5", "eq 5")
That's not far off from your proposed syntax, and much, much simpler.
EDIT:
I see you've already come up with a (good) solution that is closer to what you really want, but I thought I'd add this anyway, since my statement above about bringing MATCH, CHOOSE, etc. inside the UDF made it look easier thatn it really is.
So, here is a 'cases' UDF:
Public Function cases(caseCondResults, ParamArray caseValues())
On Error GoTo EH
Dim resOfMatch
resOfMatch = Application.Match(True, caseCondResults, 0)
If IsError(resOfMatch) Then
cases = resOfMatch
Else
Call assign(cases, caseValues(LBound(caseValues) + resOfMatch - 1))
End If
Exit Function
EH:
cases = CVErr(xlValue)
End Function
It uses a little helper routine, 'assign':
Public Sub assign(ByRef lhs, rhs)
If IsObject(rhs) Then
Set lhs = rhs
Else
lhs = rhs
End If
End Sub
The 'assign' routine just makes it easier to deal with the fact that users can call UDFs with either values or range references. Since we want our 'cases' UDF to work like Excel's 'CHOOSE', we'd like to return back references when necessary.
Basically, within the new 'cases' UDF, we do the "choose" part ourselves by indexing into the param array of case values. I slapped an error handler on there so basic stuff like a mismatch between case condition results and case values will result in a return value of #VALUE!. You would probably add more checks in a real function, like making sure the condition results were booleans, etc.
I'm glad you reached an even better solution for yourself, though! This has been interesting.
MORE ABOUT 'assign':
In response to your comment, here is more about why that is part of my answer. VBA uses a different syntax for assigning an object to a variable than it does for assigning a plain value. Look at the VBA help or see this stackoverflow question and others like it: What does the keyword Set actually do in VBA?
This matters because, when you call a VBA function from an Excel formula, the parameters can be objects of type Range, in addition to numbers, strings, booleans, errors, and arrays. (See Can an Excel VBA UDF called from the worksheet ever be passed an instance of any Excel VBA object model class other than 'Range'?)
Range references are what you describe using Excel syntax like A1:Q42. When you pass one to an Excel UDF as a parameter, it shows up as a Range object. If you want to return a Range object from the UDF, you have to do it explicitly with the VBA 'Set' keyword. If you don't use 'Set', Excel will instead take the value contained within the Range and return that. Most of the time this doesn't matter, but sometimes you want the actual range, like when you've got a named formula that must evaluate to a range because it's used as the source for a validation list.