DistinctSelector
parent
85dc70a3ab
commit
57c414a6e0
|
@ -212,7 +212,7 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
protected abstract bool TrySetCurrentCore(TAwait awaitResult, out bool terminateIteration);
|
protected abstract bool TrySetCurrentCore(TAwait awaitResult, out bool terminateIteration);
|
||||||
|
|
||||||
// Util
|
// Util
|
||||||
protected TSource SourceCurrent => enumerator.Current;
|
protected TSource SourceCurrent { get; private set; }
|
||||||
|
|
||||||
protected (bool waitCallback, bool requireNextIteration) ActionCompleted(bool trySetCurrentResult, out bool moveNextResult)
|
protected (bool waitCallback, bool requireNextIteration) ActionCompleted(bool trySetCurrentResult, out bool moveNextResult)
|
||||||
{
|
{
|
||||||
|
@ -287,7 +287,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
if (sourceHasCurrent)
|
if (sourceHasCurrent)
|
||||||
{
|
{
|
||||||
var task = TransformAsync(enumerator.Current);
|
SourceCurrent = enumerator.Current;
|
||||||
|
var task = TransformAsync(SourceCurrent);
|
||||||
if (UnwarapTask(task, out var taskResult))
|
if (UnwarapTask(task, out var taskResult))
|
||||||
{
|
{
|
||||||
var currentResult = TrySetCurrentCore(taskResult, out var terminateIteration);
|
var currentResult = TrySetCurrentCore(taskResult, out var terminateIteration);
|
||||||
|
|
|
@ -9,8 +9,6 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
{
|
{
|
||||||
public static IUniTaskAsyncEnumerable<TSource> Distinct<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
|
public static IUniTaskAsyncEnumerable<TSource> Distinct<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
|
||||||
{
|
{
|
||||||
Error.ThrowArgumentNullException(source, nameof(source));
|
|
||||||
|
|
||||||
return Distinct(source, EqualityComparer<TSource>.Default);
|
return Distinct(source, EqualityComparer<TSource>.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +19,48 @@ namespace Cysharp.Threading.Tasks.Linq
|
||||||
|
|
||||||
return new Distinct<TSource>(source, comparer);
|
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>
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,20 +32,37 @@ namespace NetCoreTests.Linq
|
||||||
[MemberData(nameof(array1))]
|
[MemberData(nameof(array1))]
|
||||||
public async Task Distinct(int[] array)
|
public async Task Distinct(int[] array)
|
||||||
{
|
{
|
||||||
var xs = await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync();
|
|
||||||
var ys = array.Distinct().ToArray();
|
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]
|
[Fact]
|
||||||
public async Task DistinctThrow()
|
public async Task DistinctThrow()
|
||||||
{
|
{
|
||||||
foreach (var item in UniTaskTestException.Throws())
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
{
|
{
|
||||||
var xs = item.Distinct().ToArrayAsync();
|
var xs = item.Distinct().ToArrayAsync();
|
||||||
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
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]
|
[Fact]
|
||||||
|
|
Loading…
Reference in New Issue