2

I seem often to find myself in a situation where I have a bunch of variables set up and I want to pass them in to a function (for example, passing parameters to another function without modification). Currently I end up doing something like this:

Get-RelatedThing -CompanyTag $CompanyTag -ProjectTag $ProjectTag -EnvName $EnvName

This seems verbose and not hugely readable. I could use positional parameters, but that sacrifices clarity and compatibility. Precreating an array and splatting just creates vertical clutter rather than horizontal (except in the unusual case where I can do something like Get-RelatedThing @PSBoundParameters, but this has pitfalls).

What I'd really like is to be able to do something like:

Get-RelatedThing @(Get-Variable CompanyTag,ProjectTag,EnvName)

but Get-Variable returns an Object[] and the splatting operator can only parse a hashtable of named params (or an array of positional params). I stole a function to solve this problem:

function Get-VariablesAsHashtable {

[CmdletBinding()]
param
(
    [Parameter(Position=0)]
    [string[]] $Name
)

$ObjectArray = Get-Variable -Name @($Name)

$Hashtable = @{}
$ObjectArray | Foreach { $Hashtable[$_.Name] = $_.Value }

return $Hashtable

}

which works when run "in advance":

# works
$calculatedVars = (Get-VariablesAsHashtable ProjectTag,EnvName,ComponentTag);
(Get-RelatedThing @calculatedVars);

but NOT when run "inline":

# fails
(Get-RelatedThing @(Get-VariablesAsHashtable ProjectTag,EnvName,ComponentTag));

Get-InitialThing : Cannot process argument transformation on parameter 'ProjectTag'. Cannot convert value to type System.String.

Am I missing something that would fix this approach? Or is there a better way of doing this?

Community
  • 1
  • 1
Dave Gregory
  • 932
  • 7
  • 12

2 Answers2

1

See if this doesn't work:

Get-RelatedThing @($(Get-Variable CompanyTag,ProjectTag,EnvName))

And you can actually simplify that some more with a different function. This shows the effect of splatting a hash table:

$Params = @{
CompanyTag = 'Company1'
ProjectTag = 'Project1'
EnvName    = 'Env1'
}

[Scriptblock]::Create("$(&{$args}@Params)")

-EnvName: Env1 -CompanyTag: Company1 -ProjectTag: Project1

Modifying your original function to reproduce that:

function Get-VariablesAsParameters {

[CmdletBinding()]
param
(
    [Parameter(Position=0)]
    [string[]] $Name
)

$ObjectArray = Get-Variable -Name @($Name)
[string]($ObjectArray | Foreach { '-{0}: {1}' -f $_.Name,$_.Value})

}

function Get-RelatedThing {$args}

Get-RelatedThing $(Get-VariablesAsParameters CompanyTag,ProjectTag,EnvName)
-CompanyTag: Company1 -ProjectTag: Project1 -EnvName: Env1
BenMorel
  • 34,448
  • 50
  • 182
  • 322
mjolinor
  • 66,130
  • 7
  • 114
  • 135
  • `Get-RelatedThing @($(Get-Variable CompanyTag,ProjectTag,EnvName))` doesn't work any differently from `Get-RelatedThing @(Get-VariablesAsHashtable ProjectTag,EnvName,ComponentTag)` for me... – Dave Gregory Jan 03 '14 at 10:55
  • Get-Variable returns an object type of PSVariable. The function is expecting strings. If you want to pass just the value of the variable using get-variable, you have to use get-variable | select -expandproperty value, or (get-variable ).value – mjolinor Jan 03 '14 at 12:06
1

Actually, you are wrong in assumption that splatting works only for hashtables. It works perfectly fine with arrays too, but it uses each element of an array as positional parameter to called command. If you are sure that all values are there in the first place, you can simply splat the array:

function Test-Binding {
param (
    $Foo,
    $Bar
)
    "Foo: $Foo"
    "Bar: $Bar"
}

$Foo = 'first value'
$Bar = 'other value'

$Splat = @(Get-Variable -ValueOnly Foo, Bar)

Test-Binding @Splat

The inline syntax you've tried fails for very simple reason: you are NOT splatting in this case. What you really do in this line:

(Get-RelatedThing @(Get-VariablesAsHashtable ProjectTag,EnvName,ComponentTag));

... is passing single argument, that you force to be an array (that what @() stands for in this case), array of single hashtable (one that your function produces). There is no way to splat hashtable you are building "ad hoc". You need to build it first, store in variable x, and than pass this variable as your "splat" using @x literal, instead of usual $x.

I've heard suggestion to fill this gap (with syntax like : command @@{ foo = 'bar'}) but until than, you are forced to build your hashtable/ array first, and splat it using variable name and @ prefix.

Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148
BartekB
  • 8,492
  • 32
  • 33