Friday Fun – Get Ranked Object

Earlier this week on Google Plus, Hal Rottenberg posted a PowerShell idea he had. His goal was to identify a group of objects that would be statistically significant. For example, given a collection of files, group the files by size but ranked by size so that you might have a group for the largest files, then big files, then average files and finally small files. His idea was to calculate a rank using a logarithm of the file size. His code was quick and dirty so I took it, as I usually do, and ran with it. The result is a function that will analyse any group of objects and rank them based on a numeric property.

The essence of the function is to take the property value and calculate its log using the largest property value as the base. If we use a file as an example, suppose the largest file size is 12500. Calculating the log of other file size with this as the base provides results like this:


PS S:\> [math]::log(12345,12500)
0.998677315654261
PS S:\> [math]::log(1244,12500)
0.755403553050584
PS S:\> [math]::log(123,12500)
0.510117406729404
PS S:\> [math]::log(90,12500)
0.47700401112898
PS S:\>

The larger the file, the higher the log which we can use as a rank. In my final code I multiply this value by 10.


[int32]$Itemrank=[math]::log($item.$property,$base)*10

I wanted the function to be able to handle any type of object, as long as it had a numeric property you could analyze. Since I assumed most people would use it on file objects, I set the default property to be Length.


Function Get-RankedObject {

[cmdletbinding()]

Param(
[Parameter(Position=0)]
[ValidateNotNullOrEmpty()]
[string]$Property="Length",
[Parameter(Position=1,Mandatory=$True,HelpMessage="You must specify an object",ValueFromPipeline=$True)]
[ValidateNotNullOrEmpty()]
[object[]]$InputObject,
[ValidateScript({$_ -gt 0 -AND $_ -le 10})]
[int32]$Rank=1
)

Begin {
Write-Verbose "Starting $($myinvocation.myCommand)"
Write-Verbose "Analyzing objects based on $property property"
Write-Verbose "Writing objects to the pipeline with a ranking of at least $Rank"
#define the log base
$base=1
#define an empty array to hold processed objects
$data=@()
Write-Debug "End of Begin script block"
} #Begin

Process {

foreach ($item in $Inputobject) {
$data+=$item

if ($item.$property -gt $base) {
$base=$item.$property
Write-Debug "base=$base"
}

} #foreach
Write-Debug "End of Process script block"
} #Process

End {
Write-Debug "Processing each item in data"
Write-Verbose "Base value is $Base"
Foreach ($item in $data) {
#calculate the rank if the property value is greater than 0
Write-Debug "Ranking value $($item.$property)"
if ($item.$property -le 0) {
$ItemRank=0
}
else {
[int32]$Itemrank=[math]::log($item.$property,$base)*10
}
Write-Debug "ItemRank will be $ItemRank"

#add it to the item and write to pipeline if rank -ge $rank
Write-Debug "Adding Rank property to item"
$item | Add-Member -MemberType NoteProperty -Name Rank -Value $ItemRank

if ($item.Rank -ge $Rank) {
Write-Output $item
}
} #foreach

Write-Verbose "Processed $($data.count) objects"
Write-Verbose "Exiting $($myinvocation.mycommand)"
Write-Debug "End of End script block"

} #end

} #end function

The assumption is that you will pipe objects into the function. If you want to rank objects on a different property, then use the -Property parameter. As each object is analyzed I add a custom property called Rank.


$item | Add-Member -MemberType NoteProperty -Name Rank -Value $ItemRank

By default the function will write all objects to the pipeline. But if you prefer, you can use the -Rank parameter to filter out objects with a low rank. For example, if you run a command using -rank 8, then only objects with a rank value of 8 and above will be written to the pipeline. Here some examples of this function in use.

Command help includes another of other examples as well. I’m sure there are a number of ways you could determine what is significant, but this approach seems to work for me and has been pretty consistent. But I hope you’ll download it, try it out, and let me know. The download version will also create an alias, gro, for the function, so comment that out if you don’t want to use it.

Download Get-RankedObject.