简体   繁体   English

powershell-在新的属性中合并数组属性

[英]powershell - combine array properties in new one

I have some array with lot of rows (thousands and more). 我有一些带有很多行(数以千计)的数组。 It have columns like 'Column XXXXX ' 它具有“列XXXXX ”列

Row example: 行示例:

Group_name = "proxy_users"
Column1   = "domain\Igor"
Column2   = null
.......
Column989 = 'domain\Andrew'
Column999 = 'domain\Mike'

What is the right and FAST way to create new variable that will be the sum of 'ColumnXXX' ignoring 'null' values? 创建新变量(忽略“空”值将成为“ ColumnXXX”的总和)的正确和快速方法是什么?

Like "domain\\igor, domain\\Andrew, domain\\mike" 就像“域\\ igor,域\\ Andrew,域\\ mike”

I can use smth like $group | 我可以像$ group一样使用smth | select -Property "Column*"...but how to sum and how to ignore null ? 选择-Property“ Column *” ...但是如何求和以及如何忽略null?

You can list all properties using ex. 您可以使用ex列出所有属性。 $_.psobject.properties , filter out the ones you want and use -join to combine the values. $_.psobject.properties ,过滤出所需的内容,然后使用-join组合值。 Ex 防爆

$o = [pscustomobject]@{
    Group_Name = "Test"
    Column1 = "Foo"
    Column2 = $null
    Column3 = "1"
}, [pscustomobject]@{
    Group_Name = "Test2"
    Column1 = $null
    Column2 = "Bar"
    Column3 = "2"
}

#Add property as constant
$o | ForEach-Object {
    $_ | Add-Member -NotePropertyName Summed -NotePropertyValue (($_.psobject.Properties | Where-Object { $_.Name -ne 'Group_name' -and $_.Value -ne 'null' } | Select-Object -ExpandProperty Value) -join '' )
}
$o | ft

Or you can use a ScriptProperty to calculate the value on every call 或者您可以使用ScriptProperty来计算每次调用的值

#Remember to exclude itself to avoid infinite recursion
$o | Add-Member -MemberType ScriptProperty -Name Summed -Value {($this.psobject.Properties | Where-Object { $_.Name -ne 'Group_name' -and $_.Name -ne 'Summed' -and $_.Value -ne 'null' } | Select-Object -ExpandProperty Value) -join '' }
$o | ft

Result: 结果:

Group_Name Column1 Column2 Column3 Summed
---------- ------- ------- ------- ------
Test       Foo             1       Foo1
Test2              Bar     2       Bar2

