4

I am wondering how one would tackle looping through a collection of objects, processing the elements of that collection in groups instead of singularly, as is the case in normal Foreach loops. For example, instead of this:

$items = get-vm
foreach ($item in $items) { do something }

I would like to do this:

$items = get-vm
foreach ((5)$item in $items) {do something}

Essentially, this statement intends to say foreach 5 items in items do some work.....

Can anyone show me the proper constructs required to accomplish this?

Wilq
  • 2,245
  • 4
  • 33
  • 37
user3014016
  • 75
  • 2
  • 5
  • 1
    Please take a step back and describe the actual problem you're trying to solve instead of what you perceive as the solution. Why do you think you need to process the VMs in batches of 5 instead of just doing it sequentially? – Ansgar Wiechers Nov 20 '13 at 18:30

4 Answers4

6

I've got this:

 $array = 1..100
 $group = 10
 $i = 0

 do {
     $array[$i..(($i+= $group) - 1)]
     '*****'
     }
      until ($i -ge $array.count -1)
mjolinor
  • 66,130
  • 7
  • 114
  • 135
  • 1
    FWIW I wanted to process the last group for mine, even if it did not contain the full group size so I changed `($i -ge $array.count -1)` to `($i -gt $array.count -1)` – Mark Schultheiss Jun 30 '18 at 13:25
  • Just to clarify @MarkSchultheiss's comment. Using -ge would only hide the last group if it had a remainder of 1 in the expression: `$array.Length % $group`. (% being the modulus operator) So, if you don't want a group with a remainder of 1 displaying use -ge, otherwise use -gt. – Cat Nov 01 '19 at 17:58
1

Here's a function that will collection items into chunks of a specified size:

function ChunkBy($items,[int]$size) {
    $list = new-object System.Collections.ArrayList
    $tmpList = new-object System.Collections.ArrayList
    foreach($item in $items) {
        $tmpList.Add($item) | out-null
        if ($tmpList.Count -ge $size) {
            $list.Add($tmpList.ToArray()) | out-null
            $tmpList.Clear()
        }
    }

    if ($tmpList.Count -gt 0) {
        $list.Add($tmpList.ToArray()) | out-null
    }

    return $list.ToArray()
}

The usage would be something like:

ChunkBy (get-process) 10 | foreach { $_.Count }
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • This is handy. Last line needs to be `return ,$list.ToArray()` to make sure you still get an array of arrays, if there's only one array to return – codeulike Oct 19 '22 at 10:24
1

You guys definitely gave me some great ideas for this functionality. I ended up going with the following:

#create base collection
$group = get-vm
$i = 0

do {
    new-variable -Name "subgroup$i" -value $group[0..4]
    ++$i
    $group = $group[5..$group.length]
}
while ($group.length -gt 0)

This code results in a number of subgroups, which is based on how many times the base collection is divisible by 5, which is the desired subgroup quantity in this case......

user3014016
  • 75
  • 2
  • 5
0

Change to Do...Until, increment counter by 5 each time.

$items = get-vm
$i = 0
do {
#STUFF
$i = $i + 5
} until ($i -ge $items.count)

(Untested, but should give you an idea)

EDIT: Fully tested:

$items = @()
foreach ($item in (get-alias)) {
$items += $item
}

$i = 0
do {
write-host $i
$i = $i + 5
} until ($i -ge $items.count)

Output:

0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105 110 115 120 125 130 135

EDIT 2:

$items = @()
for($i=1; $i -le 75; $i++) {
$items += $i
}

[int]$i = 0
$outarray = @()
do {
$outarray += $items[$i]
if ((($i+1)%5) -eq 0) {
    write-host $outarray
    write-host ---------
    $outarray = @()
}

$i = $i + 1
} until ($i -gt $items.count)
user3012708
  • 793
  • 1
  • 11
  • 33
  • this example seems to show the processing of every fifth element in the array. What I'm seeking is to process the entire array in batches of five at a time. Using your activity, which is simply printing the elements, I would expect to see groups of five numbers. For example: 12345 678910 1112131415 and so on...... – user3014016 Nov 20 '13 at 17:14