Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ImmutableArray extension methods for ordering #10608

Merged
merged 8 commits into from
Jul 16, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,102 @@ public void GetMostRecentUniqueItems()
},
s => Assert.Equal("WoRlD", s));
}

public static TheoryData<ImmutableArray<int>, ImmutableArray<int>> OrderAsArrayData
{
get
{
return new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
}
}

[Theory]
[MemberData(nameof(OrderAsArrayData))]
public void OrderAsArray(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}

public static TheoryData<ImmutableArray<int>, ImmutableArray<int>> OrderDescendingAsArrayData
{
get
{
return new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
}
}

[Theory]
[MemberData(nameof(OrderAsArrayData))]
public void OrderDescendingAsArray(ImmutableArray<int> data, ImmutableArray<int> expected)
{
var sorted = data.OrderAsArray();
Assert.Equal<int>(expected, sorted);
}

public readonly record struct ValueHolder(int Value)
{
public static implicit operator ValueHolder(int value)
=> new(value);
}

public static TheoryData<ImmutableArray<ValueHolder>, ImmutableArray<ValueHolder>> OrderByAsArrayData
{
get
{
return new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] },
};
}
}

[Theory]
[MemberData(nameof(OrderByAsArrayData))]
public void OrderByAsArray(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}

public static TheoryData<ImmutableArray<ValueHolder>, ImmutableArray<ValueHolder>> OrderByDescendingAsArrayData
{
get
{
return new()
{
{ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [1, 3, 5, 7, 9, 2, 4, 6, 8, 10], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [2, 5, 8, 1, 3, 9, 7, 4, 10, 6], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
{ [6, 10, 4, 7, 9, 3, 1, 8, 5, 2], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] },
};
}
}

[Theory]
[MemberData(nameof(OrderByDescendingAsArrayData))]
public void OrderByDescendingAsArray(ImmutableArray<ValueHolder> data, ImmutableArray<ValueHolder> expected)
{
var sorted = data.OrderByDescendingAsArray(static x => x.Value);
Assert.Equal<ValueHolder>(expected, sorted);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Buffers;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build macOS release)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build macOS release)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build macOS release)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build macOS debug)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build macOS debug)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build macOS debug)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build Linux release)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build Linux release)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build Linux release)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build Linux debug)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build Linux debug)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci (Build Linux debug)

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 7 in src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs

View check run for this annotation

Azure Pipelines / razor-tooling-ci

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs#L7

src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs(7,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.PooledObjects;

namespace System.Collections.Immutable;
Expand Down Expand Up @@ -205,4 +209,123 @@

return ~min;
}

public static ImmutableArray<T> OrderAsArray<T>(this ImmutableArray<T> array)
=> array.OrderAsArrayCore(GetComparer<T>(comparer: null, descending: false));

public static ImmutableArray<T> OrderAsArray<T>(this ImmutableArray<T> array, IComparer<T> comparer)
=> array.OrderAsArrayCore(GetComparer(comparer, descending: false));

public static ImmutableArray<T> OrderAsArray<T>(this ImmutableArray<T> array, Comparison<T> comparison)
=> array.OrderAsArrayCore(GetComparer(comparison, descending: false));

public static ImmutableArray<T> OrderDescendingAsArray<T>(this ImmutableArray<T> array)
=> array.OrderAsArrayCore(GetComparer<T>(comparer: null, descending: true));

public static ImmutableArray<T> OrderDescendingAsArray<T>(this ImmutableArray<T> array, IComparer<T> comparer)
=> array.OrderAsArrayCore(GetComparer(comparer, descending: true));

public static ImmutableArray<T> OrderDescendingAsArray<T>(this ImmutableArray<T> array, Comparison<T> comparison)
=> array.OrderAsArrayCore(GetComparer(comparison, descending: true));

public static ImmutableArray<TElement> OrderByAsArray<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector)
=> array.OrderByAsArrayCore(keySelector, GetComparer<TKey>(comparer: null, descending: false));

public static ImmutableArray<TElement> OrderByAsArray<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector, IComparer<TKey> comparer)
=> array.OrderByAsArrayCore(keySelector, GetComparer(comparer, descending: false));

public static ImmutableArray<TElement> OrderByAsArray<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector, Comparison<TKey> comparison)
=> array.OrderByAsArrayCore(keySelector, GetComparer(comparison, descending: false));

public static ImmutableArray<TElement> OrderByDescendingAsArray<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector)
=> array.OrderByAsArrayCore(keySelector, GetComparer<TKey>(comparer: null, descending: true));

public static ImmutableArray<TElement> OrderByDescendingAsArray<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector, IComparer<TKey> comparer)
=> array.OrderByAsArrayCore(keySelector, GetComparer(comparer, descending: true));

public static ImmutableArray<TElement> OrderByDescendingAsArray<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector, Comparison<TKey> comparison)
=> array.OrderByAsArrayCore(keySelector, GetComparer(comparison, descending: true));

private static IComparer<T> GetComparer<T>(IComparer<T>? comparer, bool descending)
{
if (comparer is null)
{
return descending
? DescendingComparer<T>.Default
: Comparer<T>.Default;
}

return descending
? new DescendingComparer<T>(comparer)
: comparer;
}

private static IComparer<T> GetComparer<T>(Comparison<T> comparison, bool descending)
{
var comparer = Comparer<T>.Create(comparison);

return descending
? new DescendingComparer<T>(comparer)
: comparer;
}

private sealed class DescendingComparer<T>(IComparer<T> comparer) : IComparer<T>
{
private static IComparer<T>? s_default;

public static IComparer<T> Default => s_default ??= new DescendingComparer<T>(Comparer<T>.Default);

public int Compare(T? x, T? y)
=> comparer.Compare(y!, x!);
}

private static ImmutableArray<T> OrderAsArrayCore<T>(this ImmutableArray<T> array, IComparer<T> comparer)
{
if (array.IsEmpty)
{
return array;
}

var span = array.AsSpan();

var length = span.Length;
var items = new T[length];
span.CopyTo(items);

Array.Sort(items, comparer);

return ImmutableCollectionsMarshal.AsImmutableArray(items);
}

private static ImmutableArray<TElement> OrderByAsArrayCore<TElement, TKey>(
this ImmutableArray<TElement> array, Func<TElement, TKey> keySelector, IComparer<TKey> comparer)
{
if (array.IsEmpty)
{
return array;
}

var span = array.AsSpan();

var length = span.Length;
var items = new TElement[length];
span.CopyTo(items);

using var _ = ArrayPool<TKey>.Shared.GetPooledArray(minimumLength: length, out var keys);

for (var i = 0; i < length; i++)
{
keys[i] = keySelector(items[i]);
}

Array.Sort(keys, items, 0, length, comparer);

return ImmutableCollectionsMarshal.AsImmutableArray(items);
}
}
Loading