我有一个关于在PowerShell中使用Linq的问题。我不知道如何正确使用Except
方法
示例表:
$Arr = 1..1000
$Props = ("employeeID","FindName1","FindName2")
$Table1 = New-Object System.Data.DataTable "Table1"
$Props | ForEach-Object { $Table1.Columns.Add( $_ , [String]) | Out-Null }
ForEach ($Record in $Arr ) {
$Row = $Table1.NewRow()
$Row.employeeID = $Record.ToString("00000")
$Row.FindName1 = "UserName_" + $Record.ToString()
$Row.FindName2 = "String_" + $Record.ToString("00000000")
$Table1.Rows.Add($Row)
}
$Arr2 = 980..1111
$Props = ("employeeID","FindName1")
$Table2 = New-Object System.Data.DataTable "Table2"
$Props | ForEach-Object { $Table2.Columns.Add( $_ , [String]) | Out-Null }
ForEach ($Record in $Arr2 ) {
$Row = $Table2.NewRow()
$Row.employeeID = $Record.ToString("00000")
$Row.FindName1 = "UserName_" + $Record.ToString()
$Table2.Rows.Add($Row)
}
字符串
作为工作的结果,我想从$table1
中获取记录,其中FindName1不在$Table2.FindName1
中,保留所有头
尝试执行不会产生预期的结果。
$ExceptOut = [System.Linq.Enumerable]::Except($Table1.FindName1, $Table2.FindName1)
型
正如我从article理解的那样,我需要创建自己的类,其中包含允许我在表中使用LINQ的方法。但我离编程非常远。或者可能在SQL中有一些"NOT IN"
的其他快速模拟。我希望得到帮助。谢谢。
2条答案
按热度按时间64jmpszr1#
为了使(通用的)set-difference
.Except()
LINQ method工作,作为参数传递的两个枚举(IEnumerable<T>
)必须:T
Object
用于T
,从而有效地支持PowerShell的 * 潜在混合类型 * 常规Object[]
数组(PowerShell的类型文字表示法中的[object[]]
)。IEquatable<T>
接口和/或覆盖.Equals()
方法(因此也覆盖.GetHashCode()
方法)。PowerShell似乎无法为
.Except()
找到正确的重载,[object[]]
数组由$Table1.FindName1
和$Table2.FindName1
返回(也请参阅下面的注解re v7.3+),尽管这些数组在技术上满足上述要求-我不知道为什么。然而,简单地将其中一个数组强制转换为它已经是的
[object[]]
-解决了这个问题:字符串
注意事项:
[object[]]
就足够了。型
.FindName1
列最终包含 strings,您还可以将- both - enumerables转换为[string[]]
,尽管这隐式地创建了每个数组的 * 副本 *,但在这里没有必要。现在,如果你想**返回 * 整行 *,而只使用
.FindName1
列进行 * 比较 *,事情会变得复杂得多:IEqualityComparer[T]
接口。.Rows
集合强制转换为IEnumerable[DataRow]
,这需要通过反射调用System.Linq.Enumerable.Cast()
方法 *(同样,请参阅下面更简单的v7.3+解决方案)。[DataRow[]]
,但这会导致将行集合转换为数组的效率低下。下面是一个PSv 5+解决方案,它将自定义比较器类实现为PowerShell类:
型
PowerShell 7.3+简化:
直接指定泛型方法类型参数的能力使基于反射的方法变得不必要,并简化了方法:
型
注意:仍然需要
[Linq.Enumerable]::Cast[Data.DataRow]()
调用,因为System.Data.DataTable
示例的.Rows
属性仅实现IEnumerable
,而不是IEnumerable[System.Data.DataRow]
。GitHub issue #2226建议让LINQ成为一级PowerShell公民。
zzlelutf2#
要使用本机PowerShell解决方案补充LINQ-based answer,请执行以下操作:
Compare-Object
cmdlet允许您比较集合,但请注意,虽然它更简洁,但它也比基于LINQ的解决方案慢得多:字符串
[Data.DataRow[]]
-从rows集合创建一个新数组-似乎需要Compare-Object
将行识别为可重复的。.GetEnumerator()
或转换为Collections.IEnumerable
没有帮助,转换为Collections.Generic.IEnumerable[Data.DataRow]]
失败。-Property FindName1
指定比较属性,即用来比较行的属性。-PassThru
需要使Compare-Object
按原样输出输入对象,而不是只包含-Property
指定的属性的自定义对象。.SideIndicator
NoteProperty成员装饰的,但是,使用PowerShell的ETS(扩展类型系统)-请参阅下文。Compare-Object
输出的输入对象对于 either 集合是唯一的,则Where-Object SideIndicator -eq '<='
必须用于将结果限制为对于LHS输入集合是唯一的那些差异对象(通过'<='
的.SideIndicator
属性值表示-箭头指向对象唯一的那一侧)。GitHub issue #4316提出了对
Compare-Object
小程序的许多改进,这有助于简化和加速上述解决方案。也就是说,make LINQ a first-class PowerShell citizen, #2226的提议有更多的希望。