How to write a PowerShell advanced function that can work with both piped in objects and objects get from parameter value?


I’m writing a function Chunk-Object that can chunk an array of objects into sub arrays. For example, if I pass it an array @(1, 2, 3, 4, 5) and specify 2 elements per chunk, then it will return 3 arrays @(1, 2), @(3, 4) and @(5). Also the user can provide an optional scriptblock parameter if they want to process each elements before chunk them into sub arrays. Now my code is:

The result is:

As you can see, it works with piped in objects, but does not work with values get from parameter. How to modify the code to make it work in both cases?


The difference is that when you pipe the array into Chunk-Object, the function executes the process block once for each element in the array passed as a sequence of pipeline objects, whereas when you pass the array as an argument to the -InputObject parameter, the process block executes once for the entire array, which is assigned as a whole to $InputObject.

So, let’s take a look at your pipeline version of the command:

The reason this one works is that for each iteration of the pipeline, $_ is set to the value of the current array element in the pipeline, which is also assigned to the $InputObject variable (as a single-element array, due to the [object[]] typecast. The foreach loop is actually extraneous in this case, because the $InputObject array always has a single element for each invocation of the process block. You could actually remove the loop and change $current_element = $o to $current_element = $InputObject, and you’d get the exact same results.

Now, let’s examine the version that passes an array argument to -InputObject:

The reason this doesn’t work is that the scriptblock you’re passing to the -Process parameter contains $_, but the foreach loop assigns each element to $o, and $_ isn’t defined anywhere. All elements in the results are 100 because each iteration sets $current_element to the results of the scriptblock {$_ + 100}, which always evaluates to 100 when $_ is null. To prove this out, try changing $_ in the scriptblock to $o, and you’ll get the expected results:

If you want to be able to use $_ in the scriptblock, change the foreach loop to a pipeline, by simply replacing foreach($o in $InputObject) { with $InputObject | %{. That way both versions will work, because the Chunk-Object function uses a pipeline internally, so $_ is set sequentially to each element of the array, regardless of whether the process block is invoked multiple times for a series of individual array elements passed in as pipeline input, or just once for a multiple-element array.


I looked at this again and noticed that in the line

you appear to be trying to pass $current_element as an argument to the scriptblock in $Process. This doesn’t work because parameters passed to a scriptblock work largely the same as in functions. If you invoke MyFunction 'foo', then ‘foo’ isn’t automatically assigned to $_ within the function; likewise, & {$_ + 100} 'foo' doesn’t set $_ to ‘foo’. Change your scriptblock argument to {$args[0] + 100}, and you’ll get the expected results with or without passing in pipeline input:

Note that although this version of the scriptblock argument works even if you keep the foreach loop, I’d still recommend using Foreach-Object ($InputObject | %{), because it’s generally more efficient, so the function will run faster for large amounts of data.


How to write a PowerShell advanced function that can work with both piped in objects and objects get from parameter value? by licensed under CC BY-SA | With most appropriate answer!

Leave a Reply