As an alternative, you can use $_.Name -like 'Column*' -and $_.Value -ne 'null' as the property-filter if they are actually called ColumnXXX . 或者,可以将$_.Name -like 'Column*' -and $_.Value -ne 'null' ColumnXXX $_.Name -like 'Column*' -and $_.Value -ne 'null' ColumnXXX $_.Name -like 'Column*' -and $_.Value -ne 'null'用作属性过滤器(如果它们实际上被称为ColumnXXX

To complement Frode F.'s elegant ScriptProperty-based solution with a more concise PSv4+ version that also performs better due to avoiding the use of cmdlets (a pipeline ) in the property-definition script block in favor of operators (with a large number of input objects, this may matter): 用更简洁的PSv4 +版本来补充Frode F.基于优雅的ScriptProperty的解决方案 ,该版本由于避免了在属性定义脚本块中使用cmdlet( 管道 ),从而使操作员 (具有大量的输入对象,这可能很重要):

$o | Add-Member -PassThru -MemberType ScriptProperty -Name Joined -Value { 
 @($this.psobject.Properties.Where({$_.Name -match 'Column*'}).Value) -ne 'null' -join ', ' 
}

Note the use of: 注意以下用途:

  • The .Where() collection operator (PSv4+), a faster alternative to the Where-Object cmdlet. .Where()集合运算符(PSv4 +),是Where-Object cmdlet的更快替代方法。

  • Member enumeration (PSv3+), where accessing a property at the collection level yields an array of the elements' property values; 成员枚举 (PSv3 +),在集合级别访问属性会生成元素的属性值的数组
    eg, $this.psobject.Properties.Name yields the .Name property values of all elements of the $this.psobject.Properties collection. 例如, $this.psobject.Properties.Name产生$this.psobject.Properties集合的所有元素的.Name属性值。

  • Applying comparison operator -ne to an array-valued LHS, in which case the operator acts as a filter : the operator is applied to each element , and the matching elements are returned as an array; 将比较运算符-ne应用于数组值的 LHS,在这种情况下,该运算符充当过滤器 :将该运算符应用于每个元素 ,然后将匹配元素作为数组返回; note the @(...) around the LHS, which ensures that it is treated as an array even if it happens to return only a single value. 注意LHS周围的@(...) ,即使将其仅返回单个值,也可确保将其视为数组。

With the sample data in the question, the above yields (look for property Joined ): 使用问题中的样本数据,以上结果(查找属性Joined ):

Group_name : proxy_users
Column1    : domain\Igor
Column2    : null
Column989  : domain\Andrew
Column999  : domain\Mike
Joined     : domain\Igor, domain\Andrew, domain\Mike

With the above optimizations, you could even consider a simpler Select-Object solution that constructs new custom-object instances that contain all of the input object's properties ( * ) plus a new calculated property that contains the combined column values of interest: 通过上述优化,您甚至可以考虑使用一个更简单的Select-Object解决方案,该解决方案构造包含输入对象属性( * )的自定义对象实例,以及包含感兴趣的组合列值的新计算属性:

$o | Select-Object *, @{ n='Joined'; e={ 
  @(($_.psobject.Properties.Where({$_.Name -match 'Column*'})).Value) -ne 'null' -join ', ' } 
}

This solution is slower than the one above, but not by much. 此解决方案比上面的解决方案慢,但幅度不大。
A caveat is that if you collect all output in memory, the newly constructed objects take up space in addition to the originals. 需要注意的是,如果您将所有输出收集在内存中,则新构造的对象除了原始对象外还会占用空间。


Optional reading: Performance comparison; 可选阅读:性能比较; the impact of pipeline use: 管道使用的影响:

The code at the bottom times the various approaches from Frode's and my answer. 底部的代码采用了Frode和我的回答的各种方法。

Here's a sample timing from my machine - the absolute numbers aren't important, but their ratios are (though I don't know how factors such as CPU core count and disk speed would come into play) - input set size is 1000 objects: 这是我机器上的一个示例计时-绝对数字并不重要,但是它们的比率是(尽管我不知道CPU核心数量和磁盘速度等因素会如何发挥作用)-输入集大小为1000对象:

Approach                                 TotalSeconds
--------                                 ------------
add-member w/ scriptproperty - operators 0.35
select-object w/ noteproperty            0.40
add-member w/ scriptproperty - pipeline  0.72
add-member w/ noteproperty - pipeline    0.98

Conclusions: 结论:

  • The solutions that use pipelines in the property definition are noticeably slower, by a factor of about 2, which doesn't seem to vary even with larger input counts. 在属性定义中使用管道的解决方案的速度明显慢了约2倍,即使输入数量较大,该解决方案也似乎没有变化。

  • The optimized Select-Object -based solution that creates new objects (rather than adding properties to the input objects) is only slightly slower than the optimized Add-Member solution. 创建对象(而不是向输入对象添加属性)的基于优化Select-Object的解决方案仅比优化Add-Member解决方案稍慢。

Source code for the tests: 测试的源代码:

# The number of input objects to test with.
# Adjust to experiment.
$objCount = 1000

Write-Verbose -Verbose "# of input objects: $objCount"

$ndx = 0; $approaches = 'select-object w/ noteproperty', 
                        'add-member w/ scriptproperty - operators', 
                        'add-member w/ scriptproperty - pipeline', 
                        'add-member w/ noteproperty - pipeline'
$tempFile = New-TemporaryFile # requires PSv5+
$(foreach($approach in $approaches) {

  # Create test collection (recreate it in every iteration to support repeated Add-Member operations)
  $al = [System.Collections.ArrayList]::new()
  for ($i = 0; $i -lt $objCount; ++$i) {
    $null = $al.add([pscustomobject] @{ one = 1; Column1 = 'col1'; Column2 = 'col2'; Column3 = 'null'; Column4 = 'col4' })
  }

  Measure-Command {

    Write-Verbose -Verbose "Running: $approach..."
    switch ($ndx) {
      0 { # select-object w/ noteproperty
        $al | Select-Object *, @{ n='Joined'; e={ @(($_.psobject.Properties.Where({ $_.Name -match 'Column*'})).Value) -ne 'null' -join ', ' } } | 
          Export-Csv $tempFile  
        break
      }

      1 { # add-member w/ scriptproperty - operators
        $al | Add-Member -PassThru -MemberType ScriptProperty -Name Joined -Value { @($this.psobject.Properties.Where({ $_.Name -match 'Column*'}).Value) -ne 'null' -join ', ' } |
          Export-Csv $tempFile          
        break
      }

      2 { # add-member w/ scriptproperty - pipeline
        $al | Add-Member -PassThru -MemberType ScriptProperty -Name Joined -Value { ($this.psobject.Properties | Where-Object { $_.Name -match 'Column*'  -and $_.Value -ne 'null' } | Select-Object -ExpandProperty Value) -join ', ' }  | 
         Export-Csv $tempFile
         break
        }

        3 { # add-member w/ noteproperty - pipeline; requires an intermediate ForEach-Object call
          $al | ForEach-Object {
            $_ | Add-Member -PassThru -NotePropertyName Joined -NotePropertyValue (($_.psobject.Properties | Where-Object { $_.Name -match 'Column*' -and $_.Value -ne 'null' } | Select-Object -ExpandProperty Value) -join ', ' )
          } |
            Export-Csv $tempFile
        break
      }
      default { Throw "What are you doing here?" }
    }

    # Import-Csv $tempFile | Select-Object -First 1 Joined | Write-Verbose -Verbose

    ++$ndx

  } | Select-Object @{ n='Approach'; e={ $approach }}, @{ n='TotalSeconds'; e={ '{0:N2}' -f $_.TotalSeconds } }

}) | Sort-Object { [double] $_.TotalSeconds }

Remove-Item $tempFile

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM