【问题标题】:Find what is different in two very large lists在两个非常大的列表中查找不同之处
【发布时间】:2021-09-30 01:43:02
【问题描述】:

我有两个列表,每个列表大约有 1k 人。 我要做的就是找出两者之间剩下的人。

$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

上面没有产生我想要的。

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

【问题讨论】:

    标签: powershell set-difference compareobject


    【解决方案1】:

    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 意味着报告结果没有特定顺序;要将它们按排序顺序排列,请改用[System.Collections.Generic.SortedSet[string]]。 (从 .NET 6 开始,没有用于维护 插入顺序 的内置类型)。

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

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

      [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() 方法,但这需要将散列集之一存储在辅助变量中,然后就地更新 - 类似于下面的.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
    

    【讨论】:

    • 这很完美,只是注意到[System.Collections.Generic.HashSet[string]] 也可以处理集合中的重复项而不会抛出任何异常,这非常好。
    • 确实,@SantiagoSquarzon,它创建了一个真实的集合,你也可以重复调用.Add()而不出现异常([bool]返回值表明该元素是否已经存在)。跨度>
    • 很好的问题,@SantiagoSquarzon:你可以 - 请查看我的更新。
    【解决方案2】:

    将每个列表加载到[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 封电子邮件的列表

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-12
      • 2021-06-20
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多