Используйте комбинацию Group-Object
, Sort-Object
и ForEach-Object
:
# Sample dataset.
$dataset = 1, 2, 2, 3, 4, 4, 5
do { # dummy loop to allow efficient termination of the pipeline
$dataset | Group-Object | Sort-Object Count -Descending |
ForEach-Object -Begin { $topCount = 0 } -Process {
if ($_.Count -lt $topCount) { break } # No longer top occurrence count, exit
$topCount = $_.Count # Store the occurrence count.
$_.Group[0] # Output the input value represented by the group.
}
} while ($false)
. Выше приведены значения 2
и 4
, которые являются двумя режимами (значения, встречающиеся чаще всего)по два раза в этом случае);режимы возвращаются в порядке ввода.
Примечание. Хотя это решение является концептуально простым, производительность с большими наборами данных может быть проблемой;см. нижний раздел для оптимизации, которая возможна для определенных входных данных.
Объяснение:
Group-Object
группывсе входы по равенству.
Sort-Object -Descending
сортирует результирующие группы по количеству элементов в порядке убывания (сначала чаще всего встречаются входы).
Команда ForEach-Object
проходит по отсортированным группам и выводит вход, представленный каждым, для групп / all с наибольшим количеством вхождений (частотой).
Причина фиктивного цикла do
заключается в том, что в PowerShell Core 7.0.0-preview.5 отсутствует прямой способ выхода из конвейера при обработке дальнейших входных данных. больше не требуется.
Для добавления поддержки в GitHub существует давний запрос на добавление поддержки.
Обходной путь - использовать включающий loop и вырваться из него с помощью break
.
Примечание: не используйте break
или continue
в конвейере без замкнутого цикла, так как он ищет стек вызовов для замкнутого цикла и завершает работу сценария, если его нет.
В отличие от этого, тогда как return
может используется внутри блока ForEach-Object
в конвейере, он только пропускает к элементу ввода next - он не останавливает обработку дальнейших вводов.
Более эффективное решение:
Если входные элементы равномерно простые числа или строки (в отличие от сложных объектов), возможна оптимизация:
Group-Object
-NoElement
подавляет сбор отдельных входных данных в каждой группе.
Свойство .Name
каждой группы отражает значение группировки, но делает это так: строка , поэтому его необходимо преобразовать обратно в исходный тип данных.
# 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 in the set
# (assumed to be homogeneous).
$dataType = $dataset[0].GetType()
do {
# Note the use of -NoElement
$dataset | Group-Object -NoElement | Sort-Object Count -Descending |
ForEach-Object -Begin { $topCount = 0 } -Process {
if ($_.Count -lt $topCount) { break }
$topCount = $_.Count # Store the occurrence count.
# Convert the string-valued .Name property
# back to the original type.
if ($dataType -eq [string]) {
$_.Name
} else {
$dataType::Parse($_.Name)
}
}
} while ($false)