PSPivotTable.psm1

#requires -version 4.0

Function New-PSPivotTable {

[cmdletbinding(DefaultParameterSetName = "Property")]

Param(
[Parameter(
    Position = 0,
    Mandatory,
    HelpMessage = "What is the data to analyze?"
)]
[ValidateNotNullorEmpty()]
[object]$Data,

[Parameter()]
[String]$yLabel,

[Parameter(
    Mandatory,
    HelpMessage = "What is the Y axis property?"
)]
[ValidateNotNullorEmpty()]
[String]$yProperty,

[Parameter(
    Mandatory,
    HelpMessage = "What is the X axis label?"
)]
[ValidateNotNullorEmpty()]
[string]$xLabel,

[Parameter(
    ParameterSetName = "Property"
)]
[string]$xProperty,

[Parameter(
    ParameterSetName = "Count"
)]
[switch]$Count,

[Parameter(
    ParameterSetName = "Sum"
)]
[string]$Sum,

[Parameter(
    ParameterSetName = "Sum"
)]
[ValidateSet("None","KB","MB","GB","TB","PB")]
[string]$Format = "None",

[Parameter(
    ParameterSetName = "Sum"
)]
[ValidateScript({$_ -gt 0})]
[int]$Round,

[Parameter(
    ParameterSetName = "Sum"
)]
[Parameter(
    ParameterSetName = "Count"
)]
[ValidateSet("None","Ascending","Descending")]
[string]$Sort = "None",

[Parameter(
    ParameterSetName = "Sum"
)]
[Parameter(
    ParameterSetName = "Count"
)]
[ValidateSet("Name","Value")]
[string]$SortKey = "Value"
)

Begin {
    Write-Verbose "Starting $($myinvocation.mycommand)"
    Write-Verbose "Using parameter set $($pscmdlet.parameterSetName)"
    #define some values for Write-Progress
    $Activity = "PowerShell Pivot Table"
    $status = "Creating new table"
    Write-Progress -Activity $Activity -Status $Status

    #initialize an array to hold results
    $result = @()
    #if no yLabel then use yProperty name
    if (-Not $yLabel) {
        $yLabel = $yProperty
    }
    Write-Verbose "Vertical axis label is $ylabel"
} #begin

Process {    
    Write-Progress -Activity $Activity -status "Pre-Processing"
    Write-Verbose "Creating a unique list based on $xLabel"
    <#
      Filter out null values, but not blanks. Uniqueness is case sensitive
      so we first do a quick filtering with Select-Object, then turn each
      of them to upper case and finally get unique uppercase items.
      Also explictly convert to values to strings.
    #>

    $unique = $Data | Where {$_.$xlabel -ne $Null} | 
     Select-Object -ExpandProperty $xLabel -unique | foreach {
       $_.ToString().ToUpper()} | Select-Object -unique
         
    Write-Verbose ($unique -join  ',' | out-String).Trim()
         
    Write-Verbose "Grouping objects on $yProperty"
    Write-Progress -Activity $Activity -status "Pre-Processing" -CurrentOperation "Grouping by $yProperty"

    $grouped = $Data | Group -Property $yProperty
    $status = "Analyzing data"  
    $i = 0
    $groupcount = ($grouped | measure).count
    
    foreach ($item in $grouped ) {
      Write-Verbose "Item $($item.name)"
      $i++
      #calculate what percentage is complete for Write-Progress
      $percent = ($i/$groupcount)*100
      Write-Progress -Activity $Activity -Status $Status -CurrentOperation $($item.Name) -PercentComplete $percent
         
      $hash = [ordered]@{}
      
      #process each group
        #Calculate value depending on parameter set
        Switch ($pscmdlet.parametersetname) {
        
        "Property" {
                    Write-Verbose "Processing $xLabel for $xProperty"
                    <#
                      take each property name from the horizontal axis and make
                      it a property name. Use the grouped property value as the
                      new value
                    #>

                    
                    #find non-matching labels and set value to Null
                    #make each name upper case
                     $labelGroup = $item.group | Group-Object -Property $xLabel 
                     $diff = $labelGroup | Select-Object -ExpandProperty Name -unique | 
                     Foreach { $_.ToUpper() } | Select-Object -unique
                     
                     #compare the master list of unique labels with what is in this group
                     Compare-Object -ReferenceObject $Unique -DifferenceObject $diff | 
                     Select-Object -ExpandProperty InputObject | foreach {
                        #add each item and set the value to null
                        Write-Verbose "Setting $_ to null"
                       $hash.Add($_,$null)
                     }
                    
                     $item.group | foreach {
                        $v = $_.$xProperty
                        Write-Verbose "Adding $($_.$xLabel) with a value of $v"
                        $hash.Add($($_.$xLabel),$v)
                      } #foreach
                    } #property
        "Count"  {
                    Write-Verbose "Calculating count based on $xLabel"
                     $labelGroup = $item.group | Group-Object -Property $xLabel 
                     #find non-matching labels and set count to 0
                     Write-Verbose "Finding 0 count entries"
                     #make each name upper case
                     $diff = $labelGroup | Select-Object -ExpandProperty Name -unique | 
                     Foreach { $_.ToUpper() } | Select-Object -unique
                     
                     #compare the master list of unique labels with what is in this group
                     Compare-Object -ReferenceObject $Unique -DifferenceObject $diff | 
                     Select-Object -ExpandProperty InputObject | foreach {
                        #add each item and set the value to 0
                        Write-Verbose "Setting $_ to 0"
                        
                        # Account for blank entries
                        If ([String]::IsNullOrEmpty($_)) {
                            $_ = "[NONE]"
                        }

                        $hash.add($_,0)
                     }
                     
                     Write-Verbose "Counting entries"
                     $labelGroup | foreach {
                        $n = ($_.name).ToUpper()
                        Write-Verbose "$n = $($_.count)"
                        
                        # Account for blank names
                        If ([String]::IsNullOrEmpty($n)) {
                            $n = "[NONE]"
                        }

                        $hash.Add($n,$_.count)

                    } #foreach
                 } #count
        "Sum"  {
                    Write-Verbose "Calculating sum based on $xLabel using $sum"
                    $labelGroup = $item.group | Group-Object -Property $xLabel 
                 
                     #find non-matching labels and set count to 0
                     Write-Verbose "Finding 0 count entries"
                     #make each name upper case
                     $diff = $labelGroup | Select-Object -ExpandProperty Name -unique | 
                     Foreach { $_.ToUpper() } | Select-Object -unique
                     
                     #compare the master list of unique labels with what is in this group
                     Compare-Object -ReferenceObject $Unique -DifferenceObject $diff | 
                     Select-Object -ExpandProperty InputObject | foreach {
                        #add each item and set the value to 0
                        Write-Verbose "Setting $_ sum to 0"
                        
                        # Account for blank entries
                        If ([String]::IsNullOrEmpty($_)) {
                            $_ = "[NONE]"
                        }
                        $hash.add($_,0)
                     }
                     
                     Write-Verbose "Measuring entries"
                     $labelGroup | foreach {
                        $n = ($_.name).ToUpper()
                        Write-Verbose "Measuring $n"
                        
                        $measure = $_.Group | Measure-Object -Property $Sum -sum
                        if ($Format -eq "None") {
                            $value = $measure.sum
                        }
                        else {
                            Write-Verbose "Formatting to $Format"
                             $value = $measure.sum/"1$Format"
                            }
                        if ($Round) {
                            Write-Verbose "Rounding to $Round places"
                            $Value = [math]::Round($value,$round)
                        }
                       
                       # Account for blank names
                        If ([String]::IsNullOrEmpty($n)) {
                            $n = "[NONE]"
                        }
                        $hash.add($n,$value)
                    } #foreach
                   
                } #Sum
        } #switch
       
       #sort as necessary
       if ($Sort -ne "None") {
        
        Write-Verbose "Sorting order $sort"
        Write-Verbose "Sorting on $sortkey"
        #define a hashtable of parameters for Sort-Object
        $sortParams = @{Property=$SortKey}

        if ($sort -eq "Descending") {
            $sortParams.Add("Descending",$True)
        }   
        
        $sorted = $hash.GetEnumerator() | Sort-Object @sortParams
        
        #rebuild the hash table based on sorting
        $sorted | foreach -Begin {$hash = [ordered]@{}} -process { $hash.add($_.name,$_.value)}   
       }
       
       #add ylabel
       $hash.Insert(0,$yLabel,$item.name)
       
       #add each object to the results array
       Write-Verbose "Adding object to the results array"
       $result += [pscustomobject]$hash
    } #foreach item

} #process

End {

    Write-Verbose "Writing results to the pipeline"
    $result
    Write-Verbose "Ending $($myinvocation.mycommand)"
    Write-Progress -Completed -Activity $Activity -Status "Ending"

} #end

} #end function

#define an optional alias
Set-Alias -Name npt -Value New-PSPivotTable

Export-ModuleMember -Function * -Alias *