Powershell summing similarly named array propertys - arrays

Is there a way of summing similar property names in an array rather than doing this?
foreach ($row in $ExpenseData)
{
if ($row.refuel1_cost + $row.refuel2_cost + $row.refuel3_cost -gt 0)
{
}
}

If you want that sum for each rows, calculated properties are the perfect fit.
$ExpenseData | Select Name,#{'name'='Total refuel cost';'expression'={$_.refuel1_cost + $_.refuel2_cost + $_.refuel3_cost}}
I used the sample below to generate my expense data.
function CreateSamplePump($Name,$cost1,$cost2,$cost3){
$Props = #{'Name'='';'refuel1_cost'='';'refuel2_cost'='';'refuel3_cost'=''}
$Obj = New-Object psobject -Property $props
$obj.Name = $Name
$obj.refuel1_cost = $cost1
$obj.refuel2_cost = $cost2
$obj.refuel3_cost = $cost3
return $Obj
}
# Generating a sample for test purposes
$ExpenseData = New-Object System.Collections.ArrayList
$ExpenseData.Add((CreateSamplePump -Name 'Pump1' -cost1 10 -cost2 13 -cost3 4))
$ExpenseData.Add((CreateSamplePump -Name 'Pump2' -cost1 4 -cost2 2 -cost3 3))
$ExpenseData.Add((CreateSamplePump -Name 'Pump3' -cost1 3 -cost2 2 -cost3 1))
$ExpenseData.Add((CreateSamplePump -Name 'Pump4' -cost1 4 -cost2 8 -cost3 2))
$ExpenseData.Add((CreateSamplePump -Name 'Pump6' -cost1 6 -cost2 5 -cost3 1))
Edit:
In the event less likely event you would like to sum similar property names without knowing the number of refuel costs column in your dataset prior obtaining it, you could use something like that instead to get all similar names.
(Although if you have the names, use the name instead. It will be more efficient)
$CostsProperties = $ExpenseData | get-member | where -like -Property Name -Value 'refuel*_cost' | select -ExpandProperty Name
$ExpenseData | Select Name, #{'name'='Total refuel cost';'expression'={$Total = 0;Foreach ($c in $CostsProperties) {$Total += $_."$c"};return $Total }}

try this
$a=#{"refuel1_cost"=1;"refuel2_cost"=2;"refuel3_cost"=3;"something"=10}
$a.GetEnumerator() | ?{$_.name -match "refuel" } | select -expand value |measure -Sum |select -expand sum

Key to understanding my issue was dealing with a System.Data object - this post highlights some of the issues.
Sage's line of code $CostsProperties = $ExpenseData | get-member | where -like -Property Name -Value 'refuel*_cost' | select -ExpandProperty Name simplifies the script no end - thank you for that.
Below is the shell of my script
$ExpenseData = Get-MIS-Data -query $ExpensesQuery
# Get all the wildcard columns
$RefuelCols = $ExpenseData | get-member | where -like -Property Name -Value 'refuel*_cost' | select -ExpandProperty Name
#Check if any rows were returned
if ($ExpenseData.count -gt 0)
{
#Loop each returned row
foreach ($row in $ExpenseData)
{
foreach ($c in $RefuelCols)
{
$Total += $row.$c
}
if ($Total -gt 0)
{
#Knowing we have values do some useful stuff here
}
#Reset $Total before next loop
$Total = 0
}
}

Related

Failover cluster Alert Automation

