Slice a PowerShell array into groups of smaller arrays












3















I would like to convert a single array into a group of smaller arrays, based on a variable. So, 0,1,2,3,4,5,6,7,8,9 would become 0,1,2,3,4,5,6,7,8,9 when the size is 3.



My current approach:



$ids=@(0,1,2,3,4,5,6,7,8,9)
$size=3

0..[math]::Round($ids.count/$size) | % {

# slice first elements
$x = $ids[0..($size-1)]

# redefine array w/ remaining values
$ids = $ids[$size..$ids.Length]

# return elements (as an array, which isn't happening)
$x

} | % { "IDS: $($_ -Join ",")" }


Produces:



IDS: 0
IDS: 1
IDS: 2
IDS: 3
IDS: 4
IDS: 5
IDS: 6
IDS: 7
IDS: 8
IDS: 9


I would like it to be:



IDS: 0,1,2
IDS: 3,4,5
IDS: 6,7,8
IDS: 9


What am I missing?










share|improve this question

























  • You're just assigning $ids to $x and sending it down the stream to be iterated by | % {.

    – TheIncorrigible1
    Aug 29 '17 at 21:35






  • 3





    Use ,$x instead of just $x.

    – Bill_Stewart
    Aug 29 '17 at 21:39
















3















I would like to convert a single array into a group of smaller arrays, based on a variable. So, 0,1,2,3,4,5,6,7,8,9 would become 0,1,2,3,4,5,6,7,8,9 when the size is 3.



My current approach:



$ids=@(0,1,2,3,4,5,6,7,8,9)
$size=3

0..[math]::Round($ids.count/$size) | % {

# slice first elements
$x = $ids[0..($size-1)]

# redefine array w/ remaining values
$ids = $ids[$size..$ids.Length]

# return elements (as an array, which isn't happening)
$x

} | % { "IDS: $($_ -Join ",")" }


Produces:



IDS: 0
IDS: 1
IDS: 2
IDS: 3
IDS: 4
IDS: 5
IDS: 6
IDS: 7
IDS: 8
IDS: 9


I would like it to be:



IDS: 0,1,2
IDS: 3,4,5
IDS: 6,7,8
IDS: 9


What am I missing?










share|improve this question

























  • You're just assigning $ids to $x and sending it down the stream to be iterated by | % {.

    – TheIncorrigible1
    Aug 29 '17 at 21:35






  • 3





    Use ,$x instead of just $x.

    – Bill_Stewart
    Aug 29 '17 at 21:39














3












3








3


2






I would like to convert a single array into a group of smaller arrays, based on a variable. So, 0,1,2,3,4,5,6,7,8,9 would become 0,1,2,3,4,5,6,7,8,9 when the size is 3.



My current approach:



$ids=@(0,1,2,3,4,5,6,7,8,9)
$size=3

0..[math]::Round($ids.count/$size) | % {

# slice first elements
$x = $ids[0..($size-1)]

# redefine array w/ remaining values
$ids = $ids[$size..$ids.Length]

# return elements (as an array, which isn't happening)
$x

} | % { "IDS: $($_ -Join ",")" }


Produces:



IDS: 0
IDS: 1
IDS: 2
IDS: 3
IDS: 4
IDS: 5
IDS: 6
IDS: 7
IDS: 8
IDS: 9


I would like it to be:



IDS: 0,1,2
IDS: 3,4,5
IDS: 6,7,8
IDS: 9


What am I missing?










share|improve this question
















I would like to convert a single array into a group of smaller arrays, based on a variable. So, 0,1,2,3,4,5,6,7,8,9 would become 0,1,2,3,4,5,6,7,8,9 when the size is 3.



My current approach:



$ids=@(0,1,2,3,4,5,6,7,8,9)
$size=3

0..[math]::Round($ids.count/$size) | % {

# slice first elements
$x = $ids[0..($size-1)]

# redefine array w/ remaining values
$ids = $ids[$size..$ids.Length]

# return elements (as an array, which isn't happening)
$x

} | % { "IDS: $($_ -Join ",")" }


Produces:



IDS: 0
IDS: 1
IDS: 2
IDS: 3
IDS: 4
IDS: 5
IDS: 6
IDS: 7
IDS: 8
IDS: 9


I would like it to be:



IDS: 0,1,2
IDS: 3,4,5
IDS: 6,7,8
IDS: 9


What am I missing?







arrays powershell data-partitioning






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 3:45









mklement0

132k21246284




132k21246284










asked Aug 29 '17 at 21:28









craigcraig

17.1k1778147




