2
\$\begingroup\$

Based on a question on SO I thought I'd knock up a cmdlet to perform an unpivot on an object. I've not yet considered all the options (e.g. what happens when objects with empty lists are passed), so there are likely a few issues, but I wanted to get it out to the community early / see if people think this is useful or if better alternatives already exist in PS natively.

cls

function UnPivot-Object
{
    param
    (
        [Parameter(
            Mandatory=$true, 
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )][psobject]$InputObject
        ,[Parameter(
            ValueFromPipelineByPropertyName=$true
        )][string[]]$Properties
        ,[Parameter(
            ValueFromPipelineByPropertyName=$true
        )][string]$ExpandProperty
    )
    process
    {
        $InputObject | %{
            $x = $_ ;
            $_ | select -ExpandProperty $ExpandProperty | %{
                $y = $_ ;
                $x | select $Properties | select *, @{Name="$ExpandProperty";Expression={$y}}
            }
        }
    }
}

$x = @(
     (New-Object –TypeName PSObject –Prop @{Name='one';   Var='this'; Var2='How';   List=@('a','b','c');})
    ,(New-Object –TypeName PSObject –Prop @{Name='two';   Var='test'; Var2='now';   List=@('d','e','f');})
    ,(New-Object –TypeName PSObject –Prop @{Name='three'; Var='is'  ; Var2='brown'; List=@('g','h','i');})
    ,(New-Object –TypeName PSObject –Prop @{Name='four';  Var='a'   ; Var2='cow';   List=@('j','k','l');})
    ,(New-Object –TypeName PSObject –Prop @{Name='five';  Var='test'; Var2=$null;   List=@('m','n','o');})
)

$x | UnPivot-Object -Properties Name, Var -ExpandProperty List
\$\endgroup\$
  • 2
    \$\begingroup\$ Looks pretty good. Unpivot isn't an approved verb, though. I'd move that (really nice) example into comment-based help as well. One last thing, try to avoid aliases (select, %) in scripts. They're ok for the command-line because they're quicker to type, but in a script readability rules. \$\endgroup\$ – Mike Shepard Jun 24 '15 at 2:36
2
\$\begingroup\$

Looks great! I'm going to rehash the things that @Mike Shepard said because he's absolutely right but he wrote them in a comment, so I'll expand on them a bit first, and add a few more.

Approved Verbs

PowerShell has a concept of approved verbs. You can see them by typing Get-Verb at the prompt, but you should really look at the MSDN page on approved verbs for the rules and help with selecting appropriate verbs.

For your function I like the Split or Expand verbs, like Split-Object or Expand-Property.

Convert might also be appropriate based on the description, but it doesn't quite sound right to me.

Comment Based Help

Not too much to say about this that isn't covered in about_Comment_Based_Help, but yeah it's definitely good to have. One of the things that I think is overlooked is the .LINK keyword.

If you decide to publish this somewhere, you can use this to link to online documentation or examples or support.

Get-Help MyFunction -Online will automatically open a browser window to the first .LINK URL (you can have more than one listed).

Avoid Aliases

Not everyone agrees with this, but I do.

I love and use aliases all the time at the prompt, but when writing scripts, I use the full names of functions, cmdlets, parameters, etc. I truly think it makes things clearer and more readable.

In your script:

  • % should be ForEach-Object
  • select should be Select-Object

Tab completion makes it easier since you don't have to type all of the longer names.

CmdletBinding Attribute

This is minor, but you may want to use [CmdletBinding()] for your function. Obviously, it's not necessary since your function works, but it's worth reading about_Functions_CmdletBindingAttribute to get an idea of what it does, what it can do with its options, and how it might change the way your function works.

Parameter Naming

Your -Properties parameter should be named -Property. It is convention in PowerShell to use singular names, even for things that may accept or return multiple objects.

Parameter Binding

The -Properties and -ExpandProperty parameters are both optional, but calling the function with one or both of them missing gives less than stellar results.

So the function should be updated either to gracefully handle situations where they are missing, or they should be made mandatory (like -InputObject).

Select Redundancy

$x | select $Properties | select *, @{Name="$ExpandProperty";Expression={$y}}  

You could avoid doing 2 selects there by combining $Properties and the hashtable used for $y:

$x | select ($Properties + @{Name="$ExpandProperty";Expression={$y}})

Or you could pre-append them:

$props = $Properties + @{Name="$ExpandProperty";Expression={$y}}
$x | select $props

Semicolons

You use a semicolon after your simple assigns for some reason:

$x = $_ ;
$y = $_ ;

They do no harm, but typically they are used to separate multiple statements on a single line. If you really wanted to use them in concert with line breaks, at least be consistent and do it everywhere. But, yeah actually just don't use them.

Inconsistent Coding Style

This is only concerning the placement of braces. In the process block you use C# style where the opening brace is on its own line, and in the rest you seem to use the style more common in PowerShell where the opening brace is on the same line as the preceding statement.

In some sense, this is a matter of opinion, but sometimes it makes a difference in the way the code executes, and for that reason, I always use the latter format (brace does not get its own line).

Either way you decide, be consistent.

Closing

All in all, great job!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.