In a high-availability environment (two DCs, Primary and Standby) with 50 clusters. Each cluster has 5 to 6 nodes in it. I want to ensure all the nodes in every cluster is "Online" (State) and running in "Primary" (OwnerNode). Any node that shows otherwise has to be noted.
I'm using arrays to store the required information in a foreach loop. The problem is, it is taking too long to compile. I want to complete it sooner.
$Clusternodes= * some 50 elements *
$Standbynodes= * some 50 elements *
foreach ($cluster in $Clusternodes) {
$NotOnline += Get-ClusterGroup -Cluster $Cluster |
where {$_.State -ne "Online"} |
Select-Object Name,OwnerNode,State
foreach ($node in $Standbynodes) {
$Standbys += Get-ClusterGroup -Cluster $Cluster |
where {$_.OwnerNode -eq "$node"} |
Select-Object Name,OwnerNode,State
}
}
Edit:
Get-clustergroup -Cluster $Cluster returns 5 to 6 entries in every cluster. The output contains three columns Name, OwnerNode, State.
I'm storing every entry in an array based on its state (whether it is running or not) and owner node (whether in primary or secondary DC). Hence, I want four arrays. While the former is easy, the latter isn't. As that itself is another two arrays with 50 odd elements in each. Hence I used hashtables for it as mentioned below. However, when I tried the below code it always returns an empty array.
$Clusternodes = * some 50 elements *
$Standbynodes = * some 50 elements *
$Primarynodes = * some 50 elements *
$pr = #{}
$sb = #{}
$Standbynodes | ForEach-Object { $sb[$_] = $true }
$Primarynodes | ForEach-Object { $pr[$_] = $true }
$RunninginPrimary = #()
$NotRunninginPrimary = #()
$RunninginStandby = #()
$NotRunninginStandby = #()
foreach ($cluster in $Clusternodes) {
$c = Get-ClusterGroup -Cluster $Cluster
$NotRunninginStandby += $c | Where-Object {
($_.State -ne "Online") -and ($sb.ContainsKey($_.OwnerNode))
} | Select-Object Name,OwnerNode,State
$NotRunninginPrimary += $c | Where-Object {
($_.State -ne "Online") -and ($pr.ContainsKey($_.OwnerNode))
} | Select-Object Name,OwnerNode,State
$RunninginStandby += $c | Where-Object {
($_.State -eq "Online") -and ($sb.ContainsKey($_.OwnerNode))
} | Select-Object Name,OwnerNode,State
$RunninginPrimary += $c | Where-Object {
($_.State -eq "Online") -and ($pr.ContainsKey($_.OwnerNode))
} | Select-Object Name,OwnerNode,State
}
You query each cluster multiple times. To speed up your code query each cluster just once, store the result in a variable, and use that variable in the rest of the loop. You may also want to replace the nested loop that iterates over $Standbynodes with a hashtable lookup.
$Clusternodes = ...
$Standbynodes = ...
$sb = #{}
$Standbynodes | ForEach-Object { $sb[$_] = $true }
$NotOnline = #()
$Standbys = #()
foreach ($cluster in $Clusternodes) {
$c = Get-ClusterGroup -Cluster $Cluster
$NotOnline += $c | Where-Object { $_.State -ne "Online" } |
Select-Object Name,OwnerNode,State
$Standbys += $c | Where-Object { $sb.ContainsKey($_.OwnerNode) } |
Select-Object Name,OwnerNode,State
}
Try using workflow.
workflow clusterCheck {
$Clusternodes= * some 50 elements *
$Standbynodes= * some 50 elements *
foreach -parallel ($cluster in $Clusternodes) {
$NotOnline += Get-ClusterGroup -Cluster $Cluster |
where {$_.State -ne "Online"} |
Select-Object Name,OwnerNode,State
foreach -parallel ($node in $Standbynodes) {
$Standbys += Get-ClusterGroup -Cluster $Cluster |
where {$_.OwnerNode -eq "$node"} |
Select-Object Name,OwnerNode,State
}
}
}
Source:- Scripting guy

Display all values in PSCustomObject array [duplicate]

