【问题标题】:re-arrange and combine powershell custom objects重新排列和组合 powershell 自定义对象
【发布时间】:2018-11-06 18:01:09
【问题描述】:

我有一个系统,它当前从一个将被替换的单独系统生成的 CSV 文件中读取数据。

导入的 CSV 文件如下所示

PS> Import-Csv .\SalesValues.csv 销售价值 AA BB ------------ -- -- 10 6 5 5 3 4 3 1 9

为了替换这个过程,我希望生成一个看起来与上面的 CSV 相同的对象,但我不想继续使用 CSV 文件。

我已经有一个脚本,可以从我们的数据库中读取数据并提取我需要使用的数据。我不会详细说明在这一点之前的相当长的脚本,但实际上它看起来像这样:

$SQLData = Custom-SQLFunction "SELECT * FROM SALES_DATA WHERE LIST_ID = $LISTID"

$SQLData 将包含 ~5000+ 我需要查询的 DataRow 对象。

其中一个 DataRow 对象看起来像这样:

领导ID:123456789 entry_date : 26/10/2018 16:51:16 修改日期:2018 年 1 月 11 日 01:00:02 状态:错误 用户:mrexample vendor_lead_code : TH1S15L0NGC0D3 source_id : A543212 list_id : 333004 list_name : AA 一些文本 gmt_offset_now:0.00 销售价值 : 10

list_name 将以AABB 为前缀。

SaleValue 可以是 3 及以上的任何整数,但实际上极不可能高于 100(因为这是每月捐赠),并且在绝大多数情况下将是 3、5、10 之一。

我已经有脚本,它获取 list_name 的内容,创建我需要使用的数据并将其填充到两个单独的 psobjects($AASalesValues$BBSalesValues)中,这些 psobjects 整理了整个数据集的“SaleValue”总数。

因为我无法可靠地预测任何 SaleValue 的值,我必须像这样动态创建 psobjects 属性

foreach ($record in $SQLData) {
    if ($record.list_name -match "BB") {
        if ($record.SaleValue -gt 0) {
            if ($BBSalesValues | Get-Member -Name $($record.SaleValue) -MemberType Properties) {
                $BBSalesValues.$($record.SaleValue) = $BBSalesValues.$($record.SaleValue)+1
            } else {
                $BBSalesValues | Add-Member -Name $($record.SaleValue) -MemberType NoteProperty -Value 1
            }
        }
    }    
}

两个结果对象如下所示:

PS> $AASalesValues 10 5 3 50 -- - - -- 17 14 3 1 PS> $BBSalesvalues 3 10 5 4 - - - - 36 12 11 1

我现在有了我需要的数据,但是我需要以 复制 CSV 格式的方式对其进行格式化,以便我可以将其直接传递给另一个配置为的现有 powershell 脚本期望 CSV 格式的数据,但我不想将数据写入文件。

我希望将其直接传递给脚本的下一部分。

最终我想做的是生成一个新的对象/一些输出,看起来像这篇文章顶部的Import-Csv 命令的输出。

我想要一个新对象,比如 $OverallSalesValues,看起来像这样:

PS>$overallSalesValues 销售价值 AA BB 50 1 0 10 17 12 5 14 11 4 0 1 3 3 36

在上面的示例中,$AASalesValues 的值列在 AA 列下,$BBSalesValues 的值列在 BB 列下,行匹配两个原始对象的标题。

我确实尝试过使用哈希表,但我无法弄清楚如何从动态值创建它们并将它们格式化为我需要它们的外观。

【问题讨论】:

  • 销售价值是否独一无二?
  • CSV 是否包含 Sale Values 列中的前导 £?另外,$record 是从哪里来的,为什么要从中构建自定义对象而不是哈希表?
  • $record.SaleValue 上分组不是更好的选择吗?
  • @AnsgarWiechers Hastables 记录一个键和值,我需要多个列(键、值 1、值 2),就像最后一个示例一样。我不明白如何以这种方式构建这些数据。这几乎就是我寻求帮助的原因。
  • @tyteen4a03 SalesValues 将是介于 3 或更大之间的整数。最常见的是 3,5 或 10,但会有异常值(例如,在我的最后一个示例中为 4 或 50),我无法预测它们会是什么。

标签: powershell object hashtable


【解决方案1】:

终于到了。

