Pergunta

Estou usando objetos personalizados para manter o nome e o esquema de um conjunto de objetos SQL Server. Coloquei os objetos em uma matriz, depois recebo outro conjunto de objetos e os coloco em outra matriz. O que eu gostaria de fazer agora é encontrar todas as correspondências exatas entre as duas matrizes.

Estou usando isso atualmente:

$filteredSQLObjects = @()

foreach ($SQLObject1 in $SQLObjects1)
{
    foreach ($SQLObject2 in $SQLObjects2)
    {
        if ($SQLObject1.Name   -eq $SQLObject2.Name -and
            $SQLObject1.Schema -eq $SQLObject2.Schema)
        {
            $filteredSQLObjects += $SQLObject1
        }
    }
}

Existe uma maneira melhor/mais rápida/limpa de fazer isso? Originalmente, quando eu estava apenas trabalhando com matrizes de cordas, eu podia fazer uma percorrer por uma das matrizes e usar contém no segundo, mas com objetos que não parecem possíveis.

Obrigado!

Foi útil?

Solução

Eu acho melhor se você definir a condição de igualdade em um IsEqualTo Método em seu objeto personalizado. Então, algo assim:

$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))
}

Então você pode fazer uma liner como Keith nos mostrou, ou apenas fazer o seu dobro foreach iteração. O que você acha que é mais legível:

$filteredSQLObjects = $SQLObjects1 | Where-Object { $SQLObject1 = $_; $SQLObjects2 | Where-Object { $_.IsEqualTo($SQLOBject1) } }

foreach ($SQLObject1 in $SQLObjects1)
{
    foreach ($SQLObject2 in $SQLObjects2)
    {
        if ($SQLObject1.IsEqualTo($SQLObject2))
        {
            $filteredSQLObjects += $SQLObject1
        }
    }
}

EDITAR

Ok, para começar, você não pode adicionar um Equals membro porque já existe em System.Object (Doh!). Então eu acho IsEqualTo terá que fazer.

O que você pode fazer é definir sua própria função chamada Intersect-Object (o equivalente ao .NET's Enumerable.Intersect método) que aceita a entrada do pipeline e retorna a interseção de duas seqüências (as que aparecem nas duas sequências). Esteja ciente de que não implementei totalmente essa função (assume cada item na coleção especificada por Sequence tem um IsEqualTo método, não verifica duplicatas antes de adicionar a $filteredSequence etc), mas espero que você entenda a ideia.

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
    }
}

Então você é duplo foreach Loop se transforma nisso:

$filteredSQLObjects = $SQLObjects1 | Intersect-Object -Sequence $SQLObjects2

Outras dicas

Você pode condensar isso a uma linha que seria apropriada se estivesse escrevendo isso no console:

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

Mas em um roteiro, eu o expandiria como você. É mais legível dessa maneira.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top