Is it possible to display the results of a PowerShell Compare-Object in two columns showing the differences of reference vs difference objects?
For example using my current cmdline:
Compare-Object $Base $Test
Gives:
InputObject SideIndicator
987654 =>
555555 <=
123456 <=
In reality the list is rather long. For easier data reading is it possible to format the data like so:
Base Test
555555 987654
123456
So each column shows which elements exist in that object vs the other.
For bonus points it would be fantastic to have a count in the column header like so:
Base(2) Test(1)
555555 987654
123456
Possible? Sure. Feasible? Not so much. PowerShell wasn't really built for creating this kind of tabular output. What you can do is collect the differences in a hashtable as nested arrays by input file:
$ht = #{}
Compare-Object $Base $Test | ForEach-Object {
$value = $_.InputObject
switch ($_.SideIndicator) {
'=>' { $ht['Test'] += #($value) }
'<=' { $ht['Base'] += #($value) }
}
}
then transpose the hashtable:
$cnt = $ht.Values |
ForEach-Object { $_.Count } |
Sort-Object |
Select-Object -Last 1
$keys = $ht.Keys | Sort-Object
0..($cnt-1) | ForEach-Object {
$props = [ordered]#{}
foreach ($key in $keys) {
$props[$key] = $ht[$key][$_]
}
New-Object -Type PSObject -Property $props
} | Format-Table -AutoSize
To include the item count in the header name change $props[$key] to $props["$key($($ht[$key].Count))"].

Remove one or many members from Object in powershell

I have created a custom object called $info and moving it to an array $arr ,
How is it possible to remove one member along with its all properties ?
My script:
Get-Process | ForEach-Object{
$info = New-Object -TypeName PSObject
$info | Add-Member -Type NoteProperty -Name Process -Value $_.processname
$info | Add-Member -Type NoteProperty -Name ID -Value $_.id
$arr += $info
}
$arr | ft -AutoSize
The result looks like this :
Process ID
------- --
ApplicationFrameHost 38556
AppVShNotify 9792
armsvc 2336
atieclxx 6944
atiesrxx 1844
audiodg 59432
CcmExec 3988
chrome 46068
How can I remove one particular member for example "audiodg 59432" gets removed
audiodg 59432
Your terminology is a bit incorrect here. A member is on an individual object. When you use Add-Member above you're adding properties to each individual object, then you're returning an array of objects.
You're asking how to remove an individual object from the array.
In PowerShell you cannot remove an item from an array. You could instead filter the array based on some criteria and create a new one:
$newArr = $arr | Where-Object { $_.Name -ne 'audiodg' }
# or
$newArr = $arr | Where-Object { $_.ID -ne 59432 }

PowerShell finding duplicates in CSV and outputting different header

I guess the question is in the title.
I have a CSV that looks something like
user,path,original_path
I'm trying to find duplicates on the original path, then output both the user and original_path line.
This is what I have so far.
$2 = Import-Csv 'Total 20_01_16.csv' | Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } | fl Group | out-string -width 500
This gives me the duplicates in Original_Path. I can see all the required information but I'll be danged if I know how to get to it or format it into something useful.
I did a bit of Googleing and found this script:
$ROWS = Import-CSV -Path 'Total 20_01_16.csv'
$NAMES = #{}
$OUTPUT = foreach ( $ROW in $ROWS ) {
IF ( $NAMES.ContainsKey( $ROW.Original_path ) -and $NAMES[$ROW.original_path] -lt 2 )
{ $ROW }
$NAMES[$ROW.original_path] += 1 }
Write-Output $OUTPUT
I'm reluctant to use this because, well first I have no idea what it's doing. So little of the makes any sense to me, I don't like using scripts I can't get my head around.
Also, and this is the more important part, it's only giving me a single duplicate, it's not giving me both sets. I'm after both offending lines, so I can find both users with the same file.
If anyone could be so kind as to lend a hand I'd appreciate it.
Thanks
It depends on the output format you need, but to build on what you already have we can use this to show the records in the console:
Import-Csv 'Total 20_01_16.csv' |
Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } |
Foreach-Object { $_.Group } |
Format-Table User, Path, Original_path -AutoSize
Alternatively, use this to save them in a new csv-file:
Import-Csv 'Total 20_01_16.csv' |
Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } |
Foreach-Object { $_.Group } |
Select User, Path, Original_path |
Export-csv -Path output.csv -NoTypeInformation

