powershell comparing multiple arrays - powershell

I've a case where multiple arrays need to be compared.There is one master array that contains all the elements that the child arrays have and also some extra elements.
In below example, $a is the master array and $b, $c are child arrays.
I need to compare these arrays and get the list of those extra elements in $a that are not present in $b and $c.
Practically, in my case there are 10 child arrays and a master array.
$a="dhaw","roh","kohl","faf","abd","steyn","gupt","baz","kane","benn","brendn","joyc"
$b="roh","dhaw","kohl"
$c="steyn","abd","faf","roh","dhaw"

A viable solution could be using -notcontains operator as suggested by arco444, cycle through $a array elements and check if they are contained at least in one of the other arrays.
Here is a slice of code
foreach($a_value in $a) {
if (($b -notcontains $a_value) -and ($c -notcontains $a_value)) {
"$a_value is extra"
}
}

Something like this?
Compare-Object -ReferenceObject $a -DifferenceObject ($b + $c)
If you just want to get raw objects:
(Compare-Object -ReferenceObject $a -DifferenceObject ($b + $c) |
Where-Object {$_.SideIndicator -eq '<='}).InputObject

A regex solution:
$a="dhaw","roh","kohl","faf","abd","steyn","gupt","baz","kane","benn","brendn","joyc"
$b="roh","dhaw","kohl"
$c="steyn","abd","faf","roh","dhaw"
$b_regex = ‘(?i)^(‘ + (($b |foreach {[regex]::escape($_)}) –join “|”) + ‘)$’
$c_regex = ‘(?i)^(‘ + (($c |foreach {[regex]::escape($_)}) –join “|”) + ‘)$’
Then, for elements of $a that aren't in $b:
$a -notmatch $b_regex
faf
abd
steyn
gupt
baz
kane
benn
brendn
joyc
For elements of $a that aren't in $c:
$a -notmatch $c_regex
kohl
gupt
baz
kane
benn
brendn
joyc
And for elements of $a that aren't in $b or $c:
$a -notmatch $b_regex -notmatch $c_regex
gupt
baz
kane
benn
brendn
joyc
Runtime Regex
Note: this is just provided for demonstration for the people who left comments about it. This substantially faster than the -contains / -notcontains solutions, but for a single instance comparison it's probably overkill. It can produce substantial performance gains inside a loop where you're comparing one array to many other arrays.

Related

Powershell - Matching text from array and string?

I have an array that simply pulls a list of numbers in one long column. I am trying to match it with some data in a string and if it matches, I am wanting it to state Down otherwise it will state Up in the output CSV.
Here is my code: `
IF($RESULTS -like $TEST)
{$Output = "DOWN"
}ELSE{
$OUtput = "UP"
}
`
$RESULTS is the array, and $TEST is the string. If I do -match it works, but -match only pulls single digits so it gives false positives. For example, if there is a 3 in the list as well as 638 it will mark them both as down. None of the other switches seem to work like -eq, -like, etc.
What am I missing please?
Thanks much for any assistance!
EDIT:
Sample of Data in $TEST
2
3
5
Sample of Output of $RESULTS
5
628
Since 5 exists in both, my expected output would be DOWN and everything else would be UP.
It sounds like you have two arrays of numbers, and you want to test if the input array contains at least one of the values in the test array.
You can use Compare-Object, which compares two arrays and indicates which elements are different and, with -IncludeEqual, also which ones are the same:
if (
(Compare-Object -IncludeEqual $RESULTS $TEST).
Where({ $_.SideIndicator -eq '==' }).
Count -gt 0
) {
$Output = "DOWN"
}
else {
$Output = "UP"
}
As an aside:
You can use an if statement as an expression, which means you only need to specify $Output once:
$Output =
IF (<# conditional #>) {
"DOWN"
}
ELSE {
"UP"
}
In PowerShell (Core) 7+, you can use ?:, the ternary conditional operator for a more concise solution:
$Output = <# conditional #> ? 'DOWN' : 'UP'
I would do it by using "foreach". Hope this might be helpful
foreach ($result in $RESULTS){
if ($result -like $Test){
$OUTPUT = "Down"}
else{
$OUTPUT= "UP"}
}
In your edited question you show that variable $TEST is also an array, so in that case you can do
$TEST = 2,3,5
$RESULTS = 5,628
# compare both arrays for equal items
$Output = if ($TEST | Where-Object { $RESULTS -contains $_ }) {"DOWN"} else {"UP"}
$Output
In this case, $Output will be DOWN because both arrays have the number 5
If however variable $TEST contains a multiline string, then first create an array out of that like
$TEST = #'
2
3
5
'#
# convert the string to array by splitting at the newline
$TEST = $TEST -split '\r?\n' -ne ''
$RESULTS = 5,628
# compare both arrays for equal items
$Output = if ($TEST | Where-Object { $RESULTS -contains $_ }) {"DOWN"} else {"UP"}
$Output

