how to get unique value from an array of hashtable in powershell? - powershell

how to get unique value from an array of hashtable in powershell?
$h1 = #{a=1;b=2};
$h2 = #{a=3;b=4};
$h3 = #{a=1;b=2};
$h = $h1,$h2,$h3
I want to remove duplicate value ($h1 or $h3) from $h.
thanks!

You can try something like this:
$h | select #{ Expression = { "$($_.Keys):$($_.Values)" }; Label ="AsString" }, #{ Expression ={$_}; Label = "Hash" } -unique
| select -expand Hash

Related

How to remove item from an array in PowerShell (v5.1)

I have an array of elements in PowerShell (v5.1) as below.
I want to remove the item with matching condition EmpId=102
$testData = #(
#{ Name='ABC'; EmpId=100 },
#{ Name='EFG'; EmpId=101 },
#{ Name='XYZ'; EmpId=102 }
);
Is there any simple way to achieve this?
maybe something like $testData.Remove($testData.Find("EmpId == 102")) ?
if your array is really a collection of one-item hashtables, then the following otta work ... [grin] it pipes the collection thru the Where-Object cmdlet & filters out anything item that has the value-to-exclude in the EmpId key value.
$TestData = #(
#{ Name='ABC'; EmpId=100 }
#{ Name='EFG'; EmpId=101 }
#{ Name='XYZ'; EmpId=102 }
)
$EmpId_ToExclude = 102
$TestData |
Where-Object {
$_['EmpId'] -ne $EmpId_ToExclude
}
output ...
Name Value
---- -----
EmpId 100
Name ABC
EmpId 101
Name EFG
note that the items are not required to be in order ... that is how hashtables work if you don't specify [ordered].
I hope this info is some use. Maybe you can use an arraylist. You don't need #( ) to make an array.
[collections.arraylist]$testData = #{ Name='ABC'; EmpId=100 },
#{ Name='EFG'; EmpId=101 },
#{ Name='XYZ'; EmpId=102 }
$a = $testdata[1]
$testdata.remove($a)
Or
$testdata.removeat(1)
Unfortunely, this doesn't work. I guess you would need a pointer to the hashtable:
$testdata.remove(#{ Name='XYZ'; EmpId=102 })
Actually, it would be easy with a single hashtable:
$testData = #{ 'ABC'=100
'EFG'=101
'XYZ'=102 }
$testdata.remove('xyz')
$testdata
Name Value
---- -----
ABC 100
EFG 101

Most efficient way to find duplicates within non-grouped sections of a large hash

I am trying to find the most efficient way of sifting out any duplicates in a large hash table which consists of almost 5k objects.
I am running all of this in Powershell. So, I have this large hash table which consists of (in essence) User's and Subscription Names
1. User_id | Sub_name
2. User_id | Sub_name
etc...
In most cases, there are 5+ lines for each User_id as each new line represents a subscription name that user is subscribed to.
What I need to do is this: Identify any duplicate subscriptions for each user. For example
1. mm1234 | sub_1
2. mm1234 | sub_4
3. mm1234 | sub_1
4. mm9999 | sub_1
5. mm9999 | sub_2
6. mm8888 | sub_1
7. mm8888 | sub_1
So, in the above example, I would need to remove lines 3 & 7. Now, currently there is no actual grouping in terms of how users are grouped in the hash, they are just shoveled in. I'm wondering if it is possible to do it from the final product hash like seen above. Thoughts?
Maybe this can help.
If your large hash looks similar to this:
$hash = #{
'1' = #{ 'user_uuid' = 'mm1234'; 'lob' = 'subscription_1' }
'2' = #{ 'user_uuid' = 'mm5678'; 'lob' = 'subscription_1' }
'3' = #{ 'user_uuid' = 'mm1234'; 'lob' = 'subscription_2' }
'4' = #{ 'user_uuid' = 'mm5678'; 'lob' = 'subscription_5' }
'5' = #{ 'user_uuid' = 'mm1234'; 'lob' = 'subscription_3' }
'6' = #{ 'user_uuid' = 'mm1478'; 'lob' = 'subscription_1' }
}
You could create a new result hash where the keys are the user_uuid's and the values are arrays of uniquely sorted subscriptions (or lob as you call them)
$result = #{}
$hash.Keys | ForEach-Object {
$uid = $hash.$_.user_uuid
$value = $hash.$_.lob
if ($result.ContainsKey($uid)) {
# add to the subscriptions array for this user_uuid
$result[$uid] = ($result[$uid] + $value) | Sort-Object -Unique
}
else {
# create an element for this user_uuid and make sure the value is an array
$result[$uid] = #($value)
}
}
The resulting Hashtable will have this content:
Name Value
---- -----
mm1234 {subscription_1, subscription_2, subscription_3}
mm1478 {subscription_1}
mm5678 {subscription_1, subscription_5}
If you need to convert this back into the format of the original $hash (a hash of hashes), you can do something like this:
# recreate the large hash using the deduped values
$newHash = #{}
$count = 1
$result.Keys | ForEach-Object {
foreach ($value in $result.$_) {
$newHash[$count++] = #{ 'user_uuid' = $_; 'lob' = $value }
}
}

