linq - 如何在 powershell 中使用 linq explict 或在 SQL 中使用 "NOT IN"的类似物

标签 linq powershell datatable

我有一个关于在 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" 快速模拟。 我希望得到帮助。谢谢。

最佳答案

对于(通用) .Except() LINQ method要工作,作为参数传递的两个枚举 ( IEnumerable<T> ) 必须:

  • 枚举相同类型的实例T
  • 并且,如果该类型是一个引用类型,其实例应该根据实例的内容进行有意义的比较(而不是仅仅通过引用相等,即身份),必须实现 IEquatable<T>接口(interface)和/或覆盖 .Equals()方法。

PowerShell 似乎无法为 .Except() 找到正确的重载与 [object[]] $Table1.FindName1 返回的数组和 $Table2.FindName1 ,尽管这些阵列在技术上满足上述要求 - 我不知道为什么。

但是,只需将这些数组转换为它们已有的样子 - [object[]] - 解决问题:

[Linq.Enumerable]::Except([object[]] $Table1.FindName1, [object[]] $Table2.FindName1)

鉴于 .FindName1列最终包含字符串,您也可以转换为[string[]] ,尽管这隐式地创建了每个数组的副本,这在这里是不必要的。


现在,如果您想在使用 .FindName1返回整行列仅用于比较,事情变得更加复杂:

  • 您必须实现一个自定义比较器类,它实现了 IEqualityComparer[T]界面。

  • 您必须转换 .Rows收集数据表到IEnumerable[DataRow] ,这需要调用 System.Linq.Enumerable.Cast()方法通过反射

    • 注意:虽然您可以直接转换为 [DataRow[]] ,这将涉及将行集合低效地转换为数组。

这是一个将自定义比较器类实现为 PowerShell 类的 PSv5+ 解决方案:

# A custom comparer class that compares two DataRow instances by their
# .FindName1 column.
class CustomTableComparer : Collections.Generic.IEqualityComparer[Data.DataRow] {
  [bool] Equals([Data.DataRow] $x, [Data.DataRow] $y) {
    return [string]::Equals($x.FindName1, $y.FindName1, 'Ordinal')
  }
  [int] GetHashCode([Data.DataRow] $row) {
    # Note: Any two rows for which Equals() returns $true must return the same
    #       hash code. Because *ordinal, case-sensitive* string comparison is
    #       used above, it's sufficient to simply call .GetHashCode() on
    #       the .FindName1 property value, but that would have to be tweaked
    #       for other types of string comparisons.
    return $row.FindName1.GetHashCode();
  }
}


# Use reflection to get a reference to a .Cast() method instantiation 
# that casts to IEnumerable<DataRow>.
$toIEnumerable = [Linq.Enumerable].GetMethod('Cast').MakeGenericMethod([Data.DataRow])

# Call .Except() with the casts and the custom comparer.
# Note the need to wrap the .Rows value in an aux. single-element
# array - (, ...) - for it to be treated as a single argument.
[Linq.Enumerable]::Except(
    $toIEnumerable.Invoke($null, (, $Table1.Rows)), 
    $toIEnumerable.Invoke($null, (, $Table2.Rows)), 
    [CustomTableComparer]::new()
)

GitHub issue #2226提议使 LINQ 成为一流的 PowerShell 公民。

关于linq - 如何在 powershell 中使用 linq explict 或在 SQL 中使用 "NOT IN"的类似物,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54963252/

相关文章:

C# 搜索特定文件夹以外的文件和文件夹

powershell - 使用字符串替换在Powershell中插入字符串 '$_'吗?

powershell - 如何找到 SOAP API 的 WSDL 接口(interface)? -电源外壳

javascript - 如何将 Yajra 数据表中的两列合并为一列

vb.net - 源更改后,Winforms ListBox控件未更新

c# - C# 中基于另一个浮点列表的排序列表

c# - LINQ 到 XML : Collapse mutliple levels to single list

c# - 创建 linq 查询以使用与智能手机相同的方式搜索联系人

powershell - 如何使用powershell打印字符串中的奇数字符

javascript - 通过外部按钮/链接进行数据表过滤