// 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 contiguous elements.
///
/// The type of the elements in the source sequence.
/// An async-enumerable sequence to retain distinct contiguous elements for.
/// An async-enumerable sequence only containing the distinct contiguous elements from the source sequence.
/// is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
return DistinctUntilChangedCore(source, comparer: null);
}
///
/// Returns an async-enumerable sequence that contains only distinct contiguous elements according to the comparer.
///
/// The type of the elements in the source sequence.
/// An async-enumerable sequence to retain distinct contiguous elements for.
/// Equality comparer for source elements.
/// An async-enumerable sequence only containing the distinct contiguous elements from the source sequence.
/// or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, IEqualityComparer? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
return DistinctUntilChangedCore(source, comparer);
}
///
/// Returns an async-enumerable sequence that contains only distinct contiguous 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 contiguous elements for, based on a computed key value.
/// A function to compute the comparison key for each element.
/// An async-enumerable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
/// or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, Func keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctUntilChangedCore(source, keySelector, comparer: null);
}
///
/// Returns an async-enumerable sequence that contains only distinct contiguous 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 contiguous elements for, based on a computed key value.
/// A function to compute the comparison key for each element.
/// Equality comparer for computed key values.
/// An async-enumerable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
/// or or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctUntilChangedCore(source, keySelector, comparer);
}
///
/// Returns an async-enumerable sequence that contains only distinct contiguous 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 contiguous elements for, based on a computed key value.
/// A function to compute the comparison key for each element asynchronously.
/// An async-enumerable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
/// or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, Func> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctUntilChangedCore(source, keySelector, comparer: null);
}
#if !NO_DEEP_CANCELLATION
///
/// Returns an async-enumerable sequence that contains only distinct contiguous elements according to the asynchronous and 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 contiguous elements for, based on a computed key value.
/// A function to compute the comparison key for each element asynchronously while supporting cancellation.
/// An async-enumerable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
/// or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, Func> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctUntilChangedCore(source, keySelector, comparer: null);
}
#endif
///
/// Returns an async-enumerable sequence that contains only distinct contiguous 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 contiguous elements for, based on a computed key value.
/// A function to compute the comparison key for each element asynchronously.
/// Equality comparer for computed key values.
/// An async-enumerable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
/// or or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctUntilChangedCore(source, keySelector, comparer);
}
#if !NO_DEEP_CANCELLATION
///
/// Returns an async-enumerable sequence that contains only distinct contiguous elements according to the asynchronous and 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 contiguous elements for, based on a computed key value.
/// A function to compute the comparison key for each element asynchronously while supporting cancellation.
/// Equality comparer for computed key values.
/// An async-enumerable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
/// or or is null.
public static IAsyncEnumerable DistinctUntilChanged(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return DistinctUntilChangedCore(source, keySelector, comparer);
}
#endif
private static IAsyncEnumerable DistinctUntilChangedCore(IAsyncEnumerable source, IEqualityComparer? comparer)
{
comparer ??= EqualityComparer.Default;
return Core(source, comparer);
static async IAsyncEnumerable Core(IAsyncEnumerable source, IEqualityComparer comparer, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken, false);
if (!await e.MoveNextAsync())
{
yield break;
}
var latest = e.Current;
yield return latest;
while (await e.MoveNextAsync())
{
var item = e.Current;
// REVIEW: Need comparer!.Equals to satisfy nullable reference type warnings.
if (!comparer!.Equals(latest, item))
{
latest = item;
yield return latest;
}
}
}
}
private static IAsyncEnumerable DistinctUntilChangedCore(IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer)
{
comparer ??= EqualityComparer.Default;
return Core(source, keySelector, comparer);
static async IAsyncEnumerable Core(IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken, false);
if (!await e.MoveNextAsync())
{
yield break;
}
var item = e.Current;
var latestKey = keySelector(item);
yield return item;
while (await e.MoveNextAsync())
{
item = e.Current;
var currentKey = keySelector(item);
// REVIEW: Need comparer!.Equals to satisfy nullable reference type warnings.
if (!comparer!.Equals(latestKey, currentKey))
{
latestKey = currentKey;
yield return item;
}
}
}
}
private static IAsyncEnumerable DistinctUntilChangedCore(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
comparer ??= EqualityComparer.Default;
return Core(source, keySelector, comparer);
static async IAsyncEnumerable Core(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken, false);
if (!await e.MoveNextAsync())
{
yield break;
}
var item = e.Current;
var latestKey = await keySelector(item).ConfigureAwait(false);
yield return item;
while (await e.MoveNextAsync())
{
item = e.Current;
var currentKey = await keySelector(item).ConfigureAwait(false);
// REVIEW: Need comparer!.Equals to satisfy nullable reference type warnings.
if (!comparer!.Equals(latestKey, currentKey))
{
latestKey = currentKey;
yield return item;
}
}
}
}
#if !NO_DEEP_CANCELLATION
private static IAsyncEnumerable DistinctUntilChangedCore(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer)
{
comparer ??= EqualityComparer.Default;
return Core(source, keySelector, comparer);
static async IAsyncEnumerable Core(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await using var e = source.GetConfiguredAsyncEnumerator(cancellationToken, false);
if (!await e.MoveNextAsync())
{
yield break;
}
var item = e.Current;
var latestKey = await keySelector(item, cancellationToken).ConfigureAwait(false);
yield return item;
while (await e.MoveNextAsync())
{
item = e.Current;
var currentKey = await keySelector(item, cancellationToken).ConfigureAwait(false);
// REVIEW: Need comparer!.Equals to satisfy nullable reference type warnings.
if (!comparer!.Equals(latestKey, currentKey))
{
latestKey = currentKey;
yield return item;
}
}
}
}
#endif
}
}