【问题标题】:Better "Split-ArrayInChunks" for PowerShell为 PowerShell 提供更好的“Split-ArrayInChunks”
【发布时间】:2016-02-04 20:15:51
【问题描述】:

当为我的“Split-ArrayInChunks”方法使用基于数组的脚本时,处理 190.000 多条记录需要很长时间,我的初始版本基于此代码(请参阅Split up an array into chunks and start a job on each one.

$computers = gc c:\somedir\complist.txt
$n = 6
$complists = @{}
$count = 0 
$computers |% {$complists[$count % $n] += @($_);$count++}

0..($n-1) |% {
start-job -scriptblock {gwmi win32_operatingsystem -computername $args} -argumentlist $complists[$_]
}

我找到了这篇文章Performance: The += Operator (and When to Avoid It),基本上推荐作者使用“System.Collections.Generic.List”或“System.Collections.ArrayList”来代替数组。所以我想出了这个实现:

function Split-ArrayInChunks_UsingGenericList($inArray, $numberOfChunks) {

    $list = New-Object System.Collections.Generic.List[System.Collections.Generic.List[PSCustomObject]]
    $count = 0 

    # populate with empty lists
    0..($numberOfChunks-1) | % {
        $list.Add((New-Object System.Collections.Generic.List[PSCustomObject]))
    }

    # create packages
    $inArray | % { 
        $list[$count % $numberOfChunks].Add($_); 
        $count++ 
    }

    return $list.ToArray()
}

我也尝试使用“System.Collections.ArrayList”,但是这个函数返回一个平面数组。函数内部是 $arrayList 一个嵌套数组,但是一旦在函数外部,我就有一个平面数组(192169 个项目而不是 10 个块)。

function Split-ArrayInChunks_UsingArrayList($inArray, $numberOfChunks) {

    $arryList = New-Object System.Collections.ArrayList
    $count = 0 

    # populate 
    0..($numberOfChunks-1) | % {
        $arryList.Add((New-Object System.Collections.ArrayList))
    }

    $inArray | % { 
        $arryList[$count % $numberOfChunks].Add($_); 
        $count++ 
    }

    Write-Host 'Number of arryList:'$arryList.Count
    Write-Host 'Number of items in first arryList:' $arryList[0].Count
    return $arryList
}

为了说明“扁平化”问题生成以下代码...

Write-Host '-------------------------------'
$packages1 = Split-ArrayInChunks_UsingGenericList $data.CrmRecords 10
Write-Host 'Number of packages1:'$packages1.Count
Write-Host 'Number of items in first package1:' $packages1[0].Count

Write-Host '-------------------------------'
$packages2 = Split-ArrayInChunks_UsingArrayList $data.CrmRecords 10
Write-Host 'Number of packages2:'$packages2.Count
Write-Host 'Number of items in first package2:' $packages2[0].Count

...这个输出:

-------------------------------
Number of packages1: 10
Number of items in first package1: 19215
-------------------------------
Number of arryList: 10
Number of items in first arryList: 19215
Number of packages2: 192169
Number of items in first package2: 1

所以我有两个问题:

  1. 任何改进我的“Split-ArrayInChunks_UsingArrayList”版本的选项(例如更快、更易读)?
  2. 为什么“ArrayInChunks_UsingArrayList”的返回值是一个平面数组,函数里面%arrayList是一个嵌套数组?

2016 年 2 月 4 日更新: 我根据反馈更新了我的代码(使用 [void] 来防止输出污染)并且它可以工作。唯一奇怪的是,当我使用 |format-table 时,我的版本 (Split-ArrayInChunks_UsingArrayList) 再次打印为平面列表:

function Split-ArrayInChunks_UsingArrayList($inArray, $numberOfChunks) {
    $arryList = New-Object System.Collections.ArrayList
    $count = 0 

    # populate 
    0..($numberOfChunks-1) | % {

        [void]$arryList.Add((New-Object System.Collections.ArrayList))
    }

    $inArray | % { 
        [void]$arryList[$count % $numberOfChunks].Add($_); 
        $count++ 
    }

    return $arryList
}

function Split-ArrayInChunks_CommunityVersion($inArray, $numberOfChunks) {
    $Lists = @{}
    $count = 0 

    # populate 
    0..($numberOfChunks-1) | % {
        $Lists[$_] = New-Object System.Collections.ArrayList
    }

    $inArray | % { 
        [void]$Lists[$count % $numberOfChunks].Add($_); 
        $count++ 
    }

    return $Lists
}

当我执行这段代码时...

Write-Host 'CommunityVersion'
Write-Host '-------------------------------'
Split-ArrayInChunks_CommunityVersion $list 6 | Format-Table -AutoSize

Write-Host 'ArrayInChunks_UsingArrayList'
Write-Host '-------------------------------'
Split-ArrayInChunks_UsingArrayList $list 6 | Format-Table -AutoSize

...这是控制台中的输出:

CommunityVersion
-------------------------------

Name Value                                 
---- -----                                 
5    {denn, getan, verhaftet}              
4    {haben, Böses, Morgens, war}          
3    {verleumdet, etwas, eines, es}        
2    {Josef K., er, er, er}                
1    {musste, dass, wurde, sagte}          
0    {Jemand, ohne, hätte, »Wie ein Hund!«}


ArrayInChunks_UsingArrayList
-------------------------------
Jemand
ohne
hätte
»Wie ein Hund!«
musste
dass
wurde
sagte
Josef K.
er
er
er
verleumdet
etwas
eines
es
haben
Böses
Morgens
war
denn
getan
verhaftet

我不明白为什么“ArrayInChunks_UsingArrayList”打印为列表,它是一个嵌套数组,就像“ArrayInChunks_CommunityVersion”一样。

【问题讨论】:

  • ArrayList.Add 不是void
  • 如果您正在从文件中读取列表,为什么不直接使用 -ReadCount 呢?
  • @mjolinor:不确定你的意思。就我而言,我从一个网络服务中加载了这 190.000 多条记录。我需要将这些记录打包成块,以便在作业中处理这些块。
  • 留下一个建议的答案。

标签: arrays performance powershell arraylist


【解决方案1】:

好的,我会这样做:

function Split-ArrayInChunks_UsingArrayList($inArray, $numberOfChunks) {

    $Lists = @{}
    $count = 0 

    # populate 
    0..($numberOfChunks-1) | % {
        $Lists[$_] = New-Object System.Collections.ArrayList
    }

    $inArray | % { 
        [void]$Lists[$count % $numberOfChunks].Add($_); 
        $count++ 
    }

    Write-Host 'Number of arryList:'$Lists.Count
    Write-Host 'Number of items in first arryList:' $Lists[0].Count
    return $Lists
}

【讨论】:

  • 问题中观察到的真正问题是 OP 用 (ArrayList.Add()) 的返回值污染了他的输出 - 你需要抑制这些调用
  • @Mathias 啊。固定的。谢谢。
  • @mjolinor 我更新了我的问题,请参阅“更新 2016-02-04”。您的版本按预期工作,但“ArrayList in ArrayList”方法再次打印在平面列表中。知道这是为什么吗?
  • @thuld 那是因为 PowerShell 格式化程序以这种方式工作。 Get-Help Format-Table -Parameter ExpandSplit-ArrayInChunks_UsingArrayList $list 6 | Format-Table -AutoSize -Expand CoreOnly.
  • @thuld Split-ArrayInChunks_UsingArrayList 返回多个 ArrayLists,但 Split-ArrayInChunks_CommunityVersion 返回单个 HashtableSplit-ArrayInChunks_UsingArrayList $list 6|%{"Item from pipeline: $($_.GetType().FullName)."}Split-ArrayInChunks_CommunityVersion $list 6|%{"Item from pipeline: $($_.GetType().FullName)."}.
【解决方案2】:

原来“$inArray | %”的使用使操作变得如此缓慢。当使用正常的 foreach 循环时,创建块的时间不到 2 秒。使用基于“$inArray | %”的版本需要20秒

function Split-ArrayInChunks_Fast($inArray, $numberOfChunks) {

    $arrayList = New-Object System.Collections.ArrayList
    $count = 0 

    # populate 
    0..($numberOfChunks-1) | % {
        [void]$arrayList.Add((New-Object System.Collections.ArrayList))
    }

    foreach($elem in $inArray) {

       [void]$arrayList[$count % $numberOfChunks].Add($elem) 
       $count++ 
    }

    return $arrayList.ToArray()
}

function Split-ArrayInChunks_Slow($inArray, $numberOfChunks) {

    $arrayList = New-Object System.Collections.ArrayList
    $count = 0 

    # populate 
    0..($numberOfChunks-1) | % {
        [void]$arrayList.Add((New-Object System.Collections.ArrayList))
    }

    $inArray | % { 
        [void]$arrayList[$count % $numberOfChunks].Add($_); 
        $count++ 
    }

    return $arrayList.ToArray()
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-15
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多