Поиск совпадений в массивах объектов в Powershell - PullRequest
3 голосов
/ 04 августа 2010

Я использую пользовательские объекты для хранения имени и схемы из набора объектов 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
        }
    }
}

Есть ли лучший / быстрый / чище способ сделать это? Первоначально, когда я просто работал с массивами строк, я мог просто циклически проходить по одному из массивов и использовать -contains на втором, но с объектами, которые кажутся невозможными.

Спасибо!

Ответы [ 2 ]

3 голосов
/ 04 августа 2010

Я думаю, будет лучше, если вы определите условие равенства в методе 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 (эквивалент Enumerable.Intersect метода .NET), которая принимаетвводит конвейер и возвращает заданное пересечение двух последовательностей (те, которые появляются в обеих последовательностях).Имейте в виду, что я не полностью реализовал эту функцию (предполагается, что каждый элемент в коллекции, указанной 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
2 голосов
/ 04 августа 2010

Вы можете сжать это до однострочного, что было бы уместно, если бы вы писали это на консоли:

$filtered = $SQLObjects1 | ? {$o1=$_; $SQLObjects2 | ? {$_.Name -eq $o1.Schema `
                                                   -and $_.Name -eq $o1.Schema}}

Но в сценарии я бы расширил его так, как будто он у вас есть. Так читается лучше.

...