Reputation: 2774
I'm writing a function as follows:
Function Display-ItemLocation {
Param(
[ Parameter (
Mandatory = $True,
Valuefrompipeline = $True ) ]
[ String ]$stringItem,
[ Parameter (
Mandatory = $False,
Valuefrompipeline = $True ) ]
[ String ]$stringLocation = 'unknown'
)
Echo "The location of item $stringItem is $stringLocation."
}
Display-ItemLocation 'Illudium Q-36 Explosive Space Modulator' 'Mars'
Display-ItemLocation 'Plumbus'
It works fine as written.
The location of item Illudium Q-36 Explosive Space Modulator is Mars.
The location of item Plumbus is unknown.
I'd like to be able to pre-load an array with multiple data pairs and send it via pipeline into the function.
$Data = @(
@('Bucket','Aisle 1'),
@('Spinach Pie','Freezer 4')
)
$Data | Display-ItemLocation
I can't find the magic syntax to get this to work. Can the function accept a pair of values at the same time from the pipeline?
Upvotes: 1
Views: 218
Reputation: 2774
Thanks to @mklement0 for leading me to this solution. I came up with two options to resolve my dilemma using an array.
Option 1: Use .ForEach
to pass the parameters in the usual way.
$Data = @(
@('Bucket','Aisle 1'),
@('Spinach Pie','Freezer 4')
)
$Data.ForEach({Format-ItemLocation "$($_[0])" "$($_[1])"})
Option 2: Using the pipeline (which is what I was after), I modified the function as mklement0 suggested to enable the ValuefromPipelineByPropertyName
and to include a Process { }
block.
Function Format-ItemLocation {
Param (
[ Parameter (
Mandatory = $True,
ValuefromPipelineByPropertyName = $True ) ]
[ String ]$stringItem,
[ Parameter (
Mandatory = $False,
ValuefromPipelineByPropertyName = $True ) ]
[ String ]$stringLocation = 'unknown'
)
Process {
"The location of item $stringItem is $stringLocation."
}
}
I pass the array via pipeline to a middle step to assign the parameter names to a [PSCustomObject]
. This greatly reduces the amount of text that would bulk up the code, and it's the reason I was searching for a more elegant solution.
$Data = @(
@('Bucket','Aisle 1'),
@('Spinach Pie','Freezer 4')
)
$Data |
ForEach-Object { [PSCustomObject]@{stringItem=$_[0];stringLocation=$_[1]} } |
Format-ItemLocation
I changed the function name to Format-*
as recommended.
Upvotes: 1
Reputation: 440536
Define your pipeline-binding parameters as binding by property name - ValuefromPipelineByPropertyName
- and then pipe (custom) objects that have such properties:
Function Display-ItemLocation {
Param(
[ Parameter (
Mandatory,
ValuefromPipelineByPropertyName ) ]
[ String ]$stringItem,
[ Parameter (
Mandatory = $False,
ValuefromPipelineByPropertyName ) ]
[ String ]$stringLocation = 'unknown'
)
process { # !! You need a `process` block to process *all* input objects.
Echo "The location of item $stringItem is $stringLocation."
}
}
As an aside: Display
is not an approved verb in PowerShell.
Now you can pipe to the function as follows; note that the property names must match the parameter names:
$Data = [pscustomobject] @{ stringItem = 'Bucket'; stringLocation = 'Aisle 1' },
[pscustomobject] @{ stringItem = 'Spinach Pie'; stringLocation = 'Freezer 4' }
$Data | Display-ItemLocation
The above yields:
The location of item Bucket is Aisle 1.
The location of item Spinach Pie is Freezer 4.
The above uses [pscustomobject]
instances, which are easy to construct ad hoc.
@{ stringItem = 'Bucket'; stringLocation = 'Aisle 1' }
) do not work - although changing that is being discussed in this GitHub issue.In PSv5+ you could alternatively define a custom class
:
# Define the class.
class Product {
[string] $stringItem
[string] $stringLocation
Product([object[]] $itemAndLocation) {
$this.stringItem = $itemAndLocation[0]
$this.stringLocation = $itemAndLocation[1]
}
}
# Same output as above.
[Product[]] (
('Bucket', 'Aisle 1'),
('Spinach Pie', 'Freezer 4')
) | Display-ItemLocation
Upvotes: 4