Die Suche nach Übereinstimmungen in Arrays von Objekten in Powershell
-
25-09-2019 - |
Frage
Ich verwende benutzerdefinierte Objekte den Namen und das Schema aus einer Reihe von SQL-Server-Objekten zu halten. Ich habe die Objekte in ein Array, dann habe ich einen anderen Satz von Objekten erhalten und diese in ein anderes Array setzen. Was möchte ich jetzt tun ist, alle genauen Übereinstimmungen zwischen den beiden Feldern finden.
Ich bin das zur Zeit mit:
$filteredSQLObjects = @()
foreach ($SQLObject1 in $SQLObjects1)
{
foreach ($SQLObject2 in $SQLObjects2)
{
if ($SQLObject1.Name -eq $SQLObject2.Name -and
$SQLObject1.Schema -eq $SQLObject2.Schema)
{
$filteredSQLObjects += $SQLObject1
}
}
}
Gibt es eine bessere / schnellere / sauberer Weg, dies zu tun? Ursprünglich, als ich arbeite nur mit Arrays von Strings ich konnte nur eine Schleife durch eines des Arrays und Verwendung -contains auf dem zweiten, aber mit Objekten, die nicht möglich erscheinen.
Danke!
Lösung
Ich denke, es ist besser, wenn Sie die Gleichheitsbedingung in einer IsEqualTo
Methode auf Ihrem benutzerdefiniertes Objekt definieren. So etwas wie folgt aus:
$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))
}
Dann können Sie entweder tun ein Einzeiler wie Keith hat uns gezeigt, oder einfach nur Ihre Doppel foreach
Iteration tun. Unabhängig davon, welche Sie denken, ist besser lesbar:
$filteredSQLObjects = $SQLObjects1 | Where-Object { $SQLObject1 = $_; $SQLObjects2 | Where-Object { $_.IsEqualTo($SQLOBject1) } }
foreach ($SQLObject1 in $SQLObjects1)
{
foreach ($SQLObject2 in $SQLObjects2)
{
if ($SQLObject1.IsEqualTo($SQLObject2))
{
$filteredSQLObjects += $SQLObject1
}
}
}
Bearbeiten
OK, für einen Start, können Sie ein Equals
Mitglied nicht hinzugefügt werden, da es auf System.Object
ist bereits vorhanden (doh!). Also ich denke, IsEqualTo
wird stattdessen zu tun hat.
Was Sie können, ist Ihre eigene Funktion namens Intersect-Object
tun definieren (das Äquivalent von .NET Enumerable.Intersect
Verfahren), das Pipeline-Eingabe und gibt die eingestellte Schnittpunkt von zwei Sequenzen (die, die annimmt, die in beiden Sequenzen auftreten). Seien Sie sich bewusst, dass ich nicht voll umgesetzt haben diese Funktion (jedes Element in der Auflistung von Sequence
eine IsEqualTo
Methode angegeben wird davon ausgegangen hat, sucht nicht nach Duplikate vor der Zugabe zu $filteredSequence
etc), aber ich hoffe, Sie bekommen die Idee.
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
}
}
Dann ist Ihre Doppel foreach
Schleife verwandelt sich in diese:
$filteredSQLObjects = $SQLObjects1 | Intersect-Object -Sequence $SQLObjects2
Andere Tipps
Sie könnte dies zu einem Einzeiler kondensieren, die geeignet wäre, wenn Sie diese an der Konsole geschrieben haben:
$filtered = $SQLObjects1 | ? {$o1=$_; $SQLObjects2 | ? {$_.Name -eq $o1.Schema `
-and $_.Name -eq $o1.Schema}}
Aber in einem Skript, würde ich es erweitern heraus, wie Sie es haben. Es ist besser lesbar auf diese Weise.