DistinctSelector

master
neuecc 2020-05-12 02:45:12 +09:00
parent 85dc70a3ab
commit 57c414a6e0
4 changed files with 228 additions and 60 deletions

View File

@ -212,7 +212,7 @@ namespace Cysharp.Threading.Tasks.Linq
protected abstract bool TrySetCurrentCore(TAwait awaitResult, out bool terminateIteration);
// Util
protected TSource SourceCurrent => enumerator.Current;
protected TSource SourceCurrent { get; private set; }
protected (bool waitCallback, bool requireNextIteration) ActionCompleted(bool trySetCurrentResult, out bool moveNextResult)
{
@ -287,7 +287,8 @@ namespace Cysharp.Threading.Tasks.Linq
{
if (sourceHasCurrent)
{
var task = TransformAsync(enumerator.Current);
SourceCurrent = enumerator.Current;
var task = TransformAsync(SourceCurrent);
if (UnwarapTask(task, out var taskResult))
{
var currentResult = TrySetCurrentCore(taskResult, out var terminateIteration);

View File

@ -9,8 +9,6 @@ namespace Cysharp.Threading.Tasks.Linq
{
public static IUniTaskAsyncEnumerable<TSource> Distinct<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
{
Error.ThrowArgumentNullException(source, nameof(source));
return Distinct(source, EqualityComparer<TSource>.Default);
}
@ -21,6 +19,48 @@ namespace Cysharp.Threading.Tasks.Linq
return new Distinct<TSource>(source, comparer);
}
public static IUniTaskAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return Distinct(source, keySelector, EqualityComparer<TKey>.Default);
}
public static IUniTaskAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
Error.ThrowArgumentNullException(comparer, nameof(comparer));
return new Distinct<TSource, TKey>(source, keySelector, comparer);
}
public static IUniTaskAsyncEnumerable<TSource> DistinctAwait<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector)
{
return DistinctAwait(source, keySelector, EqualityComparer<TKey>.Default);
}
public static IUniTaskAsyncEnumerable<TSource> DistinctAwait<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
Error.ThrowArgumentNullException(comparer, nameof(comparer));
return new DistinctAwait<TSource, TKey>(source, keySelector, comparer);
}
public static IUniTaskAsyncEnumerable<TSource> DistinctAwaitWithCancellation<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector)
{
return DistinctAwaitWithCancellation(source, keySelector, EqualityComparer<TKey>.Default);
}
public static IUniTaskAsyncEnumerable<TSource> DistinctAwaitWithCancellation<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(keySelector, nameof(keySelector));
Error.ThrowArgumentNullException(comparer, nameof(comparer));
return new DistinctAwaitCancellation<TSource, TKey>(source, keySelector, comparer);
}
}
internal sealed class Distinct<TSource> : IUniTaskAsyncEnumerable<TSource>
@ -73,4 +113,165 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
}
internal sealed class Distinct<TSource, TKey> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, TKey> keySelector;
readonly IEqualityComparer<TKey> comparer;
public Distinct(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
this.source = source;
this.keySelector = keySelector;
this.comparer = comparer;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, keySelector, comparer, cancellationToken);
}
class Enumerator : AsyncEnumeratorBase<TSource, TSource>
{
readonly HashSet<TKey> set;
readonly Func<TSource, TKey> keySelector;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.set = new HashSet<TKey>(comparer);
this.keySelector = keySelector;
}
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
{
if (sourceHasCurrent)
{
var v = SourceCurrent;
if (set.Add(keySelector(v)))
{
Current = v;
result = true;
return true;
}
else
{
result = default;
return false;
}
}
result = false;
return true;
}
}
}
internal sealed class DistinctAwait<TSource, TKey> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, UniTask<TKey>> keySelector;
readonly IEqualityComparer<TKey> comparer;
public DistinctAwait(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
{
this.source = source;
this.keySelector = keySelector;
this.comparer = comparer;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, keySelector, comparer, cancellationToken);
}
class Enumerator : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, TKey>
{
readonly HashSet<TKey> set;
readonly Func<TSource, UniTask<TKey>> keySelector;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.set = new HashSet<TKey>(comparer);
this.keySelector = keySelector;
}
protected override UniTask<TKey> TransformAsync(TSource sourceCurrent)
{
return keySelector(sourceCurrent);
}
protected override bool TrySetCurrentCore(TKey awaitResult, out bool terminateIteration)
{
if (set.Add(awaitResult))
{
Current = SourceCurrent;
terminateIteration = false;
return true;
}
else
{
terminateIteration = false;
return false;
}
}
}
}
internal sealed class DistinctAwaitCancellation<TSource, TKey> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
readonly IEqualityComparer<TKey> comparer;
public DistinctAwaitCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
{
this.source = source;
this.keySelector = keySelector;
this.comparer = comparer;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, keySelector, comparer, cancellationToken);
}
class Enumerator : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, TKey>
{
readonly HashSet<TKey> set;
readonly Func<TSource, CancellationToken, UniTask<TKey>> keySelector;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<TKey>> keySelector, IEqualityComparer<TKey> comparer, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.set = new HashSet<TKey>(comparer);
this.keySelector = keySelector;
}
protected override UniTask<TKey> TransformAsync(TSource sourceCurrent)
{
return keySelector(sourceCurrent, cancellationToken);
}
protected override bool TrySetCurrentCore(TKey awaitResult, out bool terminateIteration)
{
if (set.Add(awaitResult))
{
Current = SourceCurrent;
terminateIteration = false;
return true;
}
else
{
terminateIteration = false;
return false;
}
}
}
}
}

View File

@ -1,51 +0,0 @@
using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ___Dummy
{
public interface IAsyncGrouping<TKey, TValue>
{
}
public interface IOrderedAsyncEnumerable<T>
{
}
public static partial class _FileMaker
{
// Buffer,Distinct, DistinctUntilChanged, Do, MaxBy, MinBy, Never,Return, Throw
}
}

View File

@ -32,20 +32,37 @@ namespace NetCoreTests.Linq
[MemberData(nameof(array1))]
public async Task Distinct(int[] array)
{
var xs = await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync();
var ys = array.Distinct().ToArray();
xs.Should().BeEquivalentTo(ys);
{
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().BeEquivalentTo(ys);
}
}
[Fact]
public async Task DistinctThrow()
{
foreach (var item in UniTaskTestException.Throws())
{
{
var xs = item.Distinct().ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
{
var xs = item.Distinct(x => x).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
{
var xs = item.DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
{
var xs = item.DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
}
}
[Fact]