Add new property based on a different objects property

I'm trying to search through one column in each row of the table. I would then like to add another value to the row based on the number being search.
This code produces the table:
$LUNSSummary = ($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' | foreach {
$Stringdata = $_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$LUNSSummary |
select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups' |
Format-Table -AutoSize
Then I have this code which can search using the "Logical Unit Number" and produce the desired output. In this example the -contains is 1029 from the above screenshot.
$data = $LUNS_in_Pools | Out-String
$pools = $data -replace ': +','=' -split "`r`n`r`n" |
% { New-Object -Type PSCustomObject -Property (ConvertFrom-StringData $_) } |
select -Property *,#{n='LUNs';e={$_.LUNs -split ', '}} -Exclude LUNs
$pools | ? { $_.LUNs -contains 1029 } | select -Expand 'Pool Name'
Which produces in this case "Pool 2". The result can be Pool 1-99.
I want to combine these two codes to search every "Logical Unit Number" and add the result to the end of the table in a 5th section/column "Pools".
EDIT
As requested, raw data:
$NY_LUNS before $LUNSSummary gets it: http://pastebin.com/5wrd51Lf
$LUNS_in_Pools raw data: http://pastebin.com/Zg9q6jhe
Desired Output: (Pool is obtained from "Logical Unit Number")
EDIT 2
This is now the closest to correct so far, it prints the same pool result every time.
$LUNSSummary =
($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' |
foreach { $Stringdata =
$_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$data = $LUNS_in_Pools | Out-String
$pools = $data -replace ': +','=' -split "`r`n`r`n" |
% { New-Object -Type PSCustomObject -Property (ConvertFrom-StringData $_) } |
select -Property *,#{n='LUNs';e={$_.LUNs -split ', '}} -Exclude LUNs
$poolProperty = #{Label="Pool";Expression={$pools | ? { $_.LUNs -contains [int]$_.'LOGICAL UNIT NUMBER'} | select -Expand 'Pool Name'}}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
if I check the output of $pools | ? { $_.LUNs -contains [int]$_.'LOGICAL UNIT NUMBER'} | select -Expand 'Pool Name'
I only see one result. I'm thinking maybe it has to be looped some how?
From the guess of it you just need one more calculated property on the end there for 'Pool'. You already have, and tested, the logic. Just need to implement it.
$poolProperty = #{Label="Pool";Expression={
$lunID = $_.'LOGICAL UNIT NUMBER';
$pools | Where-Object{$_.LUNs -contains $lunID} |
Select-Object -Expand 'Pool Name'}
}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
We take the LOGICAL UNIT NUMBER of the current item in the pipeline and save it so that we can start another to extract the match from the $pools object. As long as you luns are exclusive this would always return one Pool Name.
The above should work but I changed how $pools was created so it matched the logic of $LUNSSummary. I used here-strings for the raw data from your paste bin.
$LUNSSummary = ($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' |
foreach { $Stringdata =
$_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$pools = ($LUNS_in_Pools | Out-String) -split '\s+(?=Pool Name)' | ForEach-Object{
New-Object -Type PSCustomObject -Property (ConvertFrom-StringData ($_ -replace ":","=")) |
Select -Property *,#{n='LUNs';e={$_.LUNs -split ',\s*'}} -Exclude LUNs
}
$poolProperty = #{Label="Pool";Expression={
$lunID = $_.'LOGICAL UNIT NUMBER';
$pools | Where-Object{$_.LUNs -contains $lunID} |
Select-Object -Expand 'Pool Name'}
}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
Looks like $LUNS_in_Pools was a newline delimited string. Piping to Out-String cleaned it up to remove the newlines and allow the regex/ConvertFrom-StringData to work.

Resources