Add -Stable to Sort-Object and related tests (#7862)

This commit is contained in:
Kirk Munro 2019-01-15 21:14:39 -04:00 committed by Aditya Patwardhan
parent be4b82c730
commit 41d9667307
2 changed files with 103 additions and 20 deletions

View file

@ -16,8 +16,20 @@ namespace Microsoft.PowerShell.Commands
public sealed class SortObjectCommand : OrderObjectBase
{
#region Command Line Switches
/// <summary>
/// This param specifies if sort order is ascending.
/// Gets or sets a value indicating whether a stable sort is required.
/// </summary>
/// <value></value>
/// <remarks>
/// Items that are duplicates according to the sort algorithm will appear
/// in the same relative order in a stable sort.
/// </remarks>
[Parameter(ParameterSetName = "Default")]
public SwitchParameter Stable { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the sort order is descending.
/// </summary>
[Parameter]
public SwitchParameter Descending
@ -26,30 +38,25 @@ namespace Microsoft.PowerShell.Commands
set { DescendingOrder = value; }
}
/// <summary>
/// This param specifies if only unique objects are filtered.
/// Gets or sets a value indicating whether the sort filters out any duplicate objects.
/// </summary>
/// <value></value>
[Parameter]
public SwitchParameter Unique
{
get { return _unique; }
public SwitchParameter Unique { get; set; }
set { _unique = value; }
}
private bool _unique;
#endregion
/// <summary>
/// This param specifies you only want the top N items returned.
/// Gets or sets the number of items to return in a Top N sort.
/// </summary>
[Parameter(ParameterSetName = "Default")]
[Parameter(ParameterSetName = "Top", Mandatory = true)]
[ValidateRange(1, int.MaxValue)]
public int Top { get; set; } = 0;
/// <summary>
/// This param specifies you only want the bottom N items returned.
/// Gets or sets the number of items to return in a Bottom N sort.
/// </summary>
[Parameter(ParameterSetName = "Bottom", Mandatory = true)]
[ValidateRange(1, int.MaxValue)]
@ -99,7 +106,7 @@ namespace Microsoft.PowerShell.Commands
// versions of PowerShell).
dataToSort.Sort(comparer);
if (_unique)
if (Unique)
{
// Move unique entries to the front of the list (this is significantly faster
// than removing them)
@ -119,7 +126,8 @@ namespace Microsoft.PowerShell.Commands
// Identify how many items will be in the heap and the current number of items
int heapCount = 0;
int heapCapacity = Top > 0 ? Top : Bottom;
int heapCapacity = Stable ? int.MaxValue
: Top > 0 ? Top : Bottom;
// Identify the comparator (the value all comparisons will be made against based on whether we're
// doing a Top N or Bottom N sort)
@ -135,7 +143,7 @@ namespace Microsoft.PowerShell.Commands
int comparator = Top > 0 ? -1 : 1;
// For unique sorts, use a sorted set to avoid adding unique items to the heap
SortedSet<OrderByPropertyEntry> uniqueSet = _unique ? new SortedSet<OrderByPropertyEntry>(orderByPropertyComparer) : null;
SortedSet<OrderByPropertyEntry> uniqueSet = Unique ? new SortedSet<OrderByPropertyEntry>(orderByPropertyComparer) : null;
// Tracking the index is necessary so that unsortable items can be output at the end, in the order
// in which they were received.
@ -149,7 +157,7 @@ namespace Microsoft.PowerShell.Commands
}
// If we're doing a unique sort and the entry is not unique, discard the duplicate entry
if (_unique && !uniqueSet.Add(dataToSort[dataIndex]))
if (Unique && !uniqueSet.Add(dataToSort[dataIndex]))
{
discardedDuplicates++;
if (dataIndex != dataToSort.Count - discardedDuplicates)
@ -244,13 +252,14 @@ namespace Microsoft.PowerShell.Commands
// Track the number of items that will be output from the data once it is sorted
int sortedItemCount = dataToProcess.Count;
// If -Top & -Bottom were not used, or if -Top or -Bottom would return all objects, invoke
// an in-place full sort
if ((Top == 0 && Bottom == 0) || Top >= dataToProcess.Count || Bottom >= dataToProcess.Count)
// If -Stable, -Top & -Bottom were not used, invoke an in-place full sort
if (!Stable && Top == 0 && Bottom == 0)
{
sortedItemCount = FullSort(dataToProcess, comparer);
}
// Otherwise, use an indexed min-/max-heap to perform an in-place sort of all objects
// Otherwise, use an indexed min-/max-heap to perform an in-place heap sort (heap
// sorts are inheritantly stable, meaning they will preserve the respective order
// of duplicate objects as they are sorted on the heap)
else
{
sortedItemCount = Heapify(dataToProcess, comparer);

View file

@ -225,6 +225,80 @@ Describe "Sort-Object DRT Unit Tests" -Tags "CI" {
}
}
Describe 'Sort-Object Stable Unit Tests' -Tags 'CI' {
Context 'Modulo stable sort' {
$unsortedData = 1..20
It 'Return each value in an ordered set, sorted by the value modulo 3, with items having the same result appearing in the same order' {
$results = $unsortedData | Sort-Object {$_ % 3} -Stable
$results[0] | Should -Be 3
$results[1] | Should -Be 6
$results[2] | Should -Be 9
$results[3] | Should -Be 12
$results[4] | Should -Be 15
$results[5] | Should -Be 18
$results[6] | Should -Be 1
$results[7] | Should -Be 4
$results[8] | Should -Be 7
$results[9] | Should -Be 10
$results[10] | Should -Be 13
$results[11] | Should -Be 16
$results[12] | Should -Be 19
$results[13] | Should -Be 2
$results[14] | Should -Be 5
$results[15] | Should -Be 8
$results[16] | Should -Be 11
$results[17] | Should -Be 14
$results[18] | Should -Be 17
$results[19] | Should -Be 20
}
It 'Return each value in an ordered set, sorted by the value modulo 3 (descending), with items having the same result appearing in the same order' {
$results = $unsortedData | Sort-Object {$_ % 3} -Stable -Descending
$results[0] | Should -Be 2
$results[1] | Should -Be 5
$results[2] | Should -Be 8
$results[3] | Should -Be 11
$results[4] | Should -Be 14
$results[5] | Should -Be 17
$results[6] | Should -Be 20
$results[7] | Should -Be 1
$results[8] | Should -Be 4
$results[9] | Should -Be 7
$results[10] | Should -Be 10
$results[11] | Should -Be 13
$results[12] | Should -Be 16
$results[13] | Should -Be 19
$results[14] | Should -Be 3
$results[15] | Should -Be 6
$results[16] | Should -Be 9
$results[17] | Should -Be 12
$results[18] | Should -Be 15
$results[19] | Should -Be 18
}
It 'Return each value in an ordered set, sorted by the value modulo 3, discarding duplicates' {
$results = $unsortedData | Sort-Object {$_ % 3} -Stable -Unique
$results[0] | Should -Be 3
$results[1] | Should -Be 1
$results[2] | Should -Be 2
}
It 'Return each value in an ordered set, sorted by the value modulo 3 (descending), discarding duplicates' {
$results = $unsortedData | Sort-Object {$_ % 3} -Stable -Unique -Descending
$results[0] | Should -Be 2
$results[1] | Should -Be 1
$results[2] | Should -Be 3
}
}
}
Describe 'Sort-Object Top and Bottom Unit Tests' -Tags 'CI' {
# Helper function to compare two sort entries