Сравните несколько элементов в объекте с несколькими элементами в другом объекте другого массива - PullRequest
1 голос
/ 01 мая 2020

Скажем [гипотетически], у меня есть два .CSV, которые я сравниваю, чтобы попытаться увидеть, какие из моих текущих членов являются исходными членами ... Я написал вложенный объект ForEach, сравнивающий все $ name и $ memberNumber из каждого объекта с любой другой объект. Он отлично работает, но занимает много времени, тем более что в каждом CSV есть десятки тысяч объектов. Есть ли другой способ, которым я должен подойти к этому?

Original_Members.csv

Имя, Member_Number

Алиса, 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

1 Ответ

1 голос
/ 01 мая 2020

Предполагая, что оба ваших файла похожи на приведенные ниже:

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

Обновление

В соответствии с запросом в комментариях, если мы хотим проверить и Name и Member_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
...