$TotalList = @()
foreach($n in 3..200){
    if($AASalesValues.$n -or $BBSalesValues.$n){
        $AACount = $AASalesValues.$n
        $BBcount = $BBSalesValues.$n

        $values = [PSCustomObject]@{
            'Sale Value'= $n
            AA = $AACount
            BB = $BBcount
            }
        $TotalList += $values
        }
    }
$TotalList

产生一个输出

Sale Value AA BB
---------- -- --
         3  3 36
         4     2
         5 14 11
        10 18 12
        50  1   

只需要添加一点以包含“0”值而不是 $null。

【讨论】:

    【解决方案2】:

    我将假设$record 包含$AASalesValues$BBSalesValues 的数据库结果列表,而不是两者,否则您需要某种选择器来避免计算一组记录另一组。

    将记录按SaleValue 属性分组为LotPings suggested

    $BBSalesValues = $record | Group-Object SaleValue -NoElement
    

    这将为您提供SaleValue 值及其各自计数的列表。

    PS> $BBSalesValues 计数名称 ----- ---- 36 3 12 10 11 5 1 4

    然后您可以使用以下值更新您的 CSV 数据:

    $file = 'C:\path\to\data.csv'
    
    # read CSV into a hashtable mapping the sale value to the complete record
    # (so that we can lookup the record by sale value)
    $csv = @{}
    Import-Csv $file | ForEach-Object {
        $csv[$_.'Sale Values'] = $_
    }
    
    # Add records for missing sale values
    $($AASalesValues; $BBSalesValues) | Select-Object -Expand Name -Unique | ForEach-Object {
        if (-not $csv.ContainsKey($_)) {
            $csv[$_] = New-Object -Type PSObject -Property @{
                'Sale Values' = $_
                'AA'          = 0
                'BB'          = 0
            }
        }
    }
    
    # update records with values from $AASalesValues
    $AASalesValues | ForEach-Object {
        [int]$csv[$_.Name].AA += $_.Count
    }
    
    # update records with values from $BBSalesValues
    $BBSalesValues | ForEach-Object {
        [int]$csv[$_.Name].BB += $_.Count
    }
    
    # write updated records back to file
    $csv.Values | Export-Csv $file -NoType
    

    即使您更新了问题,方法也几乎相同,您只需添加另一个级别的分组来收集销售数字:

    $sales = @{}
    $record | Group-Object {$_.list_name.Split()[0]} | ForEach-Object {
        $sales[$_.Name] = $_.Group | Group-Object SaleValue -NoElement
    }
    

    然后将合并调整为这样的:

    $file = 'C:\path\to\data.csv'
    
    # read CSV into a hashtable mapping the sale value to the complete record
    # (so that we can lookup the record by sale value)
    $csv = @{}
    Import-Csv $file | ForEach-Object {
        $csv[$_.'Sale Values'] = $_
    }
    
    # Add records for missing sale values
    $sales.Values | Select-Object -Expand Name -Unique | ForEach-Object {
        if (-not $csv.ContainsKey($_)) {
            $prop = @{'Sale Values' = $_}
            $sales.Keys | ForEach-Object {
                $prop[$_] = 0
            }
            $csv[$_] = New-Object -Type PSObject -Property $prop
        }
    }
    
    # update records with values from $sales
    $sales.GetEnumerator() | ForEach-Object {
        $name = $_.Key
        $_.Value | ForEach-Object {
            [int]$csv[$_.Name].$name += $_.Count
        }
    }
    
    # write updated records back to file
    $csv.Values | Export-Csv $file -NoType
    

    【讨论】:

    • 只是想感谢您花时间查看此问题,恐怕我的问题措辞糟糕意味着您的答案不是我想要的。无论如何,我最终自己解决了问题并发布了答案。我已经重写了这个问题,希望以后发现它的人更清楚。最终,我的问题是一种非常冗长的说法,即“我如何根据这些对象做一个自定义哈希表”,我认为额外的细节会有所帮助,但最终它只是混淆了问题。
    • @Patrick 即使您更新了问题,方法仍然大致相同。查看更新的答案。
    猜你喜欢
    • 2016-10-08
    • 1970-01-01
    • 2018-08-15
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 2018-02-09
    • 1970-01-01
    • 2021-09-19
    相关资源
    最近更新 更多