#Ethically Cloning Objects for Expanded Members

30 messages · Page 1 of 1 (latest)

hearty sand
#

Often there are cases when exporting to CSV, there are nested arrays.
I want to clone the object, so I can expand the list into a flat table

$Employees = @(
   [pscustomobject]@{ UserId = 0 ; Name = 'Bob'; Events = @( 'a'..'e' ) }
   [pscustomobject]@{ UserId = 1 ; Name = 'Jen'; Events = @( 'b', 'c', 'q', 'z' ) }
)

$Employees | ConvertTo-Csv
"UserId","Name","Events"
"0","Bob","System.Object[]"
"1","Jen","System.Object[]"

$Employee's can be any type
Do I clone? Do I add-member but emit the object multiple times? It doesn't have to work in all cases. If it does for a chunk it'd be worth it.

$ExpectedOutput = @(
    [pscustomobject]@{ UserId = 0 ; Name = 'Bob'; EventId = 'a' }
    [pscustomobject]@{ UserId = 0 ; Name = 'Bob'; EventId = 'b' }
    [pscustomobject]@{ UserId = 0 ; Name = 'Bob'; EventId = 'c' }
    [pscustomobject]@{ UserId = 0 ; Name = 'Bob'; EventId = 'd' }
    [pscustomobject]@{ UserId = 0 ; Name = 'Bob'; EventId = 'e' }
    [pscustomobject]@{ UserId = 1 ; Name = 'Jen'; EventId = 'b' }
    [pscustomobject]@{ UserId = 1 ; Name = 'Jen'; EventId = 'c' }
    [pscustomobject]@{ UserId = 1 ; Name = 'Jen'; EventId = 'q' }
    [pscustomobject]@{ UserId = 1 ; Name = 'Jen'; EventId = 'z' }

# csv
"UserId","Name","EventId"
"0","Bob","a"
"0","Bob","b"
"0","Bob","c"
"0","Bob","d"
"0","Bob","e"
"1","Jen","b"
"1","Jen","c"
"1","Jen","q"
"1","Jen","z"
floral crane
# hearty sand Often there are cases when exporting to CSV, there are nested arrays. I want to ...

PowerShell language allows you to work and build complicated objects. There are multiple ways to save them, such as XML or JSON, but sometimes using them is impossible or inadequate. Sometimes you want to use HTML or CSV or any other single dimension output.

keen stream
#

I might just do $property -join '^' and dump it to string and then throw it in the database for spliting.

#

But that is just quick and dirty way to get the data out and would probably do that if i was working interactively.

unique carbon
#
filter Expand-Property { 
param(
  [Alias("Property")]
  [string]$Name,

  [Parameter(ValueFromPipeline)]
  $InputObject
)
$InputObject |
  Select-Object * -Exclude $Name -Expand $Name |
  Select-Object *, @{ Name = $Name; Expr = { $_ } }
}
#

So then you can just convertto-csv

unique carbon
#
function Group-Property {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline = $true)]
        [psobject]
        ${InputObject},

        [Parameter(Position = 0)]
        [string]
        ${Name},

        [string]
        ${Culture},

        [switch]
        ${CaseSensitive}
    )

    begin {
        try {
            $outBuffer = $null
            $null = $PSBoundParameters.Remove("Name")
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }

            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Group-Object', [System.Management.Automation.CommandTypes]::Cmdlet)
        } catch {
            throw
        }
    }

    process {
        try {
            if (!$steppablePipeline) {
                $PSBoundParameters["Property"] = $InputObject.PSObject.Properties.Name.Where{ $_ -ne $Name }
                $null = $PSBoundParameters.Remove("InputObject")
                Write-Verbose "Group by $($PSBoundParameters["Property"] -join ', ')"
                $scriptCmd = { & $wrappedCmd @PSBoundParameters | ForEach-Object { $_.Group[0].$Name = $_.Group.$Name; $_.Group[0] } }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                $steppablePipeline.Begin($PSCmdlet)
            }

            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }

    clean {
        if ($null -ne $steppablePipeline) {
            $steppablePipeline.Clean()
        }
    }
}
#

So you can put it back 😉

hearty sand
#

Aw nice, that expands in the vertical direction

keen stream
keen stream
keen stream
unique carbon
#

I was playing with that some more last night, because it annoyed me that the output object in this case is typed as a string (albeit with extra properties)... Something interrupted me.

#

I think to avoid that, I'd want to stuff the existing properties into a single property on the first select, and pull them back out on the second... Or just switch to generating a whole new pscustomobject (and force set the PSTypeName) , to avoid the fact that you always end up with an object of the type of the single property you expended...

#

Fwiw, filter is just a function where the default scriptblock is process instead of end

#

Handy shortcut, nothing more

floral crane
#

I need to understand steppablepipeline one day. I keep seeing it

keen stream
unique carbon
#

Yeah, that's what this was

#

I mean, I started by generating ProxyCommand, and then hacked all the parameters off, wrote a custom scriptcommand, and moved the initialization into the process block

unique carbon
#

Yeah, I think Expand-Property is better like this:

filter Expand-Property {
    <#
        .SYNOPSIS
            Expands an array property, creating a duplicate object for each value
    #>
    param(
        # The name of a property on the input object, that has more than one value
        [Alias("Property")]
        [string]$Name,

        # The input object to duplicated
        [Parameter(ValueFromPipeline)]
        $InputObject
    )
    foreach ($Value in $InputObject.$Name) {
        $InputObject | Select-Object *, @{ Name = $Name; Expr = { $Value } } -Exclude $Name
    }
}
unique carbon
#
  @"
Name,MemberOf,Members
DevOps,Ops,Joe
DevOps,Ops,Phil
DevOps,Ops,Barb
DevOps,Dev,Joe
DevOps,Dev,Phil
DevOps,Dev,Barb
"@ | ConvertFrom-Csv | Group-Property Members | Group-Property MemberOf

Name   MemberOf   Members
----   --------   -------
DevOps {Ops, Dev} {Barb, Joe, Phil}
#

Kind of fun

keen stream
keen stream
unique carbon
#

Just put the psm1 in a module folder so it'll auto load 😉