Trouver des correspondances dans les tableaux d'objets en Powershell
-
25-09-2019 - |
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!
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.