Powershell v2.0 substitute null values from a Hash table

I have a hash table as below:
$Hash = #{
Team1=$Team1.count
Team2=$Team2.count
Team3=$Team3.count
}
$GroupByTeam = New-Object psobject -Property $Hash |
Select 'Team1','Team2','Team3' | ConvertTo-Html -Fragment
This is fine and each "team" returns their own value. However, teams may have a null value and I wish to substitute this for "0".
In an attempt to work this out, I have tried to select the null value first but can't seem to do this:
$Hash.values | select -property Values
Values
------
{1, 2}
But
$Hash.values | select -property Values | where {$_.Values is $null}
doesn't pull back anything. Also tried:
$Hash.values | select -expandproperty Values | where {$_.Values is $null}
Any ideas?
thanks
Your best option is to cast the values to int when creating the hashtable:
$Hash = #{
Team1 = [int]$Team1.Count
Team2 = [int]$Team2.Count
Team3 = [int]$Team3.Count
}
If that's not possible for some reason you could go with an enumerator:
($Hash.GetEnumerator()) | ForEach-Object {
if ($_.Value -eq $null) { $Hash[$_.Name] = 0 }
}
or (as Mathias suggested) use the Keys property to the same end:
($Hash.Keys) | ForEach-Object {
if ($Hash[$_] -eq $null) { $Hash[$_] = 0 }
}
Note that either way you need to use a subexpression (or assign the enumerated objects/keys to a variable) otherwise you'll get an error because you're modifying a data structure while it's being enumerated.
What you'll want to do is collect the keys that refer to null values, and then populate those with 0s:
# Create and populate hashtable
$HashTable = #{
Team1 = 123
Team2 = $null
Team3 = 456
}
# Find keys of `$null` values
$nullKeys = $HashTable.Keys |Where-Object { $HashTable[$_] -eq $null }
# Populate appropriate indices with 0
$nullKeys |ForEach-Object { $HashTable[$_] = 0 }

Is the Name property of output of Group-Object always string?

The following script can transform(pivot) the array by the third column (x, y). However, it needs to concatenate the first two columns for the group-object command. And then the Name of the output need to be split to get the original values.
It can be error prone if the data has the separator character. And it seems not performance optimized since extra string concatenation/split actions are needed. Is it a more direct way (like SQL group clause) in powershell?
$a =#('a','b','x',10),
#('a','b','y',20),
#('c','e','x',50),
#('c','e','y',30)
# $a | % { "[$_]"}
$a | %{
new-object PsObject -prop #{
label = "$($_[0]),$($_[1])" # Concatenate for grouping
value = #{ $_[2] = $_[3] }
}
} |
group label | % {
$l = #($_.Name -split ",") + # then split to restore
#($_.Group.value.x, $_.Group.value.y)
"[$l]"
}
Yes, the "Name" property of GroupInfo is always a string.
The easiest way to find the distinct values is to sample the first item in each group:
$a |Group-Object -Property {$_[0]},{$_[1]} |ForEach-Object {
$Group = $_.Group
# The first item in each group
$SampleItem = $Group | Select-Object -First 1
# Now we can inspect the key values, $SampleItem[0] and $SampleItem[1]
Write-Host ('This group has {0} and {1} as primary keys:' -f $SampleItem[0..1]) -ForegroundColor Green
$Group |ForEach-Object {
# echo each array in group
Write-Host ($_ -join ' ')
}
}

powershell select-object outputs array on one line

I have around 20 arrays which contain over 100 values each.
I want to output these to a csv file with column headings.
If I type any of these arrays in a powershell command prompt they display on multiple lines and I can select different items from the array using $arrayname{14] for example, so I think they are being stored correctly.
If I use the following line in my script:
"" | select-object #{Name="Column1"; Expression={"$Array1"}},#{Name="Column2"; Expression={"$Array2"}},#{Name="Column3"; Expression={"$Array3"}} | export-csv $exportLocation -notypeinformation
Then it creates the columns with the heading but each array variable is displayed on one line.
How can I get the output to display the arrays in the respective columns on a line of their own?
You need to convert your 4 arrays into an array of objects with 4 properties. Try this:
$Array1 = #(...)
$Array2 = #(...)
$Array3 = #(...)
$Array4 = #(...)
$len1 = [Math]::Max($Array1.Length, $Array2.Length)
$len2 = [Math]::Max($Array3.Length, $Array4.Length)
$maxlen = [Math]::Max($len1, $len2)
$csv = for ($i=0; $i -lt $maxlen; $i++) {
New-Object -Type PSCustomObject -Property #{
'Column1' = $Array1[$i];
'Column2' = $Array2[$i];
'Column3' = $Array3[$i];
'Column4' = $Array4[$i];
}
}
$csv | Export-Csv 'C:\path\to\output.csv'