Compare 2 arrays with powershell - powershell

I have 2 Arrays $UsersGroup and $UsersActive, i need to find where in $UsersActive i have a line with SamAccountName and the ObjectGUID .
$UsersGroup =
SamAccountName ObjectGUID
-------------- ----------
XXXX00XX 0031e949-9120-4df1-bddb-98067a141448
XXXX01XX 0031e949-9120-4df1-bdgb-99067a141448
XXXX02XX 0031e949-9120-4df1-bdab-97067a141448
and without headers
$UsersActive =
fcb483fa146b
fcb515739a2f
fcb82f1ef74c
fcc5ee8b8722
fcd3f1f471c2
fceb26a598a3
fd0b14cecd0e
98067a141448
I need to have the match user from $UsersActive to $UserGroup.Object like that
$UsersGroup | ForEach-Object {if($_.ObjectGUID -contains $UsersActive) {$_}}
But i don't get the result like that :
XXXX00XX 0031e949-9120-4df1-bddb-98067a141448
Can some one help me , thanks !

-contains is a collection containment operator, testing for the exact occurrence of the right-hand side argument in the left-hand side argument.
To test for the presence of a substring in a string, use the -like wildcard string comparison operator:
$UsersGroup | Where-Object {
$guid = $_.ObjectGUID
$UsersActive.Where({$guid -like "*$_*"}, 'First')
}
Each group entry will now be tested against every $UsersActive value until a match is found (causing Where-Object to pass the object through) or no match is found (causing Where-Object to filter out the object)

If I understand you correctly, using compare-object... This has only one match. Compare-object can be slow for large lists.
$usersgroup =
'SamAccountName,ObjectGUID
XXXX00XX,0031e949-9120-4df1-bddb-98067a141448
XXXX01XX,0031e949-9120-4df1-bdgb-99067a141448
XXXX02XX,0031e949-9120-4df1-bdab-97067a141448' | convertfrom-csv
$usersactive = -split
'fcb483fa146b
fcb515739a2f
fcb82f1ef74c
fcc5ee8b8722
fcd3f1f471c2
fceb26a598a3
fd0b14cecd0e
98067a141448'
compare ($usersgroup.objectguid -replace '.*-') $usersactive -IncludeEqual |
? sideindicator -eq '==' # order doesn't matter
InputObject SideIndicator
----------- -------------
98067a141448 ==

To offer an alternative solution:
As Mathias states in his answer, the -contains operator and its operands-reversed counterpart, -in, only perform equality comparison of a single comparison value against the elements of a collection (array).
As an aside: Given that $_.ObjectGUID is the scalar (single object) and $UsersActive the collection (array) to search in, you would have needed to use -in, not -contains ($_.ObjectGUID -in $UsersActive)
Unfortunately, as of version 7.3.2, PowerShell's pattern-matching operators, -like (with wildcard expressions) and -match (with regexes), do not support matching against multiple (an array of) patterns.
However, since you're looking to match a literal substring of each .ObjectGUID value, you can use -in if you extract that relevant substring first and use it as the comparison value:
# -> Object whose GUID is '0031e949-9120-4df1-bddb-98067a141448'
$usersGroup | Where-Object { ($_.ObjectGUID -split '-')[-1] -in $usersActive }
Note how the -split operator is used to split the GUID into tokens by -, with [-1] returning the last token, which -in then looks for by equality comparison in the $UsersActive array.
As an aside:
Allowing multiple patterns as the RHS of -like and -match - so as to return $true if any of them match - would be a helpful future improvement.
GitHub issue #2132 asks for just that.

Related

Powershell Select-Object Property multiple comparisons same property

