Powershell 将一个长数组分解为一行长度为 N 的数组数组?

问题描述 投票:0回答:10

例如,给定一个列表

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
powershell
10个回答
58
投票

这有点旧了,但我想我应该引入用于将数组拆分为块的方法。 您可以将 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++
并在没有自定义对象的情况下获得相同的效果。


12
投票

写于 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]

7
投票
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

5
投票

使用

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

3
投票

@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

1
投票
$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)]) } 

不知道为什么我必须在

(
后面加一个逗号。


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

1
投票

一种简单的方法,使用

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]

0
投票

如果集合足够大(例如数百万个元素),那么性能可能很重要。首先从性能的角度来看,之前提出的解决方案都不能让我满意。这是我的解决方案:

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
(以毫秒为单位)


0
投票
$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
© www.soinside.com 2019 - 2024. All rights reserved.