Find lines in one Object based on strings in other object

Finding lines in one file based on rows in another file.
I have one object $A with some rows like:
0c7d3283-bec2-4db1-9078-ebb79d21afdf
200bc957-26dd-4e8e-aa6e-00dc357c4ac2
218e0d2a-0e8b-4a68-8136-8f5dd749a614
I want to find matches in object $B for those rows and print the lines with the matches to an output file.
I've been trying for a week now (my first week in powershell :) )
I've come to:
$F = $B | ForEach-Object{ $A | Select-String -Pattern $_$ -AllMatches| Select-Object line }
but this doesn't give me any returning results.
Who is willing to help me out?
If you want to match your first Array, with something that should match part of a string in a second array you do something like the code below:
$A = #("0c7d3283-bec2-4db1-9078-ebb79d21afdf", "200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "218e0d2a-0e8b-4a68-8136-8f5dd749a614")
$B = #("Something 0c7d3283-bec2-4db1-9078-ebb79d21afdf", "Something else 200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "Something also e3df3978-beb7-4545-bc48-ff40d8453be1")
foreach ($Line in $A) {
if($B -match $Line) {
$B | Where-Object {$_ -match $Line}
}
}
We first loop through all the lines in the first object, then compare if the line is matched to anything in the second array. If we find a match we go through Array B to find where the Line from A matches.
You could make this code a hell lot prettier, but this the most understandable way I can write it.
Old Answer
You could use the Compare-Object cmdlet to compare the two arrays, then use the -IncludeEqual switch to show where there are matches and then use the -ExcludeDifferent switch to remove the results that do not match.
Then take that Output and put in in a file. A simple test could be something like this:
$A = #("0c7d3283-bec2-4db1-9078-ebb79d21afdf", "200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "218e0d2a-0e8b-4a68-8136-8f5dd749a614")
$B = #("0c7d3283-bec2-4db1-9078-ebb79d21afdf", "200bc957-26dd-4e8e-aa6e-00dc357c4ac2", "e3df3978-beb7-4545-bc48-ff40d8453be1")
(Compare-Object -ReferenceObject $A -DifferenceObject $B -ExcludeDifferent -IncludeEqual).InputObject | Out-File .\output.txt
This should output a file in your Shells current working directory with the two GUIDs that match:
0c7d3283-bec2-4db1-9078-ebb79d21afdf
200bc957-26dd-4e8e-aa6e-00dc357c4ac2
Where the one that didn't match is not included.

Multiple Variable Conditions for If Statement in PowerShell

Thanks for the help in this, I think i have over complicated the below but the logic just isn't responding how my mind is telling it too.
Logic in Question:
$a = "One"
$b = "Two"
$c = "Three"
$d = "Four"
If( {$a -and $b} -ne {$c and $d} ) {
Write-Host "Values are Different"
} Else {
Write-Host "values are the same"
}
I want the If statement to run when $a and $b are different to $c and $d, If the are the same see below, I want it to output that the values are the same
$a = "One"
$b = "One"
$c = "One"
$d = "One"
Thanks in advance!
You can use Compare-Object to compare the value pairs as arrays:
if (Compare-Object $a, $b $c, $d -SyncWindow 0) {
'different'
} else {
'same'
}
Note that this is convenient, but relatively slow, which may matter in a loop with many iterations.
The Compare-Object cmdlet compares two arrays and by default returns information about their differences.
-SyncWindow 0 compares only directly corresponding array elements; in other words: $a must equal $c, and $b must equal $d; without -SyncWindow, the array elements would be compared in any order so that 1, 2 would be considered equal to 2, 1 for instance.
Using the Compare-Object call's result as a conditional implicitly coerces the result to a Boolean, and any nonempty result - indicating the presence of at least 1 difference - will evaluate to $True.
As for what you tried:
Use of { ... } in your conditional is not appropriate.
Expressions enclosed in { ... } are script blocks - pieces of code you can execute later, such as with & or .
Even if you used (...) instead to clarify operator precedence (-ne has higher precedence than -and), your conditional wouldn't work as expected, however:
($a -and $b) -ne ($c -and $d) treats all variables as Booleans; in effect, given PowerShell's implicit to-Boolean conversion, you're comparing whether one value pair has at least one empty string to whether the other doesn't.
In addition to the answer from mklement0 and avoiding the rather slow Compare-Object cmdlet:
In what you tried, you will need to compare one specific value with each of the rest of the vales:
($a -eq $b) -and ($a -eq $c) -and ($a -eq $d)
Because the Comparison Operators (-eq) take a higher precedence than the Logical Operators (-and), you can leave the brackets and simplify it to:
$a -eq $b -and $a -eq $c -and $a -eq $d
To make this code DRY and easily expandable for even more values:
if ($a, $b, $c | Where {$_ -ne $d}) {
'different'
} else {
'same'
}
Just remove these {} brackets from the if statement
$a = "One"
$b = "One"
$c = "One"
$d = "One"
If($a -and $b -ne $c -and $d) {
Write-Host "Values are Different"
} Else {
Write-Host "values are the same"
}

