在两个非常大的列表中找出不同之处

Find what is different in two very large lists

我有两个名单,每个名单大约有 1000 人。 我想做的是找出两者之间剩下的人。

$BunchoEmail = Import-Csv C:\temp\Directory.csv | Select-Object primaryEmail -ExpandProperty primaryEmail

$GoogleUsers = gam print users fields suspended | ConvertFrom-Csv | Where-Object suspended -ne $true | Select-Object primaryEmail -ExpandProperty primaryEmail

$objects = @{
    ReferenceObject  = $GoogleUsers
    DifferenceObject = $BunchoEmail
}
Compare-Object @objects

上面没有产生我想要的。

找出不同之处的最佳方法是什么?

将每个列表加载到 [hashtable]:

$emailTable = @{}
$BunchoEmail |ForEach-Object { $emailTable[$_] = $_ }

$gsuiteTable = @{}
$GoogleUsers |ForEach-Object { $gsuiteTable[$_] = $_ }

现在您可以遍历一个列表并检查另一个列表是否不包含任何特定的电子邮件地址 Where-Object:

$notInGSuite = $BunchoEmail |Where-Object { -not $gsuiteTable.ContainsKey($_) }

$notInEmailList = $GoogleUsers |Where-Object { -not $emailTable.ContainsKey($_) }

哈希表 ContainsKey() 的时间复杂度为 O(1),因此它将继续处理包含 1000 封电子邮件的列表

Compare-Object 能够找出一个集合相对于另一个集合缺少哪些元素,反之亦然,或两者兼而有之。

但是,它 可能 ,并且鉴于您提到 列表,听起来您正在寻找性能良好的解决方案。

  • 但是,包含 1,000 个项目的集合在实践中可能不是问题

  • 因此,类似下面的内容可能足以获取 $BunchoEmail 中不在 $GoogleUsers 中的所有条目(用 => 代替 <= 反转逻辑):

    (Compare-Object -PassThru $BunchoEmail $GoogleUsers).
      Where({ $_.SideIndicator -eq '<=' })
    
  • 获取不在 both 集合中的那些条目(对于 either 集合是唯一的)是偶数更简单:

    Compare-Object -PassThru $BunchoEmail $GoogleUsers
    

至于提高性能

将类型 [System.Collections.Generic.HashSet`1] 与 LINQ 相结合可实现快速简洁的解决方案:

注:

  • 使用HashSet意味着结果报告没有特定顺序;要按 sorted 顺序排列它们,请改用 [System.Collections.Generic.SortedSet[string]]。 (从 .NET 6 开始,没有用于维护 插入顺序 的内置类型)。

  • 以下解决方案是真正的 set 操作,即它们报告 distinct 差异,与 Compare-Object 不同.例如,如果唯一电子邮件 foo@example.org 在一个集合中出现 两次 ,下面的解决方案仅报告它 一次 ,而 Compare-Object 报告了这两个实例。

  • Compare-Object不同,HashSetSortedSet类型默认区分大小写;您可以使用 System.StringComparer 将相等比较器传递给 case-insensitive 行为的构造函数;例如:

    [System.Collections.Generic.HashSet[string]]::new(
      [string[]] ('foo', 'FOO'),
      [System.StringComparer]::InvariantCultureIgnoreCase
    )
    

要获取 $BunchoEmail 中不在 $GoogleUsers 中的所有条目,请使用 [System.Linq.Enumerable]::Except()(反转操作数以获得逆解):

[Linq.Enumerable]::Except(
  [System.Collections.Generic.HashSet[string]] $BunchoEmail,
  [System.Collections.Generic.HashSet[string]] $GoogleUsers
)

注意:您也可以使用散列集的 .ExceptWith() 方法,但这需要将其中一个散列集存储在辅助变量中,然后在 中更新place - 类似于下面的 .SymmetricExceptWith() 解决方案。

获取那些不在 both 集合中的条目(对于 either 集合是唯一的,称为 对称差异 在集合中)需要更多的努力,使用散列集合的 .SymmetricExceptWith() 方法:

# Load one of the collections into an auxiliary hash set.
$auxHashSet = [System.Collections.Generic.HashSet[string]] $BunchoEmail

# Determine the symmetric difference between the two sets, which
# updates the calling set in place.
$auxHashSet.SymmetricExceptWith(
  [System.Collections.Generic.HashSet[string]] $GoogleUsers
)

# Output the result
$auxHashSet