I'm using custom objects to hold the name and schema from a set of SQL Server objects. I put the objects into an array, then I get another set of objects and put those into another array. What I'd like to do now is find all exact matches between the two arrays.
I'm currently using this:
$filteredSQLObjects = @()
foreach ($SQLObject1 in $SQLObjects1)
{
foreach ($SQLObject2 in $SQLObjects2)
{
if ($SQLObject1.Name -eq $SQLObject2.Name -and
$SQLObject1.Schema -eq $SQLObject2.Schema)
{
$filteredSQLObjects += $SQLObject1
}
}
}
Is there a better/faster/cleaner way to do this? Originally when I was just working with arrays of strings I could just loop through one of the arrays and use -contains on the second, but with objects that doesn't seem possible.
Thanks!
I think its better if you define the equality condition in an IsEqualTo
method on your custom object. So something like this:
$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))
}
Then you can either do a one-liner like Keith showed us, or just do your double foreach
iteration. Whichever you think is more readable:
$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, for a start, you can't add an Equals
member because it already exists on System.Object
(doh!). So I guess IsEqualTo
will have to do instead.
What you can do is define your own function called Intersect-Object
(the equivalent of .NET's Enumerable.Intersect
method) which accepts pipeline input and returns the set intersection of two sequences (the ones that appear in both sequences). Be aware that I haven't fully-implemented this function (assumes each item in the collection specified by Sequence
has an IsEqualTo
method, doesn't check for duplicates before adding to $filteredSequence
etc), but I hope you get the idea.
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
}
}
Then your double foreach
loop turns into this:
$filteredSQLObjects = $SQLObjects1 | Intersect-Object -Sequence $SQLObjects2