How to match three or more items

Can't seem to get my head around this. I want to compare three (or more) items - example below - how can I get it to state true? If I do two items it works fine
$a = 2
$b = 2
$c = 2
$a -match $b -match $c
False
Looking at $Matches it only contains two items. I tried brackets around $a and $b but still get the same thing - it keeps on only looking at the first two and ignores the third.
PS C:\Windows\system32> $Matches
Name Value
---- -----
0 2
Your snippet is not working as expected because :
comparing two variables in the first place, gives you true or false,
that means 1 or 0. So, if you'll compare 1 or 0 with 2, it will obviously give false.
In simple terms :
$a -match $b -match $c equates to :
$a -match $b
true
true -match $c
false
So, as Martin has answered, you need to do it this way if you need it for regular expression comparison:
$a -match $b -and $a -match $c
true
But since you are comparing values so you need to use, -eq .
$a -match $b -match $c
actually results in the following:
[bool] $r1 = $a -match $b
[string] $r1 -match $c
Which is probably not what you want. In fact, I'm quite unsure what you actually want. The -match operator performs a regex match of the left operand. Do you perhaps mean something like
$a -eq $b -and $b -eq $c
?
You need to combine your checks using -and. Also use -eq (equals) here:
$a -eq $b -and $a -eq $c

Powershell foreach over two arrays

All,
Trying to get this working but have not had any luck with ps.
$a =
"install.res.1028.dll"
"install.res.1031.dll"
"install.res.1033.dll"
"install.res.1036.dll"
"install.res.1040.dll"
"install.res.1041.dll"
"install.res.1042.dll"
"install.res.2052.dll"
$b =
"install.res.1041.dll"
"install.res.1042.dll"
"install.res.2052.dll"
I just wish to have a new array with the values that are found in $a and not found in $b, trying to test it out with write-host but no luck.
I have tried compare-object but I am unable to pull out just the name. I am a totally noob with ps.
Please, any suggestions appreciated.
foreach ($i in $a)
{ foreach-object ($b | where { {$_.name} -ne $i }) { write-host $i}}
Another solution using compare-object as follows:
$c = Compare-Object -ReferenceObject $a -DifferenceObject $b -PassThru
output:
install.res.1028.dll
install.res.1031.dll
install.res.1033.dll
install.res.1036.dll
install.res.1040.dll
CORRECTION:
The above code WILL work for the case where DifferenceObject is guaranteed to be a subset of ReferenceObject. It will FAIL, though, if there are additional objects in DifferenceObject that are not also present in ReferenceObject. The above code returns any objects which are present in EITHER ReferenceObject OR DifferenceObject but NOT in both.
To properly return ONLY objects in ReferenceObject that are not also present in DifferenceObject, the following code is required:
Compare-Object -ReferenceObject $a -DifferenceObject $b |
Where-Object { $_.SideIndicator -eq '<=' } |
ForEach-Object { Write-Output $_.InputObject }
The where-object clause ensures only objects that are present in ReferenceObject are passed down the pipeline.
The foreach-object clause forces the output back to a simple array (ref: Converting Custom Object arrays to String arrays in Powershell - thanks Keith)
$c = $a | Where-Object { $b -notcontains $_ }
This should do the job.
Some Explanation
Where-Object's code block tests each element of the array that's piped into it. The block is supposed to return a boolean value, and if it's true, then the result of the call will contain the item in question.
So for the conditional, we use the -notcontains operator with your second array. $_ refers to the individual item from $a.
This doesn't require an additional or nested loop.