From 85dc70a3ab28eac35df15ef66ffdf7b60f20759b Mon Sep 17 00:00:00 2001 From: neuecc Date: Tue, 12 May 2020 02:21:06 +0900 Subject: [PATCH] OrderBy --- src/UniTask.NetCore/IAsyncEnumerable.cs | 2 + src/UniTask.NetCore/Linq/OrderBy.cs | 467 ++++++++++++++++++++++-- src/UniTask.NetCore/Linq/_FileMaker.cs | 120 +----- src/UniTask.NetCoreTests/Linq/Sort.cs | 221 +++++++++++ 4 files changed, 665 insertions(+), 145 deletions(-) create mode 100644 src/UniTask.NetCoreTests/Linq/Sort.cs diff --git a/src/UniTask.NetCore/IAsyncEnumerable.cs b/src/UniTask.NetCore/IAsyncEnumerable.cs index 757333d..6807908 100644 --- a/src/UniTask.NetCore/IAsyncEnumerable.cs +++ b/src/UniTask.NetCore/IAsyncEnumerable.cs @@ -24,6 +24,8 @@ namespace Cysharp.Threading.Tasks public interface IUniTaskOrderedAsyncEnumerable : IUniTaskAsyncEnumerable { IUniTaskOrderedAsyncEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending); + IUniTaskOrderedAsyncEnumerable CreateOrderedEnumerable(Func> keySelector, IComparer comparer, bool descending); + IUniTaskOrderedAsyncEnumerable CreateOrderedEnumerable(Func> keySelector, IComparer comparer, bool descending); } //public interface IUniTaskAsyncGrouping : IUniTaskAsyncEnumerable diff --git a/src/UniTask.NetCore/Linq/OrderBy.cs b/src/UniTask.NetCore/Linq/OrderBy.cs index 8bca66f..740c0e2 100644 --- a/src/UniTask.NetCore/Linq/OrderBy.cs +++ b/src/UniTask.NetCore/Linq/OrderBy.cs @@ -1,10 +1,8 @@ using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks.Internal; -using Cysharp.Threading.Tasks.Linq; using System; using System.Collections.Generic; using System.Threading; -using System.Threading.Tasks; namespace Cysharp.Threading.Tasks.Linq { @@ -14,62 +12,104 @@ namespace Cysharp.Threading.Tasks.Linq public static IUniTaskOrderedAsyncEnumerable OrderBy(this IUniTaskAsyncEnumerable source, Func keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return new OrderedAsyncEnumerable(source, keySelector, Comparer.Default, false, null); } public static IUniTaskOrderedAsyncEnumerable OrderBy(this IUniTaskAsyncEnumerable source, Func keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return new OrderedAsyncEnumerable(source, keySelector, comparer, false, null); } public static IUniTaskOrderedAsyncEnumerable OrderByAwait(this IUniTaskAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return new OrderedAsyncEnumerableAwait(source, keySelector, Comparer.Default, false, null); } public static IUniTaskOrderedAsyncEnumerable OrderByAwait(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return new OrderedAsyncEnumerableAwait(source, keySelector, comparer, false, null); } public static IUniTaskOrderedAsyncEnumerable OrderByAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return new OrderedAsyncEnumerableAwaitWithCancellation(source, keySelector, Comparer.Default, false, null); } public static IUniTaskOrderedAsyncEnumerable OrderByAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return new OrderedAsyncEnumerableAwaitWithCancellation(source, keySelector, comparer, false, null); } public static IUniTaskOrderedAsyncEnumerable OrderByDescending(this IUniTaskAsyncEnumerable source, Func keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return new OrderedAsyncEnumerable(source, keySelector, Comparer.Default, true, null); } public static IUniTaskOrderedAsyncEnumerable OrderByDescending(this IUniTaskAsyncEnumerable source, Func keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return new OrderedAsyncEnumerable(source, keySelector, comparer, true, null); } public static IUniTaskOrderedAsyncEnumerable OrderByDescendingAwait(this IUniTaskAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return new OrderedAsyncEnumerableAwait(source, keySelector, Comparer.Default, true, null); } public static IUniTaskOrderedAsyncEnumerable OrderByDescendingAwait(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return new OrderedAsyncEnumerableAwait(source, keySelector, comparer, true, null); } public static IUniTaskOrderedAsyncEnumerable OrderByDescendingAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return new OrderedAsyncEnumerableAwaitWithCancellation(source, keySelector, Comparer.Default, true, null); } public static IUniTaskOrderedAsyncEnumerable OrderByDescendingAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return new OrderedAsyncEnumerableAwaitWithCancellation(source, keySelector, comparer, true, null); } #endregion @@ -78,64 +118,439 @@ namespace Cysharp.Threading.Tasks.Linq public static IUniTaskOrderedAsyncEnumerable ThenBy(this IUniTaskOrderedAsyncEnumerable source, Func keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return source.CreateOrderedEnumerable(keySelector, Comparer.Default, false); } public static IUniTaskOrderedAsyncEnumerable ThenBy(this IUniTaskOrderedAsyncEnumerable source, Func keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return source.CreateOrderedEnumerable(keySelector, comparer, false); } public static IUniTaskOrderedAsyncEnumerable ThenByAwait(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return source.CreateOrderedEnumerable(keySelector, Comparer.Default, false); } public static IUniTaskOrderedAsyncEnumerable ThenByAwait(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return source.CreateOrderedEnumerable(keySelector, comparer, false); } public static IUniTaskOrderedAsyncEnumerable ThenByAwaitWithCancellation(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return source.CreateOrderedEnumerable(keySelector, Comparer.Default, false); } public static IUniTaskOrderedAsyncEnumerable ThenByAwaitWithCancellation(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return source.CreateOrderedEnumerable(keySelector, comparer, false); } public static IUniTaskOrderedAsyncEnumerable ThenByDescending(this IUniTaskOrderedAsyncEnumerable source, Func keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return source.CreateOrderedEnumerable(keySelector, Comparer.Default, true); } public static IUniTaskOrderedAsyncEnumerable ThenByDescending(this IUniTaskOrderedAsyncEnumerable source, Func keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return source.CreateOrderedEnumerable(keySelector, comparer, true); } public static IUniTaskOrderedAsyncEnumerable ThenByDescendingAwait(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return source.CreateOrderedEnumerable(keySelector, Comparer.Default, true); } public static IUniTaskOrderedAsyncEnumerable ThenByDescendingAwait(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return source.CreateOrderedEnumerable(keySelector, comparer, true); } public static IUniTaskOrderedAsyncEnumerable ThenByDescendingAwaitWithCancellation(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + + return source.CreateOrderedEnumerable(keySelector, Comparer.Default, true); } public static IUniTaskOrderedAsyncEnumerable ThenByDescendingAwaitWithCancellation(this IUniTaskOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) { - throw new NotImplementedException(); + Error.ThrowArgumentNullException(source, nameof(source)); + Error.ThrowArgumentNullException(keySelector, nameof(keySelector)); + Error.ThrowArgumentNullException(comparer, nameof(comparer)); + + return source.CreateOrderedEnumerable(keySelector, comparer, true); } #endregion } + + internal abstract class AsyncEnumerableSorter + { + internal abstract UniTask ComputeKeysAsync(TElement[] elements, int count); + + internal abstract int CompareKeys(int index1, int index2); + + internal async UniTask SortAsync(TElement[] elements, int count) + { + await ComputeKeysAsync(elements, count); + + int[] map = new int[count]; + for (int i = 0; i < count; i++) map[i] = i; + QuickSort(map, 0, count - 1); + return map; + } + + void QuickSort(int[] map, int left, int right) + { + do + { + int i = left; + int j = right; + int x = map[i + ((j - i) >> 1)]; + do + { + while (i < map.Length && CompareKeys(x, map[i]) > 0) i++; + while (j >= 0 && CompareKeys(x, map[j]) < 0) j--; + if (i > j) break; + if (i < j) + { + int temp = map[i]; + map[i] = map[j]; + map[j] = temp; + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) + { + if (left < j) QuickSort(map, left, j); + left = i; + } + else + { + if (i < right) QuickSort(map, i, right); + right = j; + } + } while (left < right); + } + } + + internal class SyncSelectorAsyncEnumerableSorter : AsyncEnumerableSorter + { + readonly Func keySelector; + readonly IComparer comparer; + readonly bool descending; + readonly AsyncEnumerableSorter next; + TKey[] keys; + + internal SyncSelectorAsyncEnumerableSorter(Func keySelector, IComparer comparer, bool descending, AsyncEnumerableSorter next) + { + this.keySelector = keySelector; + this.comparer = comparer; + this.descending = descending; + this.next = next; + } + + internal override async UniTask ComputeKeysAsync(TElement[] elements, int count) + { + keys = new TKey[count]; + for (int i = 0; i < count; i++) keys[i] = keySelector(elements[i]); + if (next != null) await next.ComputeKeysAsync(elements, count); + } + + internal override int CompareKeys(int index1, int index2) + { + int c = comparer.Compare(keys[index1], keys[index2]); + if (c == 0) + { + if (next == null) return index1 - index2; + return next.CompareKeys(index1, index2); + } + return descending ? -c : c; + } + } + + internal class AsyncSelectorEnumerableSorter : AsyncEnumerableSorter + { + readonly Func> keySelector; + readonly IComparer comparer; + readonly bool descending; + readonly AsyncEnumerableSorter next; + TKey[] keys; + + internal AsyncSelectorEnumerableSorter(Func> keySelector, IComparer comparer, bool descending, AsyncEnumerableSorter next) + { + this.keySelector = keySelector; + this.comparer = comparer; + this.descending = descending; + this.next = next; + } + + internal override async UniTask ComputeKeysAsync(TElement[] elements, int count) + { + keys = new TKey[count]; + for (int i = 0; i < count; i++) keys[i] = await keySelector(elements[i]); + if (next != null) await next.ComputeKeysAsync(elements, count); + } + + internal override int CompareKeys(int index1, int index2) + { + int c = comparer.Compare(keys[index1], keys[index2]); + if (c == 0) + { + if (next == null) return index1 - index2; + return next.CompareKeys(index1, index2); + } + return descending ? -c : c; + } + } + + internal class AsyncSelectorWithCancellationEnumerableSorter : AsyncEnumerableSorter + { + readonly Func> keySelector; + readonly IComparer comparer; + readonly bool descending; + readonly AsyncEnumerableSorter next; + CancellationToken cancellationToken; + TKey[] keys; + + internal AsyncSelectorWithCancellationEnumerableSorter(Func> keySelector, IComparer comparer, bool descending, AsyncEnumerableSorter next, CancellationToken cancellationToken) + { + this.keySelector = keySelector; + this.comparer = comparer; + this.descending = descending; + this.next = next; + this.cancellationToken = cancellationToken; + } + + internal override async UniTask ComputeKeysAsync(TElement[] elements, int count) + { + keys = new TKey[count]; + for (int i = 0; i < count; i++) keys[i] = await keySelector(elements[i], cancellationToken); + if (next != null) await next.ComputeKeysAsync(elements, count); + } + + internal override int CompareKeys(int index1, int index2) + { + int c = comparer.Compare(keys[index1], keys[index2]); + if (c == 0) + { + if (next == null) return index1 - index2; + return next.CompareKeys(index1, index2); + } + return descending ? -c : c; + } + } + + internal abstract class OrderedAsyncEnumerable : IUniTaskOrderedAsyncEnumerable + { + protected readonly IUniTaskAsyncEnumerable source; + + public OrderedAsyncEnumerable(IUniTaskAsyncEnumerable source) + { + this.source = source; + } + + public IUniTaskOrderedAsyncEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) + { + return new OrderedAsyncEnumerable(source, keySelector, comparer, descending, this); + } + + public IUniTaskOrderedAsyncEnumerable CreateOrderedEnumerable(Func> keySelector, IComparer comparer, bool descending) + { + return new OrderedAsyncEnumerableAwait(source, keySelector, comparer, descending, this); + } + + public IUniTaskOrderedAsyncEnumerable CreateOrderedEnumerable(Func> keySelector, IComparer comparer, bool descending) + { + return new OrderedAsyncEnumerableAwaitWithCancellation(source, keySelector, comparer, descending, this); + } + + internal abstract AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next, CancellationToken cancellationToken); + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(this, cancellationToken); + } + + class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator + { + protected readonly OrderedAsyncEnumerable parent; + CancellationToken cancellationToken; + TElement[] buffer; + int[] map; + int index; + + public Enumerator(OrderedAsyncEnumerable parent, CancellationToken cancellationToken) + { + this.parent = parent; + this.cancellationToken = cancellationToken; + } + + public TElement Current { get; private set; } + + public UniTask MoveNextAsync() + { + cancellationToken.ThrowIfCancellationRequested(); + + if (map == null) + { + completionSource.Reset(); + CreateSortSource().Forget(); + return new UniTask(this, completionSource.Version); + } + + if (index < buffer.Length) + { + Current = buffer[map[index++]]; + return CompletedTasks.True; + } + else + { + return CompletedTasks.False; + } + } + + async UniTaskVoid CreateSortSource() + { + try + { + buffer = await parent.source.ToArrayAsync(); + if (buffer.Length == 0) + { + completionSource.TrySetResult(false); + return; + } + + var sorter = parent.GetAsyncEnumerableSorter(null, cancellationToken); + map = await sorter.SortAsync(buffer, buffer.Length); + sorter = null; + + // set first value + Current = buffer[map[index++]]; + } + catch (Exception ex) + { + completionSource.TrySetException(ex); + return; + } + + completionSource.TrySetResult(true); + } + + public UniTask DisposeAsync() + { + return default; + } + } + } + + internal class OrderedAsyncEnumerable : OrderedAsyncEnumerable + { + readonly Func keySelector; + readonly IComparer comparer; + readonly bool descending; + readonly OrderedAsyncEnumerable parent; + + public OrderedAsyncEnumerable(IUniTaskAsyncEnumerable source, Func keySelector, IComparer comparer, bool descending, OrderedAsyncEnumerable parent) + : base(source) + { + this.keySelector = keySelector; + this.comparer = comparer; + this.descending = descending; + this.parent = parent; + } + + internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next, CancellationToken cancellationToken) + { + AsyncEnumerableSorter sorter = new SyncSelectorAsyncEnumerableSorter(keySelector, comparer, descending, next); + if (parent != null) sorter = parent.GetAsyncEnumerableSorter(sorter, cancellationToken); + return sorter; + } + } + + internal class OrderedAsyncEnumerableAwait : OrderedAsyncEnumerable + { + readonly Func> keySelector; + readonly IComparer comparer; + readonly bool descending; + readonly OrderedAsyncEnumerable parent; + + public OrderedAsyncEnumerableAwait(IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer, bool descending, OrderedAsyncEnumerable parent) + : base(source) + { + this.keySelector = keySelector; + this.comparer = comparer; + this.descending = descending; + this.parent = parent; + } + + internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next, CancellationToken cancellationToken) + { + AsyncEnumerableSorter sorter = new AsyncSelectorEnumerableSorter(keySelector, comparer, descending, next); + if (parent != null) sorter = parent.GetAsyncEnumerableSorter(sorter, cancellationToken); + return sorter; + } + } + + internal class OrderedAsyncEnumerableAwaitWithCancellation : OrderedAsyncEnumerable + { + readonly Func> keySelector; + readonly IComparer comparer; + readonly bool descending; + readonly OrderedAsyncEnumerable parent; + + public OrderedAsyncEnumerableAwaitWithCancellation(IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer, bool descending, OrderedAsyncEnumerable parent) + : base(source) + { + this.keySelector = keySelector; + this.comparer = comparer; + this.descending = descending; + this.parent = parent; + } + + internal override AsyncEnumerableSorter GetAsyncEnumerableSorter(AsyncEnumerableSorter next, CancellationToken cancellationToken) + { + AsyncEnumerableSorter sorter = new AsyncSelectorWithCancellationEnumerableSorter(keySelector, comparer, descending, next, cancellationToken); + if (parent != null) sorter = parent.GetAsyncEnumerableSorter(sorter, cancellationToken); + return sorter; + } + } } \ No newline at end of file diff --git a/src/UniTask.NetCore/Linq/_FileMaker.cs b/src/UniTask.NetCore/Linq/_FileMaker.cs index bd80f4d..4f1b224 100644 --- a/src/UniTask.NetCore/Linq/_FileMaker.cs +++ b/src/UniTask.NetCore/Linq/_FileMaker.cs @@ -36,125 +36,7 @@ namespace ___Dummy - public static IOrderedAsyncEnumerable OrderBy(this IUniTaskAsyncEnumerable source, Func keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderBy(this IUniTaskAsyncEnumerable source, Func keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByAwait(this IUniTaskAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByAwait(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByDescending(this IUniTaskAsyncEnumerable source, Func keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByDescending(this IUniTaskAsyncEnumerable source, Func keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByDescendingAwait(this IUniTaskAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByDescendingAwait(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByDescendingAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable OrderByDescendingAwaitWithCancellation(this IUniTaskAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenBy(this IOrderedAsyncEnumerable source, Func keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenBy(this IOrderedAsyncEnumerable source, Func keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByAwait(this IOrderedAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByAwait(this IOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByAwaitWithCancellation(this IOrderedAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByAwaitWithCancellation(this IOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByDescending(this IOrderedAsyncEnumerable source, Func keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByDescending(this IOrderedAsyncEnumerable source, Func keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByDescendingAwait(this IOrderedAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByDescendingAwait(this IOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByDescendingAwaitWithCancellation(this IOrderedAsyncEnumerable source, Func> keySelector) - { - throw new NotImplementedException(); - } - - public static IOrderedAsyncEnumerable ThenByDescendingAwaitWithCancellation(this IOrderedAsyncEnumerable source, Func> keySelector, IComparer comparer) - { - throw new NotImplementedException(); - } + diff --git a/src/UniTask.NetCoreTests/Linq/Sort.cs b/src/UniTask.NetCoreTests/Linq/Sort.cs new file mode 100644 index 0000000..673e697 --- /dev/null +++ b/src/UniTask.NetCoreTests/Linq/Sort.cs @@ -0,0 +1,221 @@ +using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks.Linq; +using FluentAssertions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + + +namespace NetCoreTests.Linq +{ + public class SortCheck + { + public int Age { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + + public override string ToString() + { + return (Age, FirstName, LastName).ToString(); + } + } + + public class Sort + { + static int rd; + + static UniTask RandomRun(T value) + { + if (Interlocked.Increment(ref rd) % 2 == 0) + { + return UniTask.Run(() => value); + } + else + { + return UniTask.FromResult(value); + } + } + static UniTask RandomRun(T value, CancellationToken ct) + { + if (Interlocked.Increment(ref rd) % 2 == 0) + { + return UniTask.Run(() => value); + } + else + { + return UniTask.FromResult(value); + } + } + + [Fact] + public async Task OrderBy() + { + var array = new[] { 1, 99, 32, 4, 536, 7, 8 }; + { + var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync(); + var ys = array.OrderBy(x => x).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync(); + var ys = array.OrderByDescending(x => x).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync(); + var ys = array.OrderBy(x => x).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync(); + var ys = array.OrderByDescending(x => x).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync(); + var ys = array.OrderBy(x => x).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + { + var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync(); + var ys = array.OrderByDescending(x => x).ToArray(); + xs.Should().BeEquivalentTo(ys); + } + } + + [Fact] + public async Task ThenBy() + { + var array = new[] + { + new SortCheck { Age = 99, FirstName = "ABC", LastName = "DEF" }, + new SortCheck { Age = 49, FirstName = "ABC", LastName = "DEF" }, + new SortCheck { Age = 49, FirstName = "ABC", LastName = "ZKH" }, + new SortCheck { Age = 12, FirstName = "ABC", LastName = "DEF" }, + new SortCheck { Age = 49, FirstName = "ABC", LastName = "MEF" }, + new SortCheck { Age = 12, FirstName = "QQQ", LastName = "DEF" }, + new SortCheck { Age = 19, FirstName = "ZKN", LastName = "DEF" }, + new SortCheck { Age = 39, FirstName = "APO", LastName = "REF" }, + new SortCheck { Age = 59, FirstName = "ABC", LastName = "DEF" }, + new SortCheck { Age = 99, FirstName = "DBC", LastName = "DEF" }, + new SortCheck { Age = 99, FirstName = "DBC", LastName = "MEF" }, + }; + { + var a = array.OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArray(); + var b = array.OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray(); + var c = array.OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArray(); + var d = array.OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray(); + var e = array.OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArray(); + var f = array.OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray(); + var g = array.OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArray(); + var h = array.OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray(); + + { + var a2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync(); + var b2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync(); + var c2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync(); + var d2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync(); + var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync(); + var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync(); + var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync(); + var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync(); + + a.Should().BeEquivalentTo(a2); + b.Should().BeEquivalentTo(b2); + c.Should().BeEquivalentTo(c2); + d.Should().BeEquivalentTo(d2); + e.Should().BeEquivalentTo(e2); + f.Should().BeEquivalentTo(f2); + g.Should().BeEquivalentTo(g2); + h.Should().BeEquivalentTo(h2); + } + { + var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var b2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var c2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var d2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync(); + + a.Should().BeEquivalentTo(a2); + b.Should().BeEquivalentTo(b2); + c.Should().BeEquivalentTo(c2); + d.Should().BeEquivalentTo(d2); + e.Should().BeEquivalentTo(e2); + f.Should().BeEquivalentTo(f2); + g.Should().BeEquivalentTo(g2); + h.Should().BeEquivalentTo(h2); + } + { + var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var b2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var c2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var d2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync(); + + a.Should().BeEquivalentTo(a2); + b.Should().BeEquivalentTo(b2); + c.Should().BeEquivalentTo(c2); + d.Should().BeEquivalentTo(d2); + e.Should().BeEquivalentTo(e2); + f.Should().BeEquivalentTo(f2); + g.Should().BeEquivalentTo(g2); + h.Should().BeEquivalentTo(h2); + } + } + } + + + [Fact] + public async Task Throws() + { + foreach (var item in UniTaskTestException.Throws()) + { + { + var a = item.OrderBy(x => x).ToArrayAsync(); + var b = item.OrderByDescending(x => x).ToArrayAsync(); + var c = item.OrderBy(x => x).ThenBy(x => x).ToArrayAsync(); + var d = item.OrderBy(x => x).ThenByDescending(x => x).ToArrayAsync(); + + await Assert.ThrowsAsync(async () => await a); + await Assert.ThrowsAsync(async () => await b); + await Assert.ThrowsAsync(async () => await c); + await Assert.ThrowsAsync(async () => await d); + } + { + var a = item.OrderByAwait(RandomRun).ToArrayAsync(); + var b = item.OrderByDescendingAwait(RandomRun).ToArrayAsync(); + var c = item.OrderByAwait(RandomRun).ThenByAwait(RandomRun).ToArrayAsync(); + var d = item.OrderByAwait(RandomRun).ThenByDescendingAwait(RandomRun).ToArrayAsync(); + + await Assert.ThrowsAsync(async () => await a); + await Assert.ThrowsAsync(async () => await b); + await Assert.ThrowsAsync(async () => await c); + await Assert.ThrowsAsync(async () => await d); + } + { + var a = item.OrderByAwaitWithCancellation(RandomRun).ToArrayAsync(); + var b = item.OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync(); + var c = item.OrderByAwaitWithCancellation(RandomRun).ThenByAwaitWithCancellation(RandomRun).ToArrayAsync(); + var d = item.OrderByAwaitWithCancellation(RandomRun).ThenByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync(); + + await Assert.ThrowsAsync(async () => await a); + await Assert.ThrowsAsync(async () => await b); + await Assert.ThrowsAsync(async () => await c); + await Assert.ThrowsAsync(async () => await d); + } + } + + } + } +}