【问题标题】:Compare multiple elements in an object against multiple elements in another object of a different array将一个对象中的多个元素与不同数组的另一个对象中的多个元素进行比较
【发布时间】:2020-08-16 06:22:58
【问题描述】:

说[假设地],我有两个 .CSV 我正在比较尝试看看我当前的哪些成员是原始成员...我写了一个嵌套的 ForEach-Object 比较每个对象中的每个 $name 和 $memberNumber所有其他对象。它工作得很好,但需要很长时间,特别是因为每个 CSV 都有成千上万个对象。我还有其他方法可以解决这个问题吗?

Original_Members.csv

姓名、成员编号

爱丽丝,1234

吉姆,4567

Current_Members.csv

爱丽丝,4599

吉姆,4567

$currentMembers = import-csv $home\Desktop\current_members.csv |

ForEach-Object {
    $name = $_.Name      
    $memNum = $_."Member Number"

    $ogMembers = import-csv $home\Desktop\original_members.csv" |
        ForEach-Object {
            If ($ogMembers.Name -eq $name -and $ogMembers."Member Number" -eq $memNum) {
                $ogMember = "Yes"
            }  
            Else {
                $ogMember = "No"
            }
        }
            [pscustomobject]@{
            "Name"=$name
            "Member Number"=$memNum
            "Original Member?"=$ogMember
            }
} |

select "Name","Member Number","Original Member?" |
Export-CSV "$home\Desktop\OG_Compare_$(get-date -uformat "%d%b%Y").csv" -Append -NoTypeInformation

【问题讨论】:

  • 显示你的嵌套 foreach 代码
  • 将较大的转换为哈希表,然后将其用作查找表。如果需要,键可以是 .Name 或两个属性作为字符串。 ///// 你可能还想看看使用哈希集。
  • 它们都有不同的(附加)列。我有办法只在哈希表/数组中包含某些列吗?

标签: powershell import-csv foreach-object


【解决方案1】:

假设您的两个文件都如下所示:

Original_Members.csv

Name, Member_Number
Alice, 1234
Jim, 4567

Current_Members.csv

Name, Member_Number
Alice, 4599
Jim, 4567

您可以将原始成员名称存储在 System.Collections.Generic.HashSet<T> 中以进行恒定时间查找,而不是对每个名称进行线性搜索。我们可以使用System.Linq.Enumerable.ToHashSet 来创建string[] 名称的哈希集。

然后我们可以使用Where-Object 来过滤当前名称,方法是使用System.Collections.Generic.HashSet<T>.Contains(T) 检查哈希集是否包含原始名称,这是一个 O(1) 方法。

$originalMembers = Import-Csv -Path .\Original_Members.csv
$currentMembers = Import-Csv -Path .\Current_Members.csv

$originalMembersLookup = [Linq.Enumerable]::ToHashSet(
    [string[]]$originalMembers.Name, 
    [StringComparer]::CurrentCultureIgnoreCase
)

$currentMembers | 
    Where-Object {$originalMembersLookup.Contains($_.Name)}

这将输出作为原始成员的当前成员:

Name  Member_Number
----  -------------
Alice 4599
Jim   4567

更新

根据 cmets 的要求,如果我们想同时检查 NameMember_Number,我们可以连接两个字符串以用于查找:

$originalMembers = Import-Csv -Path .\Original_Members.csv
$currentMembers = Import-Csv -Path .\Current_Members.csv

$originalMembersLookup = [Linq.Enumerable]::ToHashSet(
    [string[]]($originalMembers | 
        ForEach-Object {
            $_.Name + $_.Member_Number
        }), 
    [StringComparer]::CurrentCultureIgnoreCase
)

$currentMembers | 
    Where-Object {$originalMembersLookup.Contains($_.Name + $_.Member_Number)}

现在只会返回:

Name Member_Number
---- -------------
Jim  4567

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多