Getting Missing element from comparison of Array's using powershell [duplicate] - arrays

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Comparing two arrays & get the values which are not common
I wanted a logic to get uncommon items from an array, for example:
$a=#(1,2,3,4,5,6)
$b=#(1,2,3,4,5,7,9,10)
I want the output $c to be 6 which is the missing element in $b array, priority should be only given to the array contents of $a.
Can anyone please help me out with this?
Thanks!

Either empo's approach, or
$a1=#(1,2,3,4,5,8)
$b1=#(1,2,3,4,5,6)
Compare-Object $a1 $b1 |
Where-Object { $_.SideIndicator -eq '<=' } |
Foreach-Object { $_.InputObject }
returns 8

$c = $a | ? {!($b -contains $_)}
The priority will be given to the variable you "pipe".

Related

Compare duplicates in an array. List values that are contained and not contained in array [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have these arrays, I want to list unique values. If a value in $arr2 is in $arr1 I want to list that out but only ONE time. If a value in $arr2 is NOT in $arr1 I want to list that out as well. I would like to also add the values in $arr2 that are not contained in $arr1 to a seperate array.
This is written in powershell but if you have a solution/tips in any other language that is perfectly fine with me I will rewrite to powershell.
$arr1 = #(1,1,1,2,2,3,4,4,5,7)
$arr2= #(1,2,3,4,5,6,7)
for ($i = 0; $i -lt $arr2.length; $i++){
for( $j = $i+1; $j -lt $arr1.length; $j++ ){
if($arr2[$i] -eq $arr1[$j]){
Write-Host $arr2[$i]
}
}
}
You can use something like this to create an object which can be used for your need, however it will require further code from your side (i.e.: filtering) to get the results you're looking for.
$arr1 = #(1,1,1,2,2,3,4,4,5,7)
$arr2 = #(1,2,3,4,5,6,7)
$result = [System.Collections.Generic.List[pscustomobject]]::new()
foreach($i in $arr1 + $arr2)
{
if($i -in $result.Value)
{
continue
}
$z = [ordered]#{
Value = $i
Array1 = $false
Array2 = $false
}
if($i -in $arr1)
{
$z.Array1 = $true
}
if($i -in $arr2)
{
$z.Array2 = $true
}
$result.Add([pscustomobject]$z)
}
$valuesOnBothArrays, $valuesOnOneArray = $result.Where({
$_.Array1 -eq $true -and $_.Array2 -eq $true}, 'Split'
)
$valuesOnBothArrays will result in:
Value Array1 Array2
----- ------ ------
1 True True
2 True True
3 True True
4 True True
5 True True
7 True True
$valuesOnOneArray will result in:
Value Array1 Array2
----- ------ ------
6 False True
I suggest using the Compare-Object cmdlet in combination with the .Where() array method:
$arr1 = 1,1,1,2,2,3,4,4,5,7
$arr2 = 1,2,3,4,5,6,7
$inBoth, $uniqueRight =
(Compare-Object -PassThru -IncludeEqual `
($arr1 | Select-Object -Unique) ($arr2 | Select-Object -Unique)).
Where({ $_.SideIndicator -in '==', '=>' }).
Where({ $_.SideIndicator -eq '==' }, 'Split')
"-- in both:"
$inBoth
"-- unique to arr2"
$uniqueRight
Note: Thanks to using Select-Object -Unique on the input arrays so as to only operate on distinct elements, the use of -PassThru with Compare-Object works as expected and passes the integers of interest through directly, rather than as properties of wrapper objects. The caveat is that with [int] array elements specifically, because PowerShell caches values up to 100, having duplicates in either collection would malfunction obscurely. The reason is that -PassThru decorates the pass-through elements with an ETS .SideIndicator property, which affects all uses of a given integer between 0 and 100, so that the .SideIndicator property value of a later duplicate would overwrite the original value - see this answer for more information.
Note:
If you know that the distinct elements of $arr1 are only ever a subset of the ones in $arr2, i.e. that $arr1 contains no unique elements, you can eliminate the intermediate .Where({ $_.SideIndicator -in '==', '=>' }) call.
Unfortunately, as of PowerShell 7.2, the implementation of Select-Object -Unique is quite inefficient - see GitHub issue #11221. If the input arrays were sorted, Get-Unique would be a faster alternative.
The above yields:
-- in both (distinct):
1
2
3
4
5
7
-- unique to arr2
6

Powershell: adding an element from an array to another array [duplicate]

This question already has an answer here:
Powershell: Piping output of pracl command to array
(1 answer)
Closed 1 year ago.
I am trying to add elements to array for filtering. after it goes through the loop the first time
I receive "Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'."
I have tried several methods to try and figure this out.
$JsonDB = Get-Content 'Q:\Technology\1AA\HardwareCollection.json' | Out-String | ConvertFrom-Json
foreach($client in $JsonDB)
{
if($client.HRSeparation -eq "No")
{
$ClientNotHRSeparated += $client
}
else
{
$ClientHRSeparated += $client
}
}
$JsonDB
Any help would be greatly appreciated, Thanks!!
ConvertFrom-Json parses a JSON string into PSObject(s).
Since you did not define $ClientNotHRSeparated and $ClientHRSeparated anywhere, but immediately start adding ($client) objects to it, in the first iteration your variable $ClientNotHRSeparated will become that client object.
The next time you do +=, you're trying to add an object to another object which does not work.
Define the variables on top of the script, preferably as List object that has a .Add() method.
$ClientNotHRSeparated = [System.Collections.Generic.List[object]]::new()
$ClientHRSeparated = [System.Collections.Generic.List[object]]::new()
Then in your loop use that as
$ClientNotHRSeparated.Add($client)
# same for $ClientHRSeparated
P.S. Using a List is much faster/better that adding to a simple array (#()), because when you add items to an array (which has a fixed length) with +=, the entire array needs to be rebuilt in memory, consuming memory and processing time
Although this works, you don't need a loop at all. Just do:
$ClientNotHRSeparated = $JsonDB | Where-Object { $_.HRSeparation -eq "No" }
$ClientHRSeparated = $JsonDB | Where-Object { $_.HRSeparation -ne "No" }
The first line can be rewritten as $JsonDB = Get-Content -Path 'Q:\Technology\1AA\HardwareCollection.json' -Raw | ConvertFrom-Json.
Switch -Raw makes the cmdlet read the content of the file as one single multilined string
The behavior of += is entirely dependent on the left-hand side operand. On the first assignment, the value of $ClientNotHRSeparated is $null, so the resulting operation is:
$ClientNotHRSeparated = $null + $someCustomPSObject
Which PowerShell evaluates as just:
$ClientNotHRSeparated = $someObject
On the second assigment, $ClientNotHRSeparated is no longer $null, and PowerShell instead of tries to identify an overload for + that works on two operands of type [PSObject], which is where it fails.
If you want += to perform array addition, define the two array variables ahead of time with an assignment of a resizable array (use the #() array subexpression operator):
$ClientNotHRSeparated = #()
$ClientHRSeparated = #()
$JsonDB = Get-Content 'Q:\Technology\1AA\HardwareCollection.json' | Out-String | ConvertFrom-Json
foreach ($client in $JsonDB) {
if ($client.HRSeparation -eq "No") {
$ClientNotHRSeparated += $client
}
else {
$ClientHRSeparated += $client
}
}
$JsonDB
Now += is unambiguous both the first time and subsequently - the left-hand side operand is an array in either case.
As an alternative to looping through the whole collection manually, consider using the .Where() extension method in Split mode:
$JsonDB = Get-Content 'Q:\Technology\1AA\HardwareCollection.json' | Out-String | ConvertFrom-Json
$ClientNotHRSeparated, $ClientHRSeparated = #($JsonDB).Where({$_.HRSeparation -eq 'No'}, 'Split')
Much faster and more concise :-)

In Powershell Why might a foreach-object loop not let you retrieve individual values from an array? [duplicate]

This question already has answers here:
PowerShell outputting array items when interpolating within double quotes
(3 answers)
Closed 2 years ago.
Note the following output.
$Names is an array with two values.
$Machinename is an array with two values.
Their positional values within the array are retrieved accurately outside of a foreach loop.
When fetched within a foreach loop the value requested for the first position in the array i.e. $Names[0] ignores the positional call of [0]..... I need to be able to retrieve that value by itself..... Ultimately I will need to interate through each value to input to a command...
PS C:\Users\lab> $Names
john
jeff
PS C:\Users\lab> $Names[0]
john
PS C:\Users\lab> $Names[1]
jeff
PS C:\Users\lab> $Machinename
dev1
dev2
PS C:\Users\htlab> $Machinename | ForEach-Object { Write-Output "$Names[0]"}
john jeff[0]
john jeff[0]
Sample Script:
$Names = 'john', 'jeff'
$machinename = 'dev1', 'dev2'
$Machinename | ForEach-Object {Write-Output "$Names[0]"}
You're not evaluating the array. By writing "$Names[0]" it is the equivalent of $Names + "[0]".
You need to nest the evaluation inside $(...).
Try this:
$Machinename | ForEach-Object {Write-Output "$($Names[0])"}
That gives me:
john
john
Equally, as pointed out in the comments, this works too:
$Machinename | ForEach-Object {Write-Output $Names[0]}
You could try something like this, as it seemed to work for me, unless thats not what you're asking,
for (($i = 0); $i -lt $names.Count; $i++)
{
$Names[$i] + ":" + $Machinename[$i]
}
Output:
John:dev1
Jeff:dev2

ConvertTo-Json flattens arrays over 3 levels deep [duplicate]

This question already has answers here:
Cannot convert PSCustomObjects within array back to JSON correctly
(2 answers)
Closed 6 years ago.
In powershell, the following produces correct results.
$foo = #{}
$foo.bar = #{}
$foo.bar.buzz = #("herp","derp")
ConvertTo-Json $foo
{
"bar": {
"buzz": [
"herp",
"derp"
]
}
}
However if I add one more level, then the array "buzz" is flattened into a string
$foo = #{}
$foo.bar = #{}
$foo.bar.buzz = #{}
$foo.bar.buzz.bazz = #("herp","derp")
ConvertTo-Json $foo
{
"bar": {
"buzz": {
"bazz": "herp derp"
}
}
}
Why does powershell flatten Arrays into strings?
This issue feels similar to the following SO questions, but none of the purposed solutions solves it.
why-does-powershell-silently-convert-a-string-array-with-one-item-to-a-string
what-determines-whether-the-powershell-pipeline-will-unroll-a-collection
From ConvertTo-Json documentation
-Depth
Specifies how many levels of contained objects are included in the
JSON representation. The default value is 2.
Adding -Depth 9 option to ConvertTo-Json fixes the issue
ConvertTo-Json -Depth 9 $foo
Also, there is a difference between ConvertTo-Json $foo and $foo | ConvertTo-Json. Use ConvertTo-Json $foo whenever possible.
convertto-json-an-array-with-a-single-item

Powershell Leading zeros are trimmed in array using Measure-Object cmdlet

When using Powershell to find out the maximum or minimum value in a string array, the leading zeros of the outcome string are trimmed. How to retain the zeros?
$arr = #("0001", "0002", "0003")
($arr | Measure-Object -Maximum).Maximum
>>> 3
Enumerating the array is the fastest method:
$max = ''
foreach ($el in $arr) {
if ($el -gt $max) {
$max = $el
}
}
$max
Or use SortedSet from .NET 4 framework (built-in since Win 8), it's 2 times faster than Measure-Object but two times slower than the manual enumeration above. Still might be useful if you plan to sort the data without duplicates quickly: it's faster than the built-in Sort-Object.
([Collections.Generic.SortedSet[string]]$arr).max
Obviously, it'll allocate some memory for the array index, but not the actual data as it'll be reused from the existing array. If you're concerned about it, just force garbage collection with [gc]::Collect()
try this
$arr = #("0001", "0002", "0003")
$arr | sort -Descending | select -First 1

Resources