// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace System.Linq
{
public static partial class AsyncEnumerableEx
{
///
/// Returns an async-enumerable sequence that contains only distinct elements according to the keySelector.
///
/// The type of the elements in the source sequence.
/// The type of the discriminator key computed for each element in the source sequence.
/// An async-enumerable sequence to retain distinct elements for.
/// A function to compute the comparison key for each element.
/// An async-enumerable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
/// or is null.
/// Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
[Obsolete("Use DistinctBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the functionality of selector-based overloads of Distinct now exists as DistinctBy.")]
public static IAsyncEnumerable Distinct(this IAsyncEnumerable source, Func keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctCore(source, keySelector, comparer: null);
}
///
/// Returns an async-enumerable sequence that contains only distinct elements according to the keySelector and the comparer.
///
/// The type of the elements in the source sequence.
/// The type of the discriminator key computed for each element in the source sequence.
/// An async-enumerable sequence to retain distinct elements for.
/// A function to compute the comparison key for each element.
/// Equality comparer for source elements.
/// An async-enumerable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
/// or or is null.
/// Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
[Obsolete("Use DistinctBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the functionality of selector-based overloads of Distinct now exists as DistinctBy.")]
public static IAsyncEnumerable Distinct(this IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctCore(source, keySelector, comparer);
}
///
/// Returns an async-enumerable sequence that contains only distinct elements according to the asynchronous keySelector.
///
/// The type of the elements in the source sequence.
/// The type of the discriminator key computed for each element in the source sequence.
/// An async-enumerable sequence to retain distinct elements for.
/// An asynchronous function to compute the comparison key for each element.
/// An async-enumerable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
/// or is null.
/// Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
[Obsolete("Use DistinctBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the functionality of selector-based overloads of Distinct now exists as DistinctBy.")]
public static IAsyncEnumerable Distinct(this IAsyncEnumerable source, Func> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctCore(source, keySelector, comparer: null);
}
#if !NO_DEEP_CANCELLATION
///
/// Returns an async-enumerable sequence that contains only distinct elements according to the asynchronous (cancellable) keySelector.
///
/// The type of the elements in the source sequence.
/// The type of the discriminator key computed for each element in the source sequence.
/// An async-enumerable sequence to retain distinct elements for.
/// An asynchronous (cancellable) function to compute the comparison key for each element.
/// An async-enumerable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
/// or is null.
/// Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
[Obsolete("Use DistinctBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the functionality of selector-based overloads of Distinct now exists as DistinctBy.")]
public static IAsyncEnumerable Distinct(this IAsyncEnumerable source, Func> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctCore(source, keySelector, comparer: null);
}
#endif
///
/// Returns an async-enumerable sequence that contains only distinct elements according to the asynchronous keySelector and the comparer.
///
/// The type of the elements in the source sequence.
/// The type of the discriminator key computed for each element in the source sequence.
/// An async-enumerable sequence to retain distinct elements for.
/// An asynchronous function to compute the comparison key for each element.
/// Equality comparer for source elements.
/// An async-enumerable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
/// or or is null.
/// Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
[Obsolete("Use DistinctBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the functionality of selector-based overloads of Distinct now exists as DistinctBy.")]
public static IAsyncEnumerable Distinct(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctCore(source, keySelector, comparer);
}
#if !NO_DEEP_CANCELLATION
///
/// Returns an async-enumerable sequence that contains only distinct elements according to the asynchronous (cancellable) keySelector and the comparer.
///
/// The type of the elements in the source sequence.
/// The type of the discriminator key computed for each element in the source sequence.
/// An async-enumerable sequence to retain distinct elements for.
/// An asynchronous (cancellable) function to compute the comparison key for each element.
/// Equality comparer for source elements.
/// An async-enumerable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
/// or or is null.
/// Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
[Obsolete("Use DistinctBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the functionality of selector-based overloads of Distinct now exists as DistinctBy.")]
public static IAsyncEnumerable Distinct(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctCore(source, keySelector, comparer);
}
#endif
private static IAsyncEnumerable DistinctCore(IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer)
{
return new DistinctAsyncIterator(source, keySelector, comparer);
}
private static IAsyncEnumerable DistinctCore(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
return new DistinctAsyncIteratorWithTask(source, keySelector, comparer);
}
#if !NO_DEEP_CANCELLATION
private static IAsyncEnumerable DistinctCore(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
return new DistinctAsyncIteratorWithTaskAndCancellation(source, keySelector, comparer);
}
#endif
private sealed class DistinctAsyncIterator : AsyncIterator, IAsyncIListProvider
{
private readonly IEqualityComparer? _comparer;
private readonly Func _keySelector;
private readonly IAsyncEnumerable _source;
private IAsyncEnumerator? _enumerator;
private Set? _set;
public DistinctAsyncIterator(IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer)
{
_source = source;
_keySelector = keySelector;
_comparer = comparer;
}
public async ValueTask ToArrayAsync(CancellationToken cancellationToken)
{
var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
return s.ToArray();
}
public async ValueTask> ToListAsync(CancellationToken cancellationToken)
{
var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
return s;
}
public async ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
{
if (onlyIfCheap)
{
return -1;
}
var count = 0;
var s = new Set(_comparer);
var enu = _source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enu.MoveNextAsync().ConfigureAwait(false))
{
var item = enu.Current;
if (s.Add(_keySelector(item)))
{
count++;
}
}
}
finally
{
await enu.DisposeAsync().ConfigureAwait(false);
}
return count;
}
public override AsyncIteratorBase Clone()
{
return new DistinctAsyncIterator(_source, _keySelector, _comparer);
}
public override async ValueTask DisposeAsync()
{
if (_enumerator != null)
{
await _enumerator.DisposeAsync().ConfigureAwait(false);
_enumerator = null;
_set = null;
}
await base.DisposeAsync().ConfigureAwait(false);
}
protected override async ValueTask MoveNextCore()
{
switch (_state)
{
case AsyncIteratorState.Allocated:
_enumerator = _source.GetAsyncEnumerator(_cancellationToken);
if (!await _enumerator.MoveNextAsync().ConfigureAwait(false))
{
await DisposeAsync().ConfigureAwait(false);
return false;
}
var element = _enumerator.Current;
_set = new Set(_comparer);
_set.Add(_keySelector(element));
_current = element;
_state = AsyncIteratorState.Iterating;
return true;
case AsyncIteratorState.Iterating:
while (await _enumerator!.MoveNextAsync().ConfigureAwait(false))
{
element = _enumerator.Current;
if (_set!.Add(_keySelector(element)))
{
_current = element;
return true;
}
}
break;
}
await DisposeAsync().ConfigureAwait(false);
return false;
}
private async Task> FillSetAsync(CancellationToken cancellationToken)
{
var s = new Set(_comparer);
var r = new List();
var enu = _source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enu.MoveNextAsync().ConfigureAwait(false))
{
var item = enu.Current;
if (s.Add(_keySelector(item)))
{
r.Add(item);
}
}
}
finally
{
await enu.DisposeAsync().ConfigureAwait(false);
}
return r;
}
}
private sealed class DistinctAsyncIteratorWithTask : AsyncIterator, IAsyncIListProvider
{
private readonly IEqualityComparer? _comparer;
private readonly Func> _keySelector;
private readonly IAsyncEnumerable _source;
private IAsyncEnumerator? _enumerator;
private Set? _set;
public DistinctAsyncIteratorWithTask(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
_source = source;
_keySelector = keySelector;
_comparer = comparer;
}
public async ValueTask ToArrayAsync(CancellationToken cancellationToken)
{
var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
return s.ToArray();
}
public async ValueTask> ToListAsync(CancellationToken cancellationToken)
{
var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
return s;
}
public async ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
{
if (onlyIfCheap)
{
return -1;
}
var count = 0;
var s = new Set(_comparer);
var enu = _source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enu.MoveNextAsync().ConfigureAwait(false))
{
var item = enu.Current;
if (s.Add(await _keySelector(item).ConfigureAwait(false)))
{
count++;
}
}
}
finally
{
await enu.DisposeAsync().ConfigureAwait(false);
}
return count;
}
public override AsyncIteratorBase Clone()
{
return new DistinctAsyncIteratorWithTask(_source, _keySelector, _comparer);
}
public override async ValueTask DisposeAsync()
{
if (_enumerator != null)
{
await _enumerator.DisposeAsync().ConfigureAwait(false);
_enumerator = null;
_set = null;
}
await base.DisposeAsync().ConfigureAwait(false);
}
protected override async ValueTask MoveNextCore()
{
switch (_state)
{
case AsyncIteratorState.Allocated:
_enumerator = _source.GetAsyncEnumerator(_cancellationToken);
if (!await _enumerator.MoveNextAsync().ConfigureAwait(false))
{
await DisposeAsync().ConfigureAwait(false);
return false;
}
var element = _enumerator.Current;
_set = new Set(_comparer);
_set.Add(await _keySelector(element).ConfigureAwait(false));
_current = element;
_state = AsyncIteratorState.Iterating;
return true;
case AsyncIteratorState.Iterating:
while (await _enumerator!.MoveNextAsync().ConfigureAwait(false))
{
element = _enumerator.Current;
if (_set!.Add(await _keySelector(element).ConfigureAwait(false)))
{
_current = element;
return true;
}
}
break;
}
await DisposeAsync().ConfigureAwait(false);
return false;
}
private async ValueTask> FillSetAsync(CancellationToken cancellationToken)
{
var s = new Set(_comparer);
var r = new List();
var enu = _source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enu.MoveNextAsync().ConfigureAwait(false))
{
var item = enu.Current;
if (s.Add(await _keySelector(item).ConfigureAwait(false)))
{
r.Add(item);
}
}
}
finally
{
await enu.DisposeAsync().ConfigureAwait(false);
}
return r;
}
}
#if !NO_DEEP_CANCELLATION
private sealed class DistinctAsyncIteratorWithTaskAndCancellation : AsyncIterator, IAsyncIListProvider
{
private readonly IEqualityComparer? _comparer;
private readonly Func> _keySelector;
private readonly IAsyncEnumerable _source;
private IAsyncEnumerator? _enumerator;
private Set? _set;
public DistinctAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
_source = source;
_keySelector = keySelector;
_comparer = comparer;
}
public async ValueTask ToArrayAsync(CancellationToken cancellationToken)
{
var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
return s.ToArray();
}
public async ValueTask> ToListAsync(CancellationToken cancellationToken)
{
var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
return s;
}
public async ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
{
if (onlyIfCheap)
{
return -1;
}
var count = 0;
var s = new Set(_comparer);
var enu = _source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enu.MoveNextAsync().ConfigureAwait(false))
{
var item = enu.Current;
if (s.Add(await _keySelector(item, cancellationToken).ConfigureAwait(false)))
{
count++;
}
}
}
finally
{
await enu.DisposeAsync().ConfigureAwait(false);
}
return count;
}
public override AsyncIteratorBase Clone()
{
return new DistinctAsyncIteratorWithTaskAndCancellation(_source, _keySelector, _comparer);
}
public override async ValueTask DisposeAsync()
{
if (_enumerator != null)
{
await _enumerator.DisposeAsync().ConfigureAwait(false);
_enumerator = null;
_set = null;
}
await base.DisposeAsync().ConfigureAwait(false);
}
protected override async ValueTask MoveNextCore()
{
switch (_state)
{
case AsyncIteratorState.Allocated:
_enumerator = _source.GetAsyncEnumerator(_cancellationToken);
if (!await _enumerator.MoveNextAsync().ConfigureAwait(false))
{
await DisposeAsync().ConfigureAwait(false);
return false;
}
var element = _enumerator.Current;
_set = new Set(_comparer);
_set.Add(await _keySelector(element, _cancellationToken).ConfigureAwait(false));
_current = element;
_state = AsyncIteratorState.Iterating;
return true;
case AsyncIteratorState.Iterating:
while (await _enumerator!.MoveNextAsync().ConfigureAwait(false))
{
element = _enumerator.Current;
if (_set!.Add(await _keySelector(element, _cancellationToken).ConfigureAwait(false)))
{
_current = element;
return true;
}
}
break;
}
await DisposeAsync().ConfigureAwait(false);
return false;
}
private async ValueTask> FillSetAsync(CancellationToken cancellationToken)
{
var s = new Set(_comparer);
var r = new List();
var enu = _source.GetAsyncEnumerator(cancellationToken);
try
{
while (await enu.MoveNextAsync().ConfigureAwait(false))
{
var item = enu.Current;
if (s.Add(await _keySelector(item, cancellationToken).ConfigureAwait(false)))
{
r.Add(item);
}
}
}
finally
{
await enu.DisposeAsync().ConfigureAwait(false);
}
return r;
}
}
#endif
}
}