Reputation: 82
I have a PSObject[], which we can call $DeviceFW
, that has two children I'm concerned with: Bay
(string), and Components
(Object[]) which is essentially just an array of hashtables. What I'm trying to do is export Components to CSV, with the respective Bay listed in each row.
$DeviceFW.Components | Export-Csv "FW.csv" -NoTypeInformation
is nearly perfect, except it's missing the Bay column and information.
$DeviceFW | Select Bay,Components | Export-Csv "FW.csv" -NoTypeInformation
has the Bay column and correct information, but Components just displays in a single column as "System.Management.Automation.PSObject[]"
I somehow need a way to export the expanded Components while including a column for the respective bays. What I had in mind was some magic way to address the parent's other property (Bay) from within the Components Select pipe, such as: #$DeviceFW.Components | Select-Object @{Name='Bay';Expression={$_.Parent.Bay}},*
. I'm pretty new to PowerShell though, so I'm sure this is totally off. Maybe I should just work on addressing it with a loop, and give up on piping?
For an example of what I'm after, the below...
$DeviceFW:
Bay: 1
Components: {@{Name=ROM;Ver=A;ISO=A},@{Name=PWR;Ver=B;ISO=B}}
Bay: 2
Components: {@{Name=ROM;Ver=C;ISO=C},@{Name=PWR;Ver=D;ISO=D}}
... should output as:
Bay | Name | Ver | ISO
-----------------------
1 | ROM | A | A
-----------------------
1 | PWR | B | B
-----------------------
2 | ROM | C | C
-----------------------
2 | PWR | D | D
Upvotes: 1
Views: 3703
Reputation: 191
I liked both the answers given above and they both helped me to understand how this could be achieved. I tried to combine them to create a solution that uses only one line as per Matt's answer, but is slightly simpler as per TheMadTechnician's answer. Using the $DeviceFW object as created in the TheMadTechnician's answer (I prefer this as the Components are an array of objects as opposed to an array of hashtables as in Matt's answer and hence more typical of the objects that are encountered on a daily basis when using PowerShell) I ended up with the following solution.
$DeviceFW|ForEach-Object{$Bay = $_.Bay;$_.components|ForEach-Object{$_|Select-Object *,@{label='Bay';expression={$Bay}}}}|Export-Csv "FW.csv" -NoTypeInformation
Upvotes: 1
Reputation: 36322
From the example that you give I constructed your Arrays/Objects as follows:
$DeviceFW = @(
[PSCustomObject]@{
'Bay' = '1'
'Components'=@(
[pscustomobject]@{'Name'='ROM';'Ver'='A';'ISO'='A'},
[pscustomobject]@{'Name'='PWR';'Ver'='B';'ISO'='B'}
)},
[PSCustomObject]@{
'Bay' = '2'
'Components'=@(
[pscustomobject]@{'Name'='ROM';'Ver'='C';'ISO'='C'},
[pscustomobject]@{'Name'='PWR';'Ver'='D';'ISO'='D'}
)}
)
That is an array of objects, each object contains two properties, Bay
which is a string, and Components
which is an array of objects. Each objects in Components
contains three properties, Name
, Ver
, and ISO
, each of which is a string. When I do a $DeviceFW|FL
I get:
Bay : 1
Components : {@{Name=ROM; Ver=A; ISO=A}, @{Name=PWR; Ver=B; ISO=B}}
Bay : 2
Components : {@{Name=ROM; Ver=C; ISO=C}, @{Name=PWR; Ver=D; ISO=D}}
So far that matches your example. To get the desired CSV that you wanted I would put that through a ForEach loop as such:
Foreach($record in $DeviceFW){
$record.components|select @{l='Bay';e={$record.bay}},*|Export-Csv c:\temp\output.csv -NoTypeInformation -Append
}
That output the file:
"Bay","Name","Ver","ISO"
"1","ROM","A","A"
"1","PWR","B","B"
"2","ROM","C","C"
"2","PWR","D","D"
Upvotes: 4
Reputation: 46710
$DeviceFW | ForEach-Object{
$props = @{}
$props.bay = $_.Bay
$_.Components | ForEach-Object{
$_.GetEnumerator() | ForEach-Object{
$props.($_.Name) = $_.Value
}
New-Object -TypeName PsCustomObject -Property $props
}
} | Select-Object Bay,Name,Ver,ISO | Export-Csv "FW.csv" -NoTypeInformation
Using the above to create an object with the properties of your example. I tried to make this a one-liner but it was getting complicated fast.
For every single $DeviceFW
I build a new propery hash table which first gets populated with the Bay #. Take the components and pass them into a for each loop to break them up into the two hash tables you have. With each hash table enumerate them to get access as object so we can populate the $props
with the name and value pairs of each component. Every component is sent through the pipe with its Bay number as parameter. Use a select statement to enforce the order of fields and send to Export-Csv
. This might be simplified by taking the components and using Add-Member
to add the Bay.
Something shorter
$DeviceFW | ForEach-Object{
$Bay = $_.Bay
$_.Components | ForEach-Object{
New-Object PSObject -property $_ | Add-Member -MemberType NoteProperty -Name "Bay" -Value $Bay -PassThru
}
} | Select-Object Bay,Name,Ver,ISO | Export-Csv "FW.csv" -NoTypeInformation
I was silly an tried to reinvent the wheel. Since each Component is already a hash table I didnt need to recreate it. Take each component and make an object with it. Take that object and add a member for the current bay.
Output before export
Is the same for both my examples. This should export to csv nicely.
Bay Name Ver ISO
--- ---- --- ---
1 ROM A A
1 PWR B B
2 ROM C C
2 PWR D D
FYI
I used the following to create my sample object for the above code to process.
$DeviceFW = @()
$DeviceFW += [pscustomobject]@{
Bay = 1
Components = @{Name="ROM";Ver="A";ISO="A"},@{Name="PWR";Ver="B";ISO="B"}
}
$DeviceFW += [pscustomobject]@{
Bay = 2
Components = @{Name="ROM";Ver="C";ISO="C"},@{Name="PWR";Ver="D";ISO="D"}
}
Upvotes: 3