17.1k1778147













  • You're just assigning $ids to $x and sending it down the stream to be iterated by | % {.

    – TheIncorrigible1
    Aug 29 '17 at 21:35






  • 3





    Use ,$x instead of just $x.

    – Bill_Stewart
    Aug 29 '17 at 21:39



















  • You're just assigning $ids to $x and sending it down the stream to be iterated by | % {.

    – TheIncorrigible1
    Aug 29 '17 at 21:35






  • 3





    Use ,$x instead of just $x.

    – Bill_Stewart
    Aug 29 '17 at 21:39

















You're just assigning $ids to $x and sending it down the stream to be iterated by | % {.

– TheIncorrigible1
Aug 29 '17 at 21:35





You're just assigning $ids to $x and sending it down the stream to be iterated by | % {.

– TheIncorrigible1
Aug 29 '17 at 21:35




3




3





Use ,$x instead of just $x.

– Bill_Stewart
Aug 29 '17 at 21:39





Use ,$x instead of just $x.

– Bill_Stewart
Aug 29 '17 at 21:39












5 Answers
5






active

oldest

votes


















3














You can use ,$x instead of just $x.



The about_Operators section in the documentation has this:



, Comma operator                                                  
As a binary operator, the comma creates an array. As a unary
operator, the comma creates an array with one member. Place the
comma before the member.





