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

user133580

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:

function Chunk-Object()
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true,
                   ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true)] [object[]] $InputObject,
        [Parameter()] [scriptblock] $Process,
        [Parameter()] [int] $ElementsPerChunk
    )

    Begin {
        $cache = @();
        $index = 0;
    }

    Process {
        foreach($o in $InputObject) {
            $current_element = $o;
            if($Process) {
                $current_element = & $Process $current_element;
            }
            if($cache.Length -eq $ElementsPerChunk) {
                ,$cache;
                $cache = @($current_element);
                $index = 1;
            }
            else {
                $cache += $current_element;
                $index++;
            }
        }
    }

    End {
        if($cache) {
            ,$cache;
        }
    }
}


(Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$_ + 100} -ElementsPerChunk 3)
Write-Host "------------------------------------------------"
(echo 1 2 3 4 5 6 7 | Chunk-Object -Process {$_ + 100} -ElementsPerChunk 3)

The result is:

PS C:\Users\a> C:\Untitled5.ps1
100
100
100
100
100
100
100
------------------------------------------------
101
102
103
104
105
106
107

PS C:\Users\a> 

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?

Adi Inbar

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:

echo 1 2 3 4 5 6 7 | Chunk-Object -Process {$_ + 100} -ElementsPerChunk 3

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:

Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$_ + 100} -ElementsPerChunk 3

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:

Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$o + 100} -ElementsPerChunk 3

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.


UPDATE:

I looked at this again and noticed that in the line

$current_element = & $Process $current_element;

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:

Chunk-Object -InputObject (echo 1 2 3 4 5 6 7) -Process {$args[0] + 100} -ElementsPerChunk 3

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.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

PowerShell: How can I pipeline objects to a parameter that accepts input "ByPropertyName"?

From Dev

javascript How to get an objects value from an array full of objects?

From Dev

How to get the value from a nested array with objects

From Dev

Setting value to objects' property from its function doesn't work

From Dev

Setting value to objects' property from its function doesn't work

From Dev

Advanced objects as value of Enum

From Dev

How to handle powershell Advanced function ErrorAction parameter?

From Dev

How to get both colliding sprite objects in pygame?

From Dev

Can I get each value based on key from array of objects?

From Dev

How can I compare two arrays of objects and change a value for those that are found in both arrays?

From Dev

How to get lapply to output 2 objects obtained from a function

From Dev

how to get particular key value from all the objects in a AJAX call

From Dev

How to get value from array, which is inside objects in react js?

From Dev

UnderscoreJS - how to get first instance of value from the array of objects?

From Dev

How can I write a collection of objects to file?

From Dev

How can I write a collection of objects to file?

From Dev

How can I get the youngest objects from SQLAlchemy?

From Dev

Default parameter value for objects

From Dev

How to write items from Ruby objects to file

From Dev

How can hitTestObject work for many objects?

From Dev

Can't print value from array of objects

From Dev

BitGO - Get MIN value from Array of Objects

From Dev

Get objects by value from IntegerField in queryset

From Dev

Get value from an array of objects in iOS

From Dev

Get value from array of objects in jQuery

From Dev

BitGO - Get MIN value from Array of Objects

From Dev

BitGO - Get account & value from Array of Objects

From Dev

Get unique value from array of objects

From Dev

get value of a property from array of objects

Related Related

  1. 1

    PowerShell: How can I pipeline objects to a parameter that accepts input "ByPropertyName"?

  2. 2

    javascript How to get an objects value from an array full of objects?

  3. 3

    How to get the value from a nested array with objects

  4. 4

    Setting value to objects' property from its function doesn't work

  5. 5

    Setting value to objects' property from its function doesn't work

  6. 6

    Advanced objects as value of Enum

  7. 7

    How to handle powershell Advanced function ErrorAction parameter?

  8. 8

    How to get both colliding sprite objects in pygame?

  9. 9

    Can I get each value based on key from array of objects?

  10. 10

    How can I compare two arrays of objects and change a value for those that are found in both arrays?

  11. 11

    How to get lapply to output 2 objects obtained from a function

  12. 12

    how to get particular key value from all the objects in a AJAX call

  13. 13

    How to get value from array, which is inside objects in react js?

  14. 14

    UnderscoreJS - how to get first instance of value from the array of objects?

  15. 15

    How can I write a collection of objects to file?

  16. 16

    How can I write a collection of objects to file?

  17. 17

    How can I get the youngest objects from SQLAlchemy?

  18. 18

    Default parameter value for objects

  19. 19

    How to write items from Ruby objects to file

  20. 20

    How can hitTestObject work for many objects?

  21. 21

    Can't print value from array of objects

  22. 22

    BitGO - Get MIN value from Array of Objects

  23. 23

    Get objects by value from IntegerField in queryset

  24. 24

    Get value from an array of objects in iOS

  25. 25

    Get value from array of objects in jQuery

  26. 26

    BitGO - Get MIN value from Array of Objects

  27. 27

    BitGO - Get account & value from Array of Objects

  28. 28

    Get unique value from array of objects

  29. 29

    get value of a property from array of objects

HotTag

Archive