Найти спички в массивах объектов в PowerShell
-
25-09-2019 - |
Вопрос
Я использую пользовательские объекты для удержания имени и схемы от набора объектов SQL Server. Я положил объекты в массив, тогда я получаю еще один набор объектов и помещаю их в другой массив. То, что я хотел бы сделать сейчас, найдет все точные совпадения между двумя массивами.
В настоящее время я использую это:
$filteredSQLObjects = @()
foreach ($SQLObject1 in $SQLObjects1)
{
foreach ($SQLObject2 in $SQLObjects2)
{
if ($SQLObject1.Name -eq $SQLObject2.Name -and
$SQLObject1.Schema -eq $SQLObject2.Schema)
{
$filteredSQLObjects += $SQLObject1
}
}
}
Есть ли лучший / бывший / более чистый способ сделать это? Первоначально, когда я просто работал с массивами струн, я мог бы просто петить через одну из массивов и использовать - точно во втором, но с объектами, которые не кажутся возможными.
Спасибо!
Решение
Я думаю, что лучше, если вы определите состояние равенства в IsEqualTo
Метод на вашем пользовательском объекте. Так что-то вроде этого:
$myObject = New-Object PSObject
$myObject | Add-Member -MemberType NoteProperty -Name Name -Value $name
$myObject | Add-Member -MemberType NoteProperty -Name Schema -Value $schema
$myObject | Add-Member -MemberType ScriptMethod -Name IsEqualTo -Value {
param (
[PSObject]$Object
)
return (($this.Name -eq $Object.Name) -and ($this.Schema -eq $Object.Schema))
}
Тогда вы можете сделать один вкладыш, подобный Кит, показал нам, или просто сделаю свой двойной foreach
Итерация. Что бы вы ни думали, больше читабели:
$filteredSQLObjects = $SQLObjects1 | Where-Object { $SQLObject1 = $_; $SQLObjects2 | Where-Object { $_.IsEqualTo($SQLOBject1) } }
foreach ($SQLObject1 in $SQLObjects1)
{
foreach ($SQLObject2 in $SQLObjects2)
{
if ($SQLObject1.IsEqualTo($SQLObject2))
{
$filteredSQLObjects += $SQLObject1
}
}
}
РЕДАКТИРОВАТЬ
Хорошо, для начала, вы не можете добавить Equals
член, потому что он уже существует на System.Object
(Дох!). Так что я думаю IsEqualTo
придется сделать вместо этого.
Что вы можете сделать, это определить свою собственную функцию под названием Intersect-Object
(эквивалент .NET's's Enumerable.Intersect
Метод), который принимает ввод трубопровода и возвращает множество пересечений двух последовательностей (те, которые появляются в обеих последовательностях). Помните, что я не полностью реализовал эту функцию (предполагает каждый элемент в коллекции, указанном Sequence
имеет АН IsEqualTo
Метод, не проверяет дубликаты перед добавлением к $filteredSequence
и т. Д.), Но я надеюсь, что вы получите идею.
function Intersect-Object
{
param (
[Parameter(ValueFromPipeline = $true)]
[PSObject]$Object,
[Parameter(Mandatory = $true)]
[PSObject[]]$Sequence
)
begin
{
$filteredSequence = @()
}
process
{
$Sequence | Where-Object { $_.IsEqualTo($Object) } | ForEach-Object { $filteredSequence += $_ }
}
end
{
return $filteredSequence
}
}
Тогда ваш двойной foreach
петля превращается в это:
$filteredSQLObjects = $SQLObjects1 | Intersect-Object -Sequence $SQLObjects2
Другие советы
Вы можете конденсироваться на одноклассника, что было бы уместно, если вы пишете это на консоли:
$filtered = $SQLObjects1 | ? {$o1=$_; $SQLObjects2 | ? {$_.Name -eq $o1.Schema `
-and $_.Name -eq $o1.Schema}}
Но в скрипте я бы расширил его, как у вас есть. Это более читается таким образом.