share|improve this answer































    4














    For the sake of completeness:



    function Slice-Array
    {

    [CmdletBinding()]
    param (
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$True)]
    [String]$Item,
    [int]$Size=10
    )
    BEGIN { $Items=@()}
    PROCESS {
    foreach ($i in $Item ) { $Items += $i }
    }
    END {
    0..[math]::Floor($Items.count/$Size) | ForEach-Object {
    $x, $Items = $Items[0..($Size-1)], $Items[$Size..$Items.Length]; ,$x
    }
    }
    }


    Usage:



    @(0,1,2,3,4,5,6,7,8,9) | Slice-Array -Size 3 | ForEach-Object { "IDs: $($_ -Join ",")" }





    share|improve this answer

































      2














      cls
      $ids=@(0,1,2,3,4,5,6,7,8,9)
      $size=3

      <#
      Manual Selection:
      $ids | Select-Object -First 3 -Skip 0
      $ids | Select-Object -First 3 -Skip 3
      $ids | Select-Object -First 3 -Skip 6
      $ids | Select-Object -First 3 -Skip 9
      #>

      # Select via looping
      $idx = 0
      while ($($size * $idx) -lt $ids.Length){

      $group = $ids | Select-Object -First $size -skip ($size * $idx)
      $group -join ","
      $idx ++
      }





      share|improve this answer































        1














        To add an explanation to Bill Stewart's effective solution:



        Outputting a collection such as an array[1] either implicitly or using return sends its elements individually through the pipeline; that is, the collection is enumerated (unrolled):



        # Count objects received.
        PS> (1..3 | Measure-Object).Count
        3 # Array elements were sent *individually* through the pipeline.


        Using the unary form of , (comma; the array-construction operator) to prevent enumeration is a conveniently concise, though somewhat obscure workaround:



        PS> (, (1..3) | Measure-Object).Count 
        1 # By wrapping the array in a helper array, the original array was preserved.


        That is, , <collection> creates a transient single-element helper array around the original collection so that the enumeration is only applied to the helper array, outputting the enclosed original collection as-is, as a single object.



        A conceptually clearer, but more verbose and slower approach is to use Write-Output -NoEnumerate, which clearly signals the intent to output a collection as a single object.



        PS> (Write-Output -NoEnumerate (1..3) | Measure-Object).Count 
        1 # Write-Output -NoEnumerate prevented enumeration.




        Pitfall with respect to visual inspection:



        On outputting for display, the boundaries between multiple arrays are seemingly erased again:



        PS> (1..2), (3..4) # Output two arrays without enumeration
        1
        2
        3
        4


        That is, even though two 2-element arrays were each sent as a single object each, the output, through showing elements each on their own line, makes it look like a flat 4-element array was received.



        A simple way around that is to stringify each array, which turns each array into a string containing a space-separated list of its elements.



        PS> (1..2), (3..4) | ForEach-Object { "$_" }
        1 2
        3 4


        Now it is obvious that two separate arrays were received.





        [1] What data types are enumerated:

        Instances of data types that implement the IEnumerable interface are automatically enumerated, but there are exceptions:

        Types that also implement IDictionary, such as hashtables, are not enumerated, and neither are XmlNode instances.

        Conversely, instances of DataTable (which doesn't implement IEnumerable) are enumerated (as the elements of their .Rows collection) - see the source code

        Additionally, note that stdout output from external program is enumerated line by line.






        share|improve this answer

































          0














          Craig himself has conveniently wrapped the splitting (partitioning) functionality in a robust function:



          Let me offer a better-performing evolution of it (PSv3+ syntax, renamed to Split-Array), which:




          • more efficiently collects the input objects using an extensible System.Collections.Generic.List[object]] collection.


          • doesn't modify the collection during splitting, and instead extracts ranges of elements from it.



          function Split-Array {
          [CmdletBinding()]
          param (
          [Parameter(Mandatory, ValueFromPipeline)]
          [String] $InputObject
          ,
          [ValidateRange(1, [int]::MaxValue)]
          [int] $Size = 10
          )
          begin { $items = New-Object System.Collections.Generic.List[object] }
          process { $items.AddRange($InputObject) }
          end {
          $chunkCount = [Math]::Floor($items.Count / $Size)
          foreach ($chunkNdx in 0..($chunkCount-1)) {
          , $items.GetRange($chunkNdx * $Size, $Size).ToArray()
          }
          if ($chunkCount * $Size -lt $items.Count) {
          , $items.GetRange($chunkCount * $Size, $items.Count - $chunkCount * $Size).ToArray()
          }
          }
          }


          With small input collections, the optimization won't matter much, but once you get into the thousands of elements, the speed-up can be dramatic:



          To give a rough sense of the performance improvement, using Time-Command:



          $ids = 0..1e4 # 10,000 numbers
          $size = 3 # chunk size

          Time-Command { $ids | Split-Array -size $size }, # optimized
          { $ids | Slice-Array -size $size } # original


          Sample result from a single-core Windows 10 VM with Windows 5.1 (the absolute times aren't important, but the factors are):



          Command                        Secs (10-run avg.) TimeSpan         Factor
          ------- ------------------ -------- ------
          $ids | Split-Array -size $size 0.150 00:00:00.1498207 1.00
          $ids | Slice-Array -size $size 10.382 00:00:10.3820590 69.30


          Note how the unoptimized function was almost 70 times slower.






          share|improve this answer

























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f45948580%2fslice-a-powershell-array-into-groups-of-smaller-arrays%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            5 Answers
            5






            active

            oldest

            votes








            5 Answers
            5






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            3














            You can use ,$x instead of just $x.



            The about_Operators section in the documentation has this:



            , Comma operator                                                  
            As a binary operator, the comma creates an array. As a unary
            operator, the comma creates an array with one member. Place the
            comma before the member.





            share|improve this answer




























              3














              You can use ,$x instead of just $x.



              The about_Operators section in the documentation has this:



              , Comma operator                                                  
              As a binary operator, the comma creates an array. As a unary
              operator, the comma creates an array with one member. Place the
              comma before the member.





              share|improve this answer


























                3












                3








                3







                You can use ,$x instead of just $x.



                The about_Operators section in the documentation has this:



                , Comma operator                                                  
                As a binary operator, the comma creates an array. As a unary
                operator, the comma creates an array with one member. Place the
                comma before the member.





                share|improve this answer













                You can use ,$x instead of just $x.



                The about_Operators section in the documentation has this:



                , Comma operator                                                  
                As a binary operator, the comma creates an array. As a unary
                operator, the comma creates an array with one member. Place the
                comma before the member.






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Aug 29 '17 at 21:58









                Bill_StewartBill_Stewart

                13k32435




                13k32435

























                    4














                    For the sake of completeness:



                    function Slice-Array
                    {

                    [CmdletBinding()]
                    param (
                    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$True)]
                    [String]$Item,
                    [int]$Size=10
                    )
                    BEGIN { $Items=@()}
                    PROCESS {
                    foreach ($i in $Item ) { $Items += $i }
                    }
                    END {
                    0..[math]::Floor($Items.count/$Size) | ForEach-Object {
                    $x, $Items = $Items[0..($Size-1)], $Items[$Size..$Items.Length]; ,$x
                    }
                    }
                    }


                    Usage:



                    @(0,1,2,3,4,5,6,7,8,9) | Slice-Array -Size 3 | ForEach-Object { "IDs: $($_ -Join ",")" }





                    share|improve this answer






























                      4














                      For the sake of completeness:



                      function Slice-Array
                      {

                      [CmdletBinding()]
                      param (
                      [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$True)]
                      [String]$Item,
                      [int]$Size=10
                      )
                      BEGIN { $Items=@()}
                      PROCESS {
                      foreach ($i in $Item ) { $Items += $i }
                      }
                      END {
                      0..[math]::Floor($Items.count/$Size) | ForEach-Object {
                      $x, $Items = $Items[0..($Size-1)], $Items[$Size..$Items.Length]; ,$x
                      }
                      }
                      }


                      Usage:



                      @(0,1,2,3,4,5,6,7,8,9) | Slice-Array -Size 3 | ForEach-Object { "IDs: $($_ -Join ",")" }





                      share|improve this answer




























                        4












                        4








                        4







                        For the sake of completeness:



                        function Slice-Array
                        {

                        [CmdletBinding()]
                        param (
                        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$True)]
                        [String]$Item,
                        [int]$Size=10
                        )
                        BEGIN { $Items=@()}
                        PROCESS {
                        foreach ($i in $Item ) { $Items += $i }
                        }
                        END {
                        0..[math]::Floor($Items.count/$Size) | ForEach-Object {
                        $x, $Items = $Items[0..($Size-1)], $Items[$Size..$Items.Length]; ,$x
                        }
                        }
                        }


                        Usage:



                        @(0,1,2,3,4,5,6,7,8,9) | Slice-Array -Size 3 | ForEach-Object { "IDs: $($_ -Join ",")" }





                        share|improve this answer















                        For the sake of completeness:



                        function Slice-Array
                        {

                        [CmdletBinding()]
                        param (
                        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$True)]
                        [String]$Item,
                        [int]$Size=10
                        )
                        BEGIN { $Items=@()}
                        PROCESS {
                        foreach ($i in $Item ) { $Items += $i }
                        }
                        END {
                        0..[math]::Floor($Items.count/$Size) | ForEach-Object {
                        $x, $Items = $Items[0..($Size-1)], $Items[$Size..$Items.Length]; ,$x
                        }
                        }
                        }


                        Usage:



                        @(0,1,2,3,4,5,6,7,8,9) | Slice-Array -Size 3 | ForEach-Object { "IDs: $($_ -Join ",")" }






                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Aug 30 '17 at 18:28

























                        answered Aug 29 '17 at 22:15









                        craigcraig

                        17.1k1778147




                        17.1k1778147























                            2














                            cls
                            $ids=@(0,1,2,3,4,5,6,7,8,9)
                            $size=3

                            <#
                            Manual Selection:
                            $ids | Select-Object -First 3 -Skip 0
                            $ids | Select-Object -First 3 -Skip 3
                            $ids | Select-Object -First 3 -Skip 6
                            $ids | Select-Object -First 3 -Skip 9
                            #>

                            # Select via looping
                            $idx = 0
                            while ($($size * $idx) -lt $ids.Length){

                            $group = $ids | Select-Object -First $size -skip ($size * $idx)
                            $group -join ","
                            $idx ++
                            }





                            share|improve this answer




























                              2














                              cls
                              $ids=@(0,1,2,3,4,5,6,7,8,9)
                              $size=3

                              <#
                              Manual Selection:
                              $ids | Select-Object -First 3 -Skip 0
                              $ids | Select-Object -First 3 -Skip 3
                              $ids | Select-Object -First 3 -Skip 6
                              $ids | Select-Object -First 3 -Skip 9
                              #>

                              # Select via looping
                              $idx = 0
                              while ($($size * $idx) -lt $ids.Length){

                              $group = $ids | Select-Object -First $size -skip ($size * $idx)
                              $group -join ","
                              $idx ++
                              }





                              share|improve this answer


























                                2












                                2








                                2







                                cls
                                $ids=@(0,1,2,3,4,5,6,7,8,9)
                                $size=3

                                <#
                                Manual Selection:
                                $ids | Select-Object -First 3 -Skip 0
                                $ids | Select-Object -First 3 -Skip 3
                                $ids | Select-Object -First 3 -Skip 6
                                $ids | Select-Object -First 3 -Skip 9
                                #>

                                # Select via looping
                                $idx = 0
                                while ($($size * $idx) -lt $ids.Length){

                                $group = $ids | Select-Object -First $size -skip ($size * $idx)
                                $group -join ","
                                $idx ++
                                }





                                share|improve this answer













                                cls
                                $ids=@(0,1,2,3,4,5,6,7,8,9)
                                $size=3

                                <#
                                Manual Selection:
                                $ids | Select-Object -First 3 -Skip 0
                                $ids | Select-Object -First 3 -Skip 3
                                $ids | Select-Object -First 3 -Skip 6
                                $ids | Select-Object -First 3 -Skip 9
                                #>

                                # Select via looping
                                $idx = 0
                                while ($($size * $idx) -lt $ids.Length){

                                $group = $ids | Select-Object -First $size -skip ($size * $idx)
                                $group -join ","
                                $idx ++
                                }






                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered Aug 29 '17 at 22:03









                                ChiliYagoChiliYago

                                3,413185896




                                3,413185896























                                    1














                                    To add an explanation to Bill Stewart's effective solution:



                                    Outputting a collection such as an array[1] either implicitly or using return sends its elements individually through the pipeline; that is, the collection is enumerated (unrolled):



                                    # Count objects received.
                                    PS> (1..3 | Measure-Object).Count
                                    3 # Array elements were sent *individually* through the pipeline.


                                    Using the unary form of , (comma; the array-construction operator) to prevent enumeration is a conveniently concise, though somewhat obscure workaround:



                                    PS> (, (1..3) | Measure-Object).Count 
                                    1 # By wrapping the array in a helper array, the original array was preserved.


                                    That is, , <collection> creates a transient single-element helper array around the original collection so that the enumeration is only applied to the helper array, outputting the enclosed original collection as-is, as a single object.



                                    A conceptually clearer, but more verbose and slower approach is to use Write-Output -NoEnumerate, which clearly signals the intent to output a collection as a single object.



                                    PS> (Write-Output -NoEnumerate (1..3) | Measure-Object).Count 
                                    1 # Write-Output -NoEnumerate prevented enumeration.




                                    Pitfall with respect to visual inspection:



                                    On outputting for display, the boundaries between multiple arrays are seemingly erased again:



                                    PS> (1..2), (3..4) # Output two arrays without enumeration
                                    1
                                    2
                                    3
                                    4


                                    That is, even though two 2-element arrays were each sent as a single object each, the output, through showing elements each on their own line, makes it look like a flat 4-element array was received.



                                    A simple way around that is to stringify each array, which turns each array into a string containing a space-separated list of its elements.



                                    PS> (1..2), (3..4) | ForEach-Object { "$_" }
                                    1 2
                                    3 4


                                    Now it is obvious that two separate arrays were received.





                                    [1] What data types are enumerated:

                                    Instances of data types that implement the IEnumerable interface are automatically enumerated, but there are exceptions:

                                    Types that also implement IDictionary, such as hashtables, are not enumerated, and neither are XmlNode instances.

                                    Conversely, instances of DataTable (which doesn't implement IEnumerable) are enumerated (as the elements of their .Rows collection) - see the source code

                                    Additionally, note that stdout output from external program is enumerated line by line.






                                    share|improve this answer






























                                      1














                                      To add an explanation to Bill Stewart's effective solution:



                                      Outputting a collection such as an array[1] either implicitly or using return sends its elements individually through the pipeline; that is, the collection is enumerated (unrolled):



                                      # Count objects received.
                                      PS> (1..3 | Measure-Object).Count
                                      3 # Array elements were sent *individually* through the pipeline.


                                      Using the unary form of , (comma; the array-construction operator) to prevent enumeration is a conveniently concise, though somewhat obscure workaround:



                                      PS> (, (1..3) | Measure-Object).Count 
                                      1 # By wrapping the array in a helper array, the original array was preserved.


                                      That is, , <collection> creates a transient single-element helper array around the original collection so that the enumeration is only applied to the helper array, outputting the enclosed original collection as-is, as a single object.



                                      A conceptually clearer, but more verbose and slower approach is to use Write-Output -NoEnumerate, which clearly signals the intent to output a collection as a single object.



                                      PS> (Write-Output -NoEnumerate (1..3) | Measure-Object).Count 
                                      1 # Write-Output -NoEnumerate prevented enumeration.




                                      Pitfall with respect to visual inspection:



                                      On outputting for display, the boundaries between multiple arrays are seemingly erased again:



                                      PS> (1..2), (3..4) # Output two arrays without enumeration
                                      1
                                      2
                                      3
                                      4


                                      That is, even though two 2-element arrays were each sent as a single object each, the output, through showing elements each on their own line, makes it look like a flat 4-element array was received.



                                      A simple way around that is to stringify each array, which turns each array into a string containing a space-separated list of its elements.



                                      PS> (1..2), (3..4) | ForEach-Object { "$_" }
                                      1 2
                                      3 4


                                      Now it is obvious that two separate arrays were received.





                                      [1] What data types are enumerated:

                                      Instances of data types that implement the IEnumerable interface are automatically enumerated, but there are exceptions:

                                      Types that also implement IDictionary, such as hashtables, are not enumerated, and neither are XmlNode instances.

                                      Conversely, instances of DataTable (which doesn't implement IEnumerable) are enumerated (as the elements of their .Rows collection) - see the source code

                                      Additionally, note that stdout output from external program is enumerated line by line.






                                      share|improve this answer




























                                        1












                                        1








                                        1







                                        To add an explanation to Bill Stewart's effective solution:



                                        Outputting a collection such as an array[1] either implicitly or using return sends its elements individually through the pipeline; that is, the collection is enumerated (unrolled):



                                        # Count objects received.
                                        PS> (1..3 | Measure-Object).Count
                                        3 # Array elements were sent *individually* through the pipeline.


                                        Using the unary form of , (comma; the array-construction operator) to prevent enumeration is a conveniently concise, though somewhat obscure workaround:



                                        PS> (, (1..3) | Measure-Object).Count 
                                        1 # By wrapping the array in a helper array, the original array was preserved.


                                        That is, , <collection> creates a transient single-element helper array around the original collection so that the enumeration is only applied to the helper array, outputting the enclosed original collection as-is, as a single object.



                                        A conceptually clearer, but more verbose and slower approach is to use Write-Output -NoEnumerate, which clearly signals the intent to output a collection as a single object.



                                        PS> (Write-Output -NoEnumerate (1..3) | Measure-Object).Count 
                                        1 # Write-Output -NoEnumerate prevented enumeration.




                                        Pitfall with respect to visual inspection:



                                        On outputting for display, the boundaries between multiple arrays are seemingly erased again:



                                        PS> (1..2), (3..4) # Output two arrays without enumeration
                                        1
                                        2
                                        3
                                        4


                                        That is, even though two 2-element arrays were each sent as a single object each, the output, through showing elements each on their own line, makes it look like a flat 4-element array was received.



                                        A simple way around that is to stringify each array, which turns each array into a string containing a space-separated list of its elements.



                                        PS> (1..2), (3..4) | ForEach-Object { "$_" }
                                        1 2
                                        3 4


                                        Now it is obvious that two separate arrays were received.





                                        [1] What data types are enumerated:

                                        Instances of data types that implement the IEnumerable interface are automatically enumerated, but there are exceptions:

                                        Types that also implement IDictionary, such as hashtables, are not enumerated, and neither are XmlNode instances.

                                        Conversely, instances of DataTable (which doesn't implement IEnumerable) are enumerated (as the elements of their .Rows collection) - see the source code

                                        Additionally, note that stdout output from external program is enumerated line by line.






                                        share|improve this answer















                                        To add an explanation to Bill Stewart's effective solution:



                                        Outputting a collection such as an array[1] either implicitly or using return sends its elements individually through the pipeline; that is, the collection is enumerated (unrolled):



                                        # Count objects received.
                                        PS> (1..3 | Measure-Object).Count
                                        3 # Array elements were sent *individually* through the pipeline.


                                        Using the unary form of , (comma; the array-construction operator) to prevent enumeration is a conveniently concise, though somewhat obscure workaround:



                                        PS> (, (1..3) | Measure-Object).Count 
                                        1 # By wrapping the array in a helper array, the original array was preserved.


                                        That is, , <collection> creates a transient single-element helper array around the original collection so that the enumeration is only applied to the helper array, outputting the enclosed original collection as-is, as a single object.



                                        A conceptually clearer, but more verbose and slower approach is to use Write-Output -NoEnumerate, which clearly signals the intent to output a collection as a single object.



                                        PS> (Write-Output -NoEnumerate (1..3) | Measure-Object).Count 
                                        1 # Write-Output -NoEnumerate prevented enumeration.




                                        Pitfall with respect to visual inspection:



                                        On outputting for display, the boundaries between multiple arrays are seemingly erased again:



                                        PS> (1..2), (3..4) # Output two arrays without enumeration
                                        1
                                        2
                                        3
                                        4


                                        That is, even though two 2-element arrays were each sent as a single object each, the output, through showing elements each on their own line, makes it look like a flat 4-element array was received.



                                        A simple way around that is to stringify each array, which turns each array into a string containing a space-separated list of its elements.



                                        PS> (1..2), (3..4) | ForEach-Object { "$_" }
                                        1 2
                                        3 4


                                        Now it is obvious that two separate arrays were received.





                                        [1] What data types are enumerated:

                                        Instances of data types that implement the IEnumerable interface are automatically enumerated, but there are exceptions:

                                        Types that also implement IDictionary, such as hashtables, are not enumerated, and neither are XmlNode instances.

                                        Conversely, instances of DataTable (which doesn't implement IEnumerable) are enumerated (as the elements of their .Rows collection) - see the source code

                                        Additionally, note that stdout output from external program is enumerated line by line.







                                        share|improve this answer














                                        share|improve this answer



                                        share|improve this answer








                                        edited Nov 14 '18 at 23:44

























                                        answered Nov 14 '18 at 14:25









                                        mklement0mklement0

                                        132k21246284




                                        132k21246284























                                            0














                                            Craig himself has conveniently wrapped the splitting (partitioning) functionality in a robust function:



                                            Let me offer a better-performing evolution of it (PSv3+ syntax, renamed to Split-Array), which:




                                            • more efficiently collects the input objects using an extensible System.Collections.Generic.List[object]] collection.


                                            • doesn't modify the collection during splitting, and instead extracts ranges of elements from it.



                                            function Split-Array {
                                            [CmdletBinding()]
                                            param (
                                            [Parameter(Mandatory, ValueFromPipeline)]
                                            [String] $InputObject
                                            ,
                                            [ValidateRange(1, [int]::MaxValue)]
                                            [int] $Size = 10
                                            )
                                            begin { $items = New-Object System.Collections.Generic.List[object] }
                                            process { $items.AddRange($InputObject) }
                                            end {
                                            $chunkCount = [Math]::Floor($items.Count / $Size)
                                            foreach ($chunkNdx in 0..($chunkCount-1)) {
                                            , $items.GetRange($chunkNdx * $Size, $Size).ToArray()
                                            }
                                            if ($chunkCount * $Size -lt $items.Count) {
                                            , $items.GetRange($chunkCount * $Size, $items.Count - $chunkCount * $Size).ToArray()
                                            }
                                            }
                                            }


                                            With small input collections, the optimization won't matter much, but once you get into the thousands of elements, the speed-up can be dramatic:



                                            To give a rough sense of the performance improvement, using Time-Command:



                                            $ids = 0..1e4 # 10,000 numbers
                                            $size = 3 # chunk size

                                            Time-Command { $ids | Split-Array -size $size }, # optimized
                                            { $ids | Slice-Array -size $size } # original


                                            Sample result from a single-core Windows 10 VM with Windows 5.1 (the absolute times aren't important, but the factors are):



                                            Command                        Secs (10-run avg.) TimeSpan         Factor
                                            ------- ------------------ -------- ------
                                            $ids | Split-Array -size $size 0.150 00:00:00.1498207 1.00
                                            $ids | Slice-Array -size $size 10.382 00:00:10.3820590 69.30


                                            Note how the unoptimized function was almost 70 times slower.






                                            share|improve this answer






























                                              0














                                              Craig himself has conveniently wrapped the splitting (partitioning) functionality in a robust function:



                                              Let me offer a better-performing evolution of it (PSv3+ syntax, renamed to Split-Array), which:




                                              • more efficiently collects the input objects using an extensible System.Collections.Generic.List[object]] collection.


                                              • doesn't modify the collection during splitting, and instead extracts ranges of elements from it.



                                              function Split-Array {
                                              [CmdletBinding()]
                                              param (
                                              [Parameter(Mandatory, ValueFromPipeline)]
                                              [String] $InputObject
                                              ,
                                              [ValidateRange(1, [int]::MaxValue)]
                                              [int] $Size = 10
                                              )
                                              begin { $items = New-Object System.Collections.Generic.List[object] }
                                              process { $items.AddRange($InputObject) }
                                              end {
                                              $chunkCount = [Math]::Floor($items.Count / $Size)
                                              foreach ($chunkNdx in 0..($chunkCount-1)) {
                                              , $items.GetRange($chunkNdx * $Size, $Size).ToArray()
                                              }
                                              if ($chunkCount * $Size -lt $items.Count) {
                                              , $items.GetRange($chunkCount * $Size, $items.Count - $chunkCount * $Size).ToArray()
                                              }
                                              }
                                              }


                                              With small input collections, the optimization won't matter much, but once you get into the thousands of elements, the speed-up can be dramatic:



                                              To give a rough sense of the performance improvement, using Time-Command:



                                              $ids = 0..1e4 # 10,000 numbers
                                              $size = 3 # chunk size

                                              Time-Command { $ids | Split-Array -size $size }, # optimized
                                              { $ids | Slice-Array -size $size } # original


                                              Sample result from a single-core Windows 10 VM with Windows 5.1 (the absolute times aren't important, but the factors are):



                                              Command                        Secs (10-run avg.) TimeSpan         Factor
                                              ------- ------------------ -------- ------
                                              $ids | Split-Array -size $size 0.150 00:00:00.1498207 1.00
                                              $ids | Slice-Array -size $size 10.382 00:00:10.3820590 69.30


                                              Note how the unoptimized function was almost 70 times slower.






                                              share|improve this answer




























                                                0












                                                0








                                                0







                                                Craig himself has conveniently wrapped the splitting (partitioning) functionality in a robust function:



                                                Let me offer a better-performing evolution of it (PSv3+ syntax, renamed to Split-Array), which:




                                                • more efficiently collects the input objects using an extensible System.Collections.Generic.List[object]] collection.


                                                • doesn't modify the collection during splitting, and instead extracts ranges of elements from it.



                                                function Split-Array {
                                                [CmdletBinding()]
                                                param (
                                                [Parameter(Mandatory, ValueFromPipeline)]
                                                [String] $InputObject
                                                ,
                                                [ValidateRange(1, [int]::MaxValue)]
                                                [int] $Size = 10
                                                )
                                                begin { $items = New-Object System.Collections.Generic.List[object] }
                                                process { $items.AddRange($InputObject) }
                                                end {
                                                $chunkCount = [Math]::Floor($items.Count / $Size)
                                                foreach ($chunkNdx in 0..($chunkCount-1)) {
                                                , $items.GetRange($chunkNdx * $Size, $Size).ToArray()
                                                }
                                                if ($chunkCount * $Size -lt $items.Count) {
                                                , $items.GetRange($chunkCount * $Size, $items.Count - $chunkCount * $Size).ToArray()
                                                }
                                                }
                                                }


                                                With small input collections, the optimization won't matter much, but once you get into the thousands of elements, the speed-up can be dramatic:



                                                To give a rough sense of the performance improvement, using Time-Command:



                                                $ids = 0..1e4 # 10,000 numbers
                                                $size = 3 # chunk size

                                                Time-Command { $ids | Split-Array -size $size }, # optimized
                                                { $ids | Slice-Array -size $size } # original


                                                Sample result from a single-core Windows 10 VM with Windows 5.1 (the absolute times aren't important, but the factors are):



                                                Command                        Secs (10-run avg.) TimeSpan         Factor
                                                ------- ------------------ -------- ------
                                                $ids | Split-Array -size $size 0.150 00:00:00.1498207 1.00
                                                $ids | Slice-Array -size $size 10.382 00:00:10.3820590 69.30


                                                Note how the unoptimized function was almost 70 times slower.






                                                share|improve this answer















                                                Craig himself has conveniently wrapped the splitting (partitioning) functionality in a robust function:



                                                Let me offer a better-performing evolution of it (PSv3+ syntax, renamed to Split-Array), which:




                                                • more efficiently collects the input objects using an extensible System.Collections.Generic.List[object]] collection.


                                                • doesn't modify the collection during splitting, and instead extracts ranges of elements from it.



                                                function Split-Array {
                                                [CmdletBinding()]
                                                param (
                                                [Parameter(Mandatory, ValueFromPipeline)]
                                                [String] $InputObject
                                                ,
                                                [ValidateRange(1, [int]::MaxValue)]
                                                [int] $Size = 10
                                                )
                                                begin { $items = New-Object System.Collections.Generic.List[object] }
                                                process { $items.AddRange($InputObject) }
                                                end {
                                                $chunkCount = [Math]::Floor($items.Count / $Size)
                                                foreach ($chunkNdx in 0..($chunkCount-1)) {
                                                , $items.GetRange($chunkNdx * $Size, $Size).ToArray()
                                                }
                                                if ($chunkCount * $Size -lt $items.Count) {
                                                , $items.GetRange($chunkCount * $Size, $items.Count - $chunkCount * $Size).ToArray()
                                                }
                                                }
                                                }


                                                With small input collections, the optimization won't matter much, but once you get into the thousands of elements, the speed-up can be dramatic:



                                                To give a rough sense of the performance improvement, using Time-Command:



                                                $ids = 0..1e4 # 10,000 numbers
                                                $size = 3 # chunk size

                                                Time-Command { $ids | Split-Array -size $size }, # optimized
                                                { $ids | Slice-Array -size $size } # original


                                                Sample result from a single-core Windows 10 VM with Windows 5.1 (the absolute times aren't important, but the factors are):



                                                Command                        Secs (10-run avg.) TimeSpan         Factor
                                                ------- ------------------ -------- ------
                                                $ids | Split-Array -size $size 0.150 00:00:00.1498207 1.00
                                                $ids | Slice-Array -size $size 10.382 00:00:10.3820590 69.30


                                                Note how the unoptimized function was almost 70 times slower.







                                                share|improve this answer














                                                share|improve this answer



                                                share|improve this answer








                                                edited Nov 15 '18 at 3:00

























                                                answered Nov 14 '18 at 16:19









                                                mklement0mklement0

                                                132k21246284




                                                132k21246284






























                                                    draft saved

                                                    draft discarded




















































                                                    Thanks for contributing an answer to Stack Overflow!


                                                    • Please be sure to answer the question. Provide details and share your research!

                                                    But avoid



                                                    • Asking for help, clarification, or responding to other answers.

                                                    • Making statements based on opinion; back them up with references or personal experience.


                                                    To learn more, see our tips on writing great answers.




                                                    draft saved


                                                    draft discarded














                                                    StackExchange.ready(
                                                    function () {
                                                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f45948580%2fslice-a-powershell-array-into-groups-of-smaller-arrays%23new-answer', 'question_page');
                                                    }
                                                    );

                                                    Post as a guest















                                                    Required, but never shown





















































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown

































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown







                                                    Popular posts from this blog

                                                    Florida Star v. B. J. F.

                                                    Danny Elfman

                                                    Lugert, Oklahoma