Question

J'utilise des objets personnalisés pour contenir le nom et le schéma d'un ensemble d'objets SQL Server. Je mets les objets dans un tableau, je reçois un autre ensemble d'objets et de mettre ceux dans un autre tableau. Ce que je voudrais faire est maintenant trouver toutes les correspondances exactes entre les deux tableaux.

J'utilise actuellement ceci:

$filteredSQLObjects = @()

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

Y at-il une meilleure / plus rapide / plus propre façon de le faire? À l'origine, quand je viens de travailler avec des tableaux de chaînes que je pouvais boucle à travers l'un des tableaux et utiliser -contains sur la seconde, mais avec des objets qui ne semble pas possible.

Merci!

Était-ce utile?

La solution

Je pense que mieux si vous définissez la condition de l'égalité dans une méthode IsEqualTo sur votre objet personnalisé. Donc, quelque chose comme ceci:

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

Ensuite, vous pouvez soit le faire en une ligne comme Keith nous a montré, ou faire simplement son double itération de foreach. Quel que soit vous pensez est plus lisible:

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

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

EDIT

OK, pour commencer, vous ne pouvez pas ajouter un membre de Equals parce qu'il existe déjà sur System.Object (doh!). Donc, je suppose que IsEqualTo devra faire à la place.

Ce que vous pouvez faire est de définir votre propre fonction appelée Intersect-Object (l'équivalent de .NET de Enumerable.Intersect procédé ) qui accepte l'entrée en pipeline et retourne l'intersection de deux séquences (ceux qui apparaissent dans les deux séquences). Sachez que je ne l'ai pas entièrement mis en œuvre cette fonction (chaque élément prend dans la collection spécifiée par Sequence a une méthode IsEqualTo, ne vérifie pas les doublons avant d'ajouter à $filteredSequence etc), mais j'espère que vous avez l'idée.

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

Ensuite, votre double boucle de foreach se transforme en ceci:

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

Autres conseils

Vous pouvez condenser cela en une ligne qui serait approprié si vous écrivez ceci à la console:

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

Mais dans un script, je l'étendre sur comme vous l'avez. Il est plus facile à lire de cette façon.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top