Is it possible get a variable name?
function SortList($list, $order){
$tempList=New-Object Collections.Generic.List[string]
for($i=0;$i -lt $order.count;$i++){
$tempList.Add($list[$order[$i]])
}
$list=$tempList
}
This function sort a list of things based in other order. What i want to achieve is modify a global variable passed as $list.
I can't write '$global:list = $tempList' because that is going to create the variable "list" as global, and the line that i use instead create an internal $list that is lost when the function end, i.e. the global $list remains equal.
I don't know how can I modify that var if I don't know his name. The function its supposed to be used with differents globals lists.
December 10th, 2011 8:43pm
function SortList($order){
$tempList=New-Object Collections.Generic.List[string]
for($i=0;$i -lt $order.count;$i++){
$tempList.Add($global:list[$order[$i]])
}
$global:list=$tempList
}
New-Variable -Scope global -Name list
$global:list = (1,2,3)
SortList (2,1,0)
$global:list
#Remove-Variable -Name list
Notice I have left out the local parameter $list, and am using purely the global variable.
December 10th, 2011 9:17pm
As I say, I want to work with different lists not with only one.
function SortList($list, $order){
if($list.count -ne $order.count){"The order has to have the same length: $($list.count)"; return}
$tempList=New-Object Collections.Generic.List[string]
for($i=0;$i -lt $order.count;$i++){
$tempList.Add($list[$order[$i]])
}
for($i=0;$i -lt $order.count;$i++){
$List[$i] = $tempList[$i]
}
}
I found that this would work but I don't like it, i have to assign all the element.
December 11th, 2011 12:25am
I don't believe it is possible (nor would it be desirable) for a function to be able to determine the name of the variable making up one of the arguments it was passed. What would your script do if it found out that the argument was not actually a global
variable, but a script level variable, a literal constant, the content of a file courtesy of get-childitem, or an element from a hash table?
I don't see that your function is returning any value to the calling program (other than an error message in some cases). Why not return the value you want to pass back to one of the arguments as the return value? Something like this:
function SortList($list, $order){
$tempList=New-Object Collections.Generic.List[string]
for($i=0;$i -lt $order.count;$i++){
$tempList.Add($list[$order[$i]])
}
$tempList
}
Then call it like this:
$mylist = sortlist $mylist $order
December 11th, 2011 2:04am
Or, l alternately, use the temporary variable differently:
function SortList($list, $order){
if($list.count -ne $order.count){"The order has to have the same length: $($list.count)"; return}
$tempList=$list
$list=New-Object Collections.Generic.List[string]
for($i=0;$i -lt $order.count;$i++){
$List.Add($templist[$order[$i]])
}
}
December 11th, 2011 2:11am
Exactly because of that
$mylist = sortlist $mylist $order
is longer than
sortlist $mylist $order
I just ask this question because I was having the hope that maybe some kind of oscure and undocumented reflection mecanism exist to retrieve at any given time the name of a variable. If it don't exist well I move on and use the second script that I
show (that I don't like because of the 2n execution time)
December 11th, 2011 2:21am
granted calling the function in the manner I suggested uses a few more chanracters. But the number of additional characters you'd need to add to the function to determine the name of the variable, if such a thing were possible would probably be greater.
In addition, the complexity of this additional code would be much greater as well. In any case, relying on "obscure, undocumented" features seems more work than it is worth.
As to your 2n issue, I believe that neither of my suggested examples would exceed 1n.
December 11th, 2011 3:14am
One way would be to pass the argument as a scriptblock. The literal name of the varible will be available as the string value of the script block. The value of the variable will be available as the result of the invocation of the script block:
function test ($list){
"variable name of argument pass was $list"
"value of argument pass was $(&$list)"
}
$global:list1 = 1,2,3
test {$list1}
variable name of argument pass was $list1
value of argument pass was 1 2 3
December 11th, 2011 4:10am
Another option is to use the $$ automatic variable. For this to work, you'd need to modify your function so that the global variable is the last argument passed:
function test ($order,$list){
"variable name passed was $$"
}
$global:global_list = "abc"
test 123 $global_list
variable name passed was global_list
December 11th, 2011 4:32am
My PowerShell V2 seems to have a bug in it that makes that technique not
work:
C:> test 123 $global_list
variable name passed was global_list
C:> test 123 $global_list
variable name passed was global_list
C:> test 123 $global_list
variable name passed was global_list
C:> test $global_list 123
variable name passed was global_list
C:> test 456 789
variable name passed was 123
December 11th, 2011 5:16am
Hmm. Same here. Seems like it should work, but it seems buggy.
December 11th, 2011 5:32am
One way would be to pass the argument as a scriptblock. The literal name of the varible will be available as the string value of the script block. The value of the variable will be available as the result of the invocation of the script block:
function test ($list){
"variable name of argument pass was $list"
"value of argument pass was $(&$list)"
}
$global:list1 = 1,2,3
test {$list1}
variable name of argument pass was $list1
value of argument pass was 1 2 3
December 11th, 2011 5:36am
Leteo X....I don't remember well his name and why it delete his own post :S, but well, it shows how $MyInvocation automatic variable could be used. The function using this variable:
function SortListX($list, $order){
if($list.count -ne $order.count){"The order has to have the same length: $($list.count)"; return}
$tempList=New-Object Collections.Generic.List[string]
for($i=0;$i -lt $order.count;$i++){
$tempList.Add($list[$order[$i]])
}
$name = ($MyInvocation.Line -split "\s+" | select -Index 1).remove(0,1)
Set-Variable $name -Value $tempList -Scope Global -Force
}
The invocation is clean, for example: "SortListX $MyGlobalList (2,1,0)" and the execution time match the solution of passing scriptblock as Mjollnor says. To the guy that mention the use of $MyInvocation: thanks and don't delete your useful post!
I check the used time with this:
1..10|% {$tot=New-TimeSpan} {$tot+=Measure-Command {SortList2N $WorkList (2,1,0)}}
$tot
1..10|% {$tot=New-TimeSpan} {$tot+=Measure-Command {SortListSB {$WorkList} (2,1,0)}}
$tot
1..10|% {$tot=New-TimeSpan} {$tot+=Measure-Command {SortListMI $WorkList (2,1,0)}}
$tot
Still the 2N solution runs faster but I'm going to accept the rests as proper answers because they provide alternative ways to get the variable argument name.
December 11th, 2011 6:58am
Is it just me, or does it seem to anyone else that all this convoluted extra work (and an apparently buggy feature) makes the complexity of replacing this statement:
sortlist $mylist $order
with this one that Voodoomsr said was too long:
$mylist = sortlist $mylist $order
a bit less of an issue?
Is there a universe somewhere in which the use of "some kind of oscure and undocumented reflection mecanism" is considered better programming practice than simplicity, to say nothing of the use of global variables where they are not needed?
December 14th, 2011 9:12am
Is it just me, or does it seem to anyone else that all this convoluted extra work (and an apparently buggy feature) makes the complexity of replacing this statement:
sortlist $mylist $order
with this one that Voodoomsr said was too long:
$mylist = sortlist $mylist $order
a bit less of an issue?
Is there a universe somewhere in which the use of "some kind of oscure and undocumented reflection mecanism" is considered better programming practice than simplicity, to say nothing of the use of global variables where they are not needed?
My sentiments exactly. There is IMO sometimes a certain feeling of superiority that comes with creating a piece of obfusticated code that works, but is virtually impossible to understand.
This is in contrast to my own view, which is that
code should be as simple and clear as possible. This is just a basic Best Practice, for reasons of extensibility and maintainability (esp. for others who didn't write the code in the first place).
As for global variables, I never use them. I was trained in classical Pascal, and keeping variables as local as possible is another Best Practice. If you are using global variables, it's likely you are writing spagetti code, not structured code.
Once again, not good for maintainability. (My teacher didn't even allow program-scope variables - everything had to be passed by reference if that's what you needed to do.)
December 14th, 2011 9:38am
I love obscure undocumented methods! :D
but, only for working in the console, which I do more than I write
scripts...
scripts however, whats an extra kb or two? is typing it out slowing the
loading of the script? no... is it slowing down the processing of the
script? very unlikely...
December 14th, 2011 4:39pm
This is admittedly probably a bad application of the principle, but it does serve as a platform to explore the different mechanisms available to expose both the literal name of a variable (or any other component of the code) and it's evaluated value.
This can be more useful in some applications than the one presented for consideration in this thread.
December 14th, 2011 4:45pm
This is admittedly probably a bad application of the principle, but it does serve as a platform to explore the different mechanisms available to expose both the literal name of a variable (or any other component of the code) and it's evaluated value.
This can be more useful in some applications than the one presented for consideration in th
December 14th, 2011 5:01pm
Dealing with script code as both code and data.
December 14th, 2011 5:05pm
About "Best practices":
Teddy, personally I think that doing everything based only in "Best practices" is not really the "Best". If we don't experiment and break that autoimposed box the space to explore and find new ways of doing things is going to be seriously shrunken.
In the process of finding and uncovering those dark things they stop being like that and instead the become familiar to anyone.
About the importance of the call lenght:
The length of how we call and access a functionality, for me at least, is very important, I see it from the point of view of the user that want 'a function to sort a any global list based in a given order'. In this case the user don't need to
know how it works, he only need to be sure that the function do what supposedly do.
Back on the proposed "solutions":
Mjolinor the scriptblock parameter is a nice way to get that variable name, I'm going to mark it as the answer. The use of $MyInvocation.Line is also very useful, but it needs a little more parsing. The final call that the user must do is consice
in both cases:
sortlist {$AnyGlobalList} $OrderArray # scriptblock parameter
sortlist $AnyGlobalList $OrderArray # $MyInvocation
December 14th, 2011 6:27pm
Best Practices are best, the reason they are there is because someone who
created that black box (more on your second point in a bit) has said 'this
is the best way to use my black box'
that being said, im not saying you shouldnt try new things, but you shouldnt
deploy new things. do you want to buy software thats full of shortcuts? no,
I want to know that they created clean and structured code, so there are
less patches and updates I need to worry about...
you are right, the end user doesnt care about the actual inner workings,
but when you leave your job and someone has to come in and they are asked to
modify your script, well, if its all cryptic they'll likely just toss it out
and start over wasting more time...
you state that a small amount of code is important to you, but not why....
I like short code too provided its efficient... there is no point in making
it short if it doesnt increase the over all resource usage... which, in
your case, a couple more letters isnt going to impact much...
also in your case you are limiting the user, you are FORCING them to update
the list they provided, perhaps they want a new list that is sorted but
wants to keep the current order as well? now they have to do extra work,
$oldlist = $AnGlobalList
sortlist $AnyGlobalList $OrderArray
you arent really giving a strong case for hacking away at things...
December 14th, 2011 6:40pm
JRich you are assuming a great amount of stuff.
The requisite is: 'a function to sort a any global list based in a given order', The behaviour must be like [array]::Reverse, i.e. it must modify the argument and don't return anything. This is fixed. If we analize the infinite ways that the user
could want to do this were going to discuss for an equal infinite time, that is not a good thing.
You say that I should not "deploy new things", I could not be more in disagrement with that statement but arguing about it is completelly out of topic.
You are asumming cryptic code everywhere, deployments, installations, etc, etc. I say where is the cryptic code, are we in the topic about that extreme little function, when the real problematic was "modify any global variable passed as argument"?
December 14th, 2011 7:16pm
true, let me toss another option at you
function test{
param($name)
$temp = (Get-Variable -Scope global -Name $name).value
write-host "Variable to be modified: $name"
write-host "Current Value: $temp"
Set-Variable -Scope global -Name $name -Value "CHANGED!"
$temp = (Get-Variable -Scope global -Name $name).value
write-host "New Value: $temp"
}
$global:myvar = "TESTING!"
test myvar
December 14th, 2011 7:34pm
Justin, what point are you trying to make with that code sample?
December 14th, 2011 7:51pm
rather than pass the variable and try to get its name, just pass the name of
the variable and then pull that variable in...
December 14th, 2011 8:10pm
you sir...are a genius! greatest signature ever
August 21st, 2013 3:53pm