Use a combination of Group-Object
, Sort-Object
, and a do ... while
loop:
# Sample dataset.
$dataset = 1, 2, 2, 3, 4, 4, 5
# Group the same numbers and sort the groups by member count, highest counts first.
$groups = $dataset | Group-Object | Sort-Object Count -Descending
# Output only the numbers represented by those groups that have
# the highest member count.
$i = 0
do { $groups[$i].Group[0] } while ($groups[++$i].Count -eq $groups[0].Count)
The above yields 2
and 4
, which are the two modes (values occurring most frequently, twice each in this case), sorted in ascending order (because Group-Object
sorts by the grouping criterion and Sort-Object
's sorting algorithm is stable).
Note: While this solution is conceptually straightforward, performance with large datasets may be a concern; see the bottom section for an optimization that is possible for certain inputs.
Explanation:
Group-Object
groups all inputs by equality.
Sort-Object -Descending
sorts the resulting groups by member count in descending fashion (most frequently occurring inputs first).
The do ... while
statement loops over the sorted groups and outputs the input represented by each as long as the group-member and therefore occurrence count (frequency) is the highest, as implied by the first group's member count.
Better-performing solution, with strings and numbers:
If the input elements are uniformly simple numbers or strings (as opposed to complex objects), an optimization is possible:
Group-Object
's -NoElement
suppresses collecting the individual inputs in each group.
Each group's .Name
property reflects the grouping value, but does so as a string, so it must be converted back to its original data type.
# Sample dataset.
# Must be composed of all numbers or strings.
$dataset = 1, 2, 2, 3, 4, 4, 5
# Determine the data type of the elements of the dataset via its first element.
# All elements are assumed to be of the same type.
$type = $dataset[0].GetType()
# Group the same numbers and sort the groups by member count, highest counts first.
$groups = $dataset | Group-Object -NoElement | Sort-Object Count -Descending
# Output only the numbers represented by those groups that have
# the highest member count.
# -as $type converts the .Name string value back to the original type.
$i = 0
do { $groups[$i].Name -as $type } while ($groups[++$i].Count -eq $groups[0].Count)