private/charts/ConvertTo-ChartData.ps1
|
function ConvertTo-ChartData { <# .SYNOPSIS Normalizes various data formats to a consistent chart data structure. #> param($RawData, $LabelProperty, $ValueProperty) $result = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($item in $RawData) { # A null data point has no PSObject.Properties, so indexing it below'll throw "cannot index into a null array". Both New-UiChart and Update-UiChart funnel raw -Data through here, so one skip covers both entry points. if ($null -eq $item) { continue } $label = $null $value = $null # Already normalized from hashtable processing if ($item -is [hashtable] -and $item.ContainsKey('Label') -and $item.ContainsKey('Value')) { $label = $item.Label $value = $item.Value } else { # Try explicit property names first, then common defaults $labelProps = if ($LabelProperty) { @($LabelProperty) } else { @('Label', 'Name', 'Key') } $valueProps = if ($ValueProperty) { @($ValueProperty) } else { @('Value', 'Count', 'Sum', 'Total') } # A raw hashtable's data keys live on IDictionary, not PSObject.Properties and that only carries the .NET Hashtable members (Keys, Count, ...). INspect directly so a plain @{ Name = 'apple'; Count = 5 } resolves instead of silently charting nothing. # Generic Dictionary hides .Contains behind an explicit implementation that only finds when the call is typed IDictionary. Capturing the cast into a variable loses it and the call throws again. $isDict = $item -is [System.Collections.IDictionary] foreach ($prop in $labelProps) { if ($isDict) { if (([System.Collections.IDictionary]$item).Contains($prop)) { $label = $item[$prop]; break } } elseif ($item.PSObject.Properties[$prop]) { $label = $item.$prop break } } foreach ($prop in $valueProps) { if ($isDict) { if (([System.Collections.IDictionary]$item).Contains($prop)) { $value = $item[$prop]; break } } elseif ($item.PSObject.Properties[$prop]) { $value = $item.$prop break } } } if ($null -ne $label -and $null -ne $value) { # '-as [double]' returns $null instead of erroring out on any other input, so one messed up data point won't bring down the whole chart down with it. # NaN/Infinity survive the cast though (a ratio on an empty set, x/0 in double math), so check for them too or the bar height and axis scaling go nuts. $numeric = $value -as [double] if ($null -ne $numeric -and ![double]::IsNaN($numeric) -and ![double]::IsInfinity($numeric)) { $result.Add([PSCustomObject]@{ Label = [string]$label Value = $numeric }) } } } return $result } |