I am using Select-Object to filter a CSV to get the necessary columns.
When I use this: $Filter = $Csv | Select-Object ($Csv[0].PSObject.Properties.Name -like "*Results*" it filters all columns and displays everything containing results, this works fine. But how do I get it to keep my first column where the header is "Sample" as well as keeping the results? I have tried without success:
$Filter = $Csv | Select-Object ($Csv[0].PSObject.Properties.Name -like "*Results*" -and $Csv[0].PSObject.Properties.Name -like "Sample")
I understand you can add multiple properties comma separated but I am looking for the same property but with multiple matching parameters.
The output would include a column that have header name "Sample" and columns that would contain the word "Results". They both work individually in the first line of code provided, but how do i make it work together with both matching strings?
Edit: Expected output added
Select-Object's -Property parameter takes an array of property names (see documentation at Select-Object).
Your individual expressions ($Csv[0].PSObject.Properties.Name -like "*Results*" and $Csv[0].PSObject.Properties.Name -like "Sample") each individually return an array of matching columns as expected, but when you -and them together it becomes a boolean expression that returns $true or $false so your Select-Object becomes the equivalent of:
$Filter = $Csv | Select-Object -Property #( $true )
To demonstrate this, we'll use some sample data:
$csv = #"
Sample, Sample2, Something Else, A1Results, ResultsZ2
aaa, bbb, ccc, ddd, eee
"#
$data = $csv | ConvertFrom-Csv
and then see what your individual expressions return:
PS> ($data[0].PSObject.Properties.Name -like "*Results*")
A1Results
ResultsZ2
PS> ($data[0].PSObject.Properties.Name -like "Sample")
Sample
and now we'll try your combined expression:
PS> ($data[0].PSObject.Properties.Name -like "*Results*") -and ($data[0].PSObject.Properties.Name -like "Sample")
True
If you want to generate the list of all matching columns you'll need to combine the two separate lists in a different way - you can add them together and PowerShell will return a new array that contains the two separate arrays concatenated:
PS> ($data[0].PSObject.Properties.Name -like "*Results*") + ($data[0].PSObject.Properties.Name -like "Sample")
A1Results
ResultsZ2
Sample
and then if you plug that back into your original code you get this:
PS> $data | Select-Object ( ($data[0].PSObject.Properties.Name -like "*Results*") + ($data[0].PSObject.Properties.Name -like "Sample") )
A1Results ResultsZ2 Sample
--------- --------- ------
ddd eee aaa
Cheeky Update
#mklement0 notes in their answer that the left-hand side of the + needs to be an array in order for the addition operator to trigger array concatenation. You can ensure this by coercing the result of the individual expressions into arrays using the Array Subexpression Operator like this:
PS> $data | Select-Object ( #($data[0].PSObject.Properties.Name -like "*Results*") + #($data[0].PSObject.Properties.Name -like "Sample") )
In order to pass multiple property (column) names to the (possibly positionally implied) -Property parameter of the Select-Object cmdlet using an expression ((...)), you must pass a flat array.
To that end, use + for concatenation, making sure that (at least) the LHS is an array.
The following places the Sample property first, using #(...), the array-subexpression operator to wrap it in an array:
$Csv |
Select-Object (#('Sample') + ($Csv[0].PSObject.Properties.Name -like "*Results*"))

Powershell: Comparing Array (-notcontains) will not work

I want to compare two Lists of Arrays and to display the Delta in a third array:
$ListOfVMs
$ListOfRunningVMs
$StoppedVMs = $ListOfVMs | { Where-Object $_.Name -notcontains $ListOfVMs.Name }
This Filter delivers still the complete Content of $ListOfVMs and not only the Delta. What I am doing wrong?
You may do the following:
$StoppedVMs = $ListOfVMs | Where-Object Name -notin $ListOfRunningVMs.Name
You need to pipe to Where-Object. Where-Object is what contains the script block (if you need to use it). You are also not comparing both lists here as you only reference $ListOfVMs.
Since you are comparing a single item against a collection, you will want to use -notin if the single item is on the left-hand side (LHS). -notcontains would be used if the collection is on the LHS.

Rewrite powershell file comparison without loops or if statements

What's the best way to rewrite the following powershell code that compares a list of two files, ensuring they have the same (or greater) file count, and that the second list contains every file in the first list:
$noNewFiles = $NewFiles.Count -ge $OldFiles.Count
foreach ($oldFile in $OldFiles){
if (!$NewFiles.Contains($oldFile)) {
return $false
}
}
PSv3+ syntax (? is a built-in alias for Where-Object cmdlet):
(Compare-Object $NewFiles $OldFiles | ? SideIndicator -eq '=>').Count -eq 0
More efficient PSv4+ alternative, using the Where() method (as suggested by Brian (the OP) himself):
(Compare-Object $NewFiles $OldFiles).Where-Object({ $_.SideIndicator -eq '=>' }).Count -eq 0
By default, Compare-Object only returns differences between two sets and outputs objects whose .SideIndicator property indicates the set that an element is unique to:
Since string value => indicates an element that is unique to the 2nd set (RHS), we can filter the differences down to elements unique to the 2nd set, so if their count is 0, the implication is that there are no elements unique to the 2nd set.
Side note:
How "sameness" (equality) is determined depends on the data type of the elements.
A pitfall is that instances of reference types are compared by their .ToString() values, which can result in disparate objects being considered equal. For instance, Compare-Object #{ one=1 } #{ two=2 } produces no output.

Find Exchange mailboxes that are forwarded to a Distribution List

Trying to figure out a way to find all the mailboxes that are forwarded to a Distribution List.
No luck with this, any one has better idea?
$group=Get-DistributionGroup | select PrimarySmtpAddress
Get-Mailbox | Where-Object { $_.ForwardingAddress -eq "$group" } | Select-Object Name,ForwardingAddress
Untested (PSv2+; PSv3+ would allow for simpler syntax):
$groupEmailAddresses = Get-DistributionGroup | foreach { $_.PrimarySmtpAddress.ToString() }
Get-Mailbox | where { $_.ForwardingSmtpAddress -and
$groupEmailAddresses -contains $_.ForwardingSmtpAddress.ToString() } |
Select-Object Name, ForwardingSmtpAddress
Note that while many PowerShell operators can operate on arrays (collections),
the array must be on the LHS (left-hand side)
and the RHS (right-hand side) must be a scalar (a non-collection value)
In the case at hand, -contains tells us whether the collection on the LHS has the single RHS value among its elements.

Check if object is in a list of objects

As PowerShell has many SQL query-like cmdlets, is there a fast way to check if object is in list of other objects with the Where-Object cmdlet?
Something like in SQL:
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1,value2,...)
Of course I can write a simple subroutine, but I was just wondering if there is such feature.
You can use the -contains operator:
Get-ColumnNames $table | Where-Object { value1,value2,... -contains $_ }
It's backwards, though with the collection of values on the left side.
In PowerShell 3 you can also use the -in operator:
Where-Object { $_ -in value1,value2,... }
or even
Where-Object -In value1,value2,...
Also, a quirk of how PowerShell works with comparison operators, you can apply them directly to a collection on the left side:
Where-Object { value1,value2,... -eq $_ }
The -eq operator here will either yield the respective element if it is in the list, or $null (which coerces to $false).