【问题标题】:How to dynamically add PSCustomObjects to a list [duplicate]如何将 PSCustomObjects 动态添加到列表中[重复]
【发布时间】:2021-12-18 00:14:44
【问题描述】:

我正在创建一个脚本来解析 CSV 文件,其中我将 CSV 中每个索引字段的内容存储为 PSCustomObject 中的 NoteProperty。

当我逐行解析文件时,我将 PSCustomObject 添加到列表类型。当我输出我的列表时,我希望能够执行以下操作:

 $list | Format-Table

并且可以很好地查看 csv 文件中的每一行,并分成标题向上的列。

问题

当我将 PSCustomObject 添加到列表时,它会将列表的类型更改为 PSCustomObject。在实践中,这具有将对该 PSCustomObject 所做的任何更新追溯应用到列表中的每个元素的明显效果。

这是一个示例:

 $list  = [System.Collections.Generic.List[object]]::new()
 $PSCustomObject    = [PSCustomObject]@{ count  = 0}
 Foreach ($i in 1..5) {
    $PSCustomObject.count +=1
    $list.Add($PSCustomObject)
 }

预期输出:

PS>$list
    count
    -----
        1
        2
        3
        4
        5

实际输出:

PS>$list
    count
    -----
        5
        5
        5
        5
        5
问题

有什么方法可以得到预期的输出?

限制/附加上下文(如果有帮助)

我正在尝试优化性能,因为我可能会解析非常大的 CSV 文件。这就是为什么我被一个列表困住了。我了解列表中的 Add 方法比使用 += 为每一行重新创建数组更快。我还使用运行空间池分别解析每个字段并通过$list.$field[$lineNumber] = <field value> 更新对象,所以这就是为什么我需要一种动态更新 PSCustomObject 的方法。我的代码的更大视图是:

    $out = [hashtable]::Synchronized(@{})
    $out.Add($key, @{'dataSets' = [List[object]]::new() } )    ### $key is the file name as I loop through each csv in a directory.
    $rowTemplate = [PSCustomObject]@{rowNum = 0}

    ### Additional steps to prepare the $out dictionary and some other variables
    ...
    ...
    try {
        ### Skip lines prior to the line with the headers
        $fileParser = [System.IO.StreamReader]$path
        Foreach ( $i in 1..$headerLineNumber ) {
            [void]$fileParser.ReadLine()
        }
        ### Load the file into a variable, and add empty PSCustomObjects for each line as a placeholder.
        while ($null -ne ($line = $fileParser.ReadLine())) { 
            [void]$fileContents.Add($line)
            $rowTemplate.RowNum += 1
            [void]$out.$key.dataSets.Add($rowTemplate)
        }
    }
    finally {$fileParser.close(); $fileParser.dispose()}
    ### Prepare the script block for each runspace
    $runspaceScript = {
        Param( $fileContents, $column, $columnIndex, $delimiter, $key, $out )
        $columnValues   = [System.Collections.ArrayList]::new()
        $linecount      = 0

        Foreach ( $line in $fileContents) {

            $entry = $line.split($delimiter)[$columnIndex]
            $out.$key.dataSets[$linecount].$column = $entry
            $linecount += 1
        }
    }
    ### Instantiate the runspace pool.

PS 版 (5.1.19041)

【问题讨论】:

  • 简而言之:将 .NET 引用类型的实例(例如 [pscustomobject])添加到集合会向该实例添加 reference,如果您尝试重用该实例并多次添加它,所有此类集合条目都指向完全相同的实例,这反映了对其执行的最新修改。有关详细信息和解决方案,请参阅链接的副本。
  • 感谢编码逻辑背后的解释。我不知道这只是一个参考,但它是有道理的。
  • 很高兴听到这个消息,布莱森。至于保持模板哈希表中条目(属性)的顺序:请参阅 Mathias 的更新重用 [ordered] 哈希表。
  • 我在哈希表中看到了这一点。我希望将它传送到格式表中以水平显示键(或注释属性),这似乎不适用于哈希表。所以我想我失去了订单。
  • 如果您不介意支付转换费用,您可以随时将您的 ordered 哈希表转换为[pscustomobject](如答案)以获得所需的格式;例如$oht = [ordered] @{ one = 1; two = 2; three = 3 }; [pscustomobject] $oht(您还可以显式使用 Format-Table,但默认情况下,您最多可以使用 4 个属性来获取表格格式)。

标签: powershell powershell-5.1


【解决方案1】:

您一遍又一遍地(重新)将 same 对象添加到列表中。

您需要在每次循环运行时创建一个新对象,但您仍然可以“模板化”对象 - 只需使用哈希表/字典而不是自定义对象:

# this hashtable will be our object "template"
$scaffold = @{ Count = 0}

foreach($i in 1..5){
  $scaffold.Count += 1
  $newObject = [pscustomobject]$scaffold

  $list.Add($newObject)
}

作为mklement0 suggests,如果您正在模板化具有多个属性的对象,您可能需要考虑使用有序字典来保留属性的顺序:

# this hashtable will be our object "template"
$scaffold = [ordered]@{ ID = 0; Count = 0}

foreach($i in 1..5){
  $scaffold['ID'] = Get-Random
  $scaffold['Count'] = $i
  $newObject = [pscustomobject]$scaffold

  $list.Add($newObject)
}

【讨论】:

  • @mklement0 分享你的秘密欺骗目标列表 :)
  • :) 恐怕它在我的私人笔记收藏中,并且基于对我有意义的关键字。 +1 用于哈希表作为模板的想法,但我建议使用[ordered] 来维护属性顺序。
  • 感谢闪电般的快速响应。这确实修复了我提出的样本。不过,由于某种原因,它不适用于我的较大脚本。我添加了$newObject = [$PSCustomObject]$rowTemplate,然后是$out.$key.dataSets.Add($newObject)。然后我在while循环之后输出$out.$key.datasets,但它有同样的错误行为。
  • 好的,$rowTemplate = $rowTemplate.psobject.Copy() 代替 $newObject = [PSCustomObject]$scaffold 有效。
  • @MathiasR.Jessen 我确实也忘记了这样做。谢谢您的帮助。现在我有两种方法来解决它:)
猜你喜欢
  • 2011-01-23
  • 1970-01-01
  • 2015-07-04
  • 2017-02-20
  • 1970-01-01
  • 2014-06-14
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
相关资源
最近更新 更多