例如,给定一个列表
1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8
和一个数字4,它返回一个长度为4的列表列表,即
(1, 2, 3, 4), (5, 6, 7, 8), (1, 2, 3, 4), (5, 6, 7, 8)
。
基本上我想在Powershell中实现以下Python代码。
s = 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8
z = zip(*[iter(s)]*4) # Here N is 4
# z is (1, 2, 3, 4), (5, 6, 7, 8), (1, 2, 3, 4), (5, 6, 7, 8)
以下脚本返回 17 而不是 5。
$a = 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,0
$b = 0..($a.Length / 4) | % { @($a[($_*4)..($_*4 + 4 - 1)]) }
$b.Length
这有点旧了,但我想我应该引入用于将数组拆分为块的方法。 您可以将 Group-Object 与构造属性一起使用:
$bigList = 1..1000
$counter = [pscustomobject] @{ Value = 0 }
$groupSize = 100
$groups = $bigList | Group-Object -Property { [math]::Floor($counter.Value++ / $groupSize) }
$groups
将是 GroupInfo 对象的集合;在这种情况下,每个组将恰好有 100 个元素(可通过 $groups[0].Group
、$groups[1].Group
等访问。)我使用计数器的对象属性来避免 -Property 脚本块内的范围问题,因为一个简单的 $i++
不会写回原始变量。 或者,您可以使用 $script:counter = 0
和 $script:counter++
并在没有自定义对象的情况下获得相同的效果。
写于 2009 年 PowerShell 拆分每个函数
可能可以改进。
Function Split-Every($list, $count=4) {
$aggregateList = @()
$blocks = [Math]::Floor($list.Count / $count)
$leftOver = $list.Count % $count
for($i=0; $i -lt $blocks; $i++) {
$end = $count * ($i + 1) - 1
$aggregateList += @(,$list[$start..$end])
$start = $end + 1
}
if($leftOver -gt 0) {
$aggregateList += @(,$list[$start..($end+$leftOver)])
}
$aggregateList
}
$s = 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8
$r = Split-Every $s 4
$r[0]
""
$r[1]
""
$r[2]
""
$r[3]
PS> $a = 1..16
PS> $z=for($i=0; $i -lt $a.length; $i+=4){ ,($a[$i]..$a[$i+3])}
PS> $z.count
4
PS> $z[0]
1
2
3
4
PS> $z[1]
5
6
7
8
PS> $z[2]
9
10
11
12
PS> $z[3]
13
14
15
16
使用
select
提供解决方案。不用担心$list.Count
能否被$chunkSize
整除。
function DivideList {
param(
[object[]]$list,
[int]$chunkSize
)
for ($i = 0; $i -lt $list.Count; $i += $chunkSize) {
, ($list | select -Skip $i -First $chunkSize)
}
}
DivideList -list @(1..17) -chunkSize 4 | foreach { $_ -join ',' }
输出:
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
17
@Shay Levy 回答:如果您将 a 的值更改为 1..15,那么您的解决方案将不再起作用(Peter Reavy 评论)
所以这对我有用:
$a = 1..15
$z=for($i=0; $i -lt $a.length; $i+=4){if ($a.length -gt ($i+3)) { ,($a[$i]..$a[$i+3])} else { ,($a[$i]..$a[-1])}}
$z.count
$a = 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,0
$b = 0..([Math]::ceiling($a.Length / 4) - 1) |
% { @(, $a[($_*4)..($_*4 + 4 - 1)]) }
不知道为什么我必须在
(
后面加一个逗号。
Clear-Host
$s = 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18
$count = $s.Length
$split = $count/2
$split --
$b = $s[0..$split]
$split ++
$a = $s[$split..$count]
write-host "first array"
$b
write-host "next array"
$a
#clean up
Get-Variable -Exclude PWD,*Preference | Remove-Variable -EA 0
List<T>
来收集输入对象元素和输出,而无需使用 $PSCmdlet.WriteObject
进行枚举。这与 Windows PowerShell 5.1 兼容。
function Split-Collection {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[object[]] $InputObject,
[Parameter(Position = 0)]
[ValidateRange(1, [int]::MaxValue)]
[int] $ChunkSize = 5
)
begin {
$list = [System.Collections.Generic.List[object]]::new()
}
process {
foreach($item in $InputObject) {
$list.Add($item)
if($list.Count -eq $ChunkSize) {
$PSCmdlet.WriteObject($list.ToArray())
$list.Clear()
}
}
}
end {
if($list.Count) {
$PSCmdlet.WriteObject($list.ToArray())
}
}
}
用途:
PS ..\pwsh> 0..10 | Split-Collection 3 | ForEach-Object { "[$_]" }
[0 1 2]
[3 4 5]
[6 7 8]
[9 10]
PS ..\pwsh> Split-Collection -InputObject (0..10) 3 | ForEach-Object { "[$_]" }
[0 1 2]
[3 4 5]
[6 7 8]
[9 10]
如果使用 PowerShell 7+,还有一种更简单的方法是使用 LINQ 中的
Enumerable.Chunk
。
PS ..\pwsh> [System.Linq.Enumerable]::Chunk([int[]] (0..10), 3) | ForEach-Object { "[$_]" }
[0 1 2]
[3 4 5]
[6 7 8]
[9 10]
如果集合足够大(例如数百万个元素),那么性能可能很重要。首先从性能的角度来看,之前提出的解决方案都不能让我满意。这是我的解决方案:
Function Chunk {
param(
[object[]] $source,
[int] $size = 1)
$chunkCount = [Math]::Ceiling($source.Count / $size)
0 .. ($chunkCount - 1) `
| ForEach-Object {
$startIndex = $_ * $size
$endIndex = [Math]::Min(($_ + 1) * $size, $source.Count)
,$source[$startIndex .. ($endIndex - 1)]
}
}
使用示例:
Chunk @(1..17) 4 | foreach { $_ -join ',' }
输出:
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
17
绩效衡量:
(Measure-Command { Chunk @(1..1000000) 1000 }).TotalMilliseconds
输出:
140.467
(以毫秒为单位)
$chunk_size = 10
@(1..100) | % { $c = @(); $i = 0 } { if ($i % $chunk_size -eq 0) { $c += ,@() }; $c[-1] += $_; $i++ }
$c
$chunk_size = 10 # Set desired chunk size
@(1..100) | % `
{ # Initialize loop variables
$c = @(); # Cumulating array of chunks
$i = 0; # Incrementing index
} {
if ($i % $chunk_size -eq 0) { # If the index is divisible by chunk size
$c += ,@() # Add another chunk to the accumulator
}
$c[-1] += $_; # Add current element to the last chunk
$i++ # Increment
}
$c