Public/ConvertTo-CrossTab.ps1

function ConvertTo-CrossTab {
<#
    .SYNOPSIS
        Converts simple rows into a cross tab query
    .DESCRIPTION
    Takes data like
            Place, Month, Person
            London, Jan, Alice
            London, Feb, Bob
            London, Mar, Chris
            New York, Jan, Alex
            New York, Feb, Jean
            New York, Mar, Phil
            And converts it to
            Place, Jan, Feb, Mar
            London, Alice, Bob, Chris
            New York Alex, Jean, Phil
    .EXAMPLE
    Ps > $data = @"
    Place, Month, Person
    London, Jan, Alice
    London, Feb, Bob
    London, Mar, Chris
    New York, Jan, Alex
    New York, Feb, Jean
    New York, Mar, Phil
    "@ | convertfrom-csv
    Ps > $data | ConvertTo-CrossTab -RowName Place -ColumnName Month -ValueName person | ft
 
    Takes some sample data as a string and converts it from Csv format and Outputs a Crosstab using Places as a row names and Months column names
 
    Place Feb Jan Mar
    ----- --- --- ---
    London Bob Alice Chris
    New York Jean Alex Phil
 
    .EXAMPLE
    ps> $data | ConvertTo-CrossTab -RowName Month -ColumnName Place -ValueName person | ft
 
    Pivots the same data but switches the rows and columns. Note that data is sorted by the row name so Feb is first alphabetically.
    Thhe result looks like this
 
    Month London New York
    ----- ------ --------
    Feb Bob Jean
    Jan Alice Alex
    Mar Chris Phil
#>


    param(
        [string]$RowName,
        [String]$ColumnName,
        [string]$PrefixColumn = "",
        [String]$SuffixColumn = "",
        [string]$ValueName,
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        $InputObject
        )
    begin {
        $data = @()
    }
    process {
        $data += $inputObject
    }
    end {
        $Rows    = @{}
        $Columns = @{}
        if ($data.where({($null -eq  $_.$RowName) -or ($null -eq $_.$ColumnName) -or ($null -eq $_.$ValueName)}) ) {
            Write-Warning "Some data is missing $RowName and/or $ColumnName and/or $ValueName properties"
        }
        $Duplicates = $false
        foreach ($d in $data.where({$_.$RowName -and $_.$ColumnName -and $_.$ValueName}))   {
            if ($null -eq $rows[$d.$RowName]) {$rows[$d.$RowName] = @{}}
            if ($null -ne $rows[$d.$RowName][$d.$ColumnName]) {$Duplicates = $true} else {$Columns[$d.$ColumnName] = $true }
            $rows[$d.$RowName][$d.$ColumnName] = $d.$ValueName
        }
        if ($Duplicates) {
            Write-Warning "Some Row/Column combinations had duplicate rows, the last value in the data will be used"
        }
        $OutputProperties= @($RowName) + ($Columns.Keys | Sort-Object | ForEach-Object {@{n=($PrefixColumn + $_ + $SuffixColumn); e= $_.tostring()}})
        $rows.Keys | Sort-Object | ForEach-Object {
            $r = $rows[$_]
            $r[$RowName] = $_
            [pscustomobject]$r
        } | Select-Object -Property $OutputProperties

    }
}