Сравните несколько элементов в одном объекте с несколькими элементами в другом объекте другого массива
Скажем [гипотетически], у меня есть два.CSV, которые я сравниваю, чтобы попытаться увидеть, какие из моих текущих членов являются исходными... Я написал вложенный ForEach-Object, сравнивающий каждое $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 ответ
Предполагая, что оба ваших файла выглядят следующим образом:
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