Skip to content

Commit bae196d

Browse files
committed
Add enumerable method SelectWithIsLast and WithObservable
1 parent 651c1a8 commit bae196d

6 files changed

Lines changed: 574 additions & 0 deletions

File tree

src/DomainCommonExtensions/ArraysExtensions/EnumerableExtensions.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,5 +856,78 @@ public static bool HasAny<TSource>(this IEnumerable<TSource> source, Func<TSourc
856856

857857
return false;
858858
}
859+
860+
/// -------------------------------------------------------------------------------------------------
861+
/// <summary>
862+
/// Is last.
863+
/// </summary>
864+
/// <typeparam name="T">Generic type parameter.</typeparam>
865+
/// <param name="source">The source enumerator.</param>
866+
/// <remarks>
867+
/// Using this method, the enumerator will advance.
868+
/// </remarks>
869+
/// <returns>
870+
/// True if last, false if not.
871+
/// </returns>
872+
/// =================================================================================================
873+
public static bool IsLast<T>(this IEnumerator<T> source)
874+
{
875+
if (source.IsNull())
876+
return true;
877+
878+
return source.MoveNext().IsFalse();
879+
}
880+
881+
/// -------------------------------------------------------------------------------------------------
882+
/// <summary>
883+
/// Is last.
884+
/// </summary>
885+
/// <typeparam name="TKey">Type of the key.</typeparam>
886+
/// <typeparam name="TValue">Type of the value.</typeparam>
887+
/// <param name="source">The source enumerator.</param>
888+
/// <remarks>
889+
/// Using this method, the enumerator will advance.
890+
/// </remarks>
891+
/// <returns>
892+
/// True if last, false if not.
893+
/// </returns>
894+
/// =================================================================================================
895+
public static bool IsLast<TKey, TValue>(
896+
this IEnumerator<KeyValuePair<TKey, TValue>> source)
897+
{
898+
if (source.IsNull())
899+
return true;
900+
901+
return source.MoveNext().IsFalse();
902+
}
903+
904+
/// -------------------------------------------------------------------------------------------------
905+
/// <summary>
906+
/// Enumerates select with is last in this collection.
907+
/// </summary>
908+
/// <typeparam name="T">Generic type parameter.</typeparam>
909+
/// <param name="source">The source for this extension method.</param>
910+
/// <returns>
911+
/// An enumerator that allows foreach to be used to process select with is last in this
912+
/// collection.
913+
/// </returns>
914+
/// =================================================================================================
915+
public static IEnumerable<TupleResult<T, bool>> SelectWithIsLast<T>(
916+
this IEnumerable<T> source)
917+
{
918+
using var e = source.GetEnumerator();
919+
if (e.MoveNext().IsFalse())
920+
yield break;
921+
922+
var current = e.Current;
923+
924+
while (e.MoveNext())
925+
{
926+
yield return TupleResult.Create(current, false);
927+
current = e.Current;
928+
}
929+
930+
yield return TupleResult.Create(current, true);
931+
}
859932
}
860933
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// ***********************************************************************
2+
// Assembly : RzR.Shared.Extensions.DomainCommonExtensions
3+
// Author : RzR
4+
// Created On : 2026-01-28 22:01
5+
//
6+
// Last Modified By : RzR
7+
// Last Modified On : 2026-02-11 20:07
8+
// ***********************************************************************
9+
// <copyright file="ObservableEnumeratorExtensions.cs" company="RzR SOFT & TECH">
10+
// Copyright © RzR. All rights reserved.
11+
// </copyright>
12+
//
13+
// <summary>
14+
// </summary>
15+
// ***********************************************************************
16+
17+
#region U S A G E S
18+
19+
using System;
20+
using System.Collections.Generic;
21+
using DomainCommonExtensions.Collections;
22+
using DomainCommonExtensions.Utilities.Ensure;
23+
24+
#endregion
25+
26+
namespace DomainCommonExtensions.ArraysExtensions
27+
{
28+
/// -------------------------------------------------------------------------------------------------
29+
/// <summary>
30+
/// An observable enumerator extensions.
31+
/// </summary>
32+
/// =================================================================================================
33+
public static class ObservableEnumeratorExtensions
34+
{
35+
/// -------------------------------------------------------------------------------------------------
36+
/// <summary>
37+
/// An IEnumerable&lt;T&gt; extension method that convert to observable.
38+
/// </summary>
39+
/// <exception cref="ArgumentNullException">
40+
/// Thrown when one or more required arguments are null.
41+
/// </exception>
42+
/// <typeparam name="T">Generic type parameter.</typeparam>
43+
/// <param name="source">The source to act on.</param>
44+
/// <returns>
45+
/// An ObservableEnumerator&lt;T&gt;
46+
/// </returns>
47+
/// =================================================================================================
48+
public static ObservableEnumerator<T> WithObservable<T>(
49+
this IEnumerable<T> source)
50+
{
51+
DomainEnsure.IsNotNull(source, nameof(source));
52+
53+
return new ObservableEnumerator<T>(source);
54+
}
55+
56+
/// -------------------------------------------------------------------------------------------------
57+
/// <summary>
58+
/// An IEnumerable&lt;T&gt; extension method that convert to observable.
59+
/// </summary>
60+
/// <exception cref="ArgumentNullException">
61+
/// Thrown when one or more required arguments are null.
62+
/// </exception>
63+
/// <typeparam name="T">Generic type parameter.</typeparam>
64+
/// <param name="enumerator">The enumerator to act on.</param>
65+
/// <returns>
66+
/// An ObservableEnumerator&lt;T&gt;
67+
/// </returns>
68+
/// =================================================================================================
69+
public static ObservableEnumerator<T> WithObservable<T>(
70+
this IEnumerator<T> enumerator)
71+
{
72+
DomainEnsure.IsNotNull(enumerator, nameof(enumerator));
73+
74+
return new ObservableEnumerator<T>(enumerator);
75+
}
76+
}
77+
}

src/DomainCommonExtensions/Collections/IndexableEnumerable.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ public IndexableEnumerable(List<T> list)
113113
: this((IList<T>)list)
114114
{ }
115115

116+
/// -------------------------------------------------------------------------------------------------
117+
/// <summary>
118+
/// Initializes a new instance of the <see cref="IndexableEnumerable{T}"/> class.
119+
/// </summary>
120+
/// <param name="list">(Immutable) the list.</param>
121+
/// =================================================================================================
122+
public IndexableEnumerable(IEnumerable<T> list)
123+
: this((IList<T>)list)
124+
{ }
125+
116126
/// -------------------------------------------------------------------------------------------------
117127
/// <summary>
118128
/// Initializes the index enumerable.
@@ -145,6 +155,17 @@ public IndexableEnumerable(List<T> list)
145155
/// </returns>
146156
/// =================================================================================================
147157
public static IIndexableEnumerable<T> Initialize(List<T> list) => new IndexableEnumerable<T>(list);
158+
159+
/// -------------------------------------------------------------------------------------------------
160+
/// <summary>
161+
/// Initializes the index enumerable.
162+
/// </summary>
163+
/// <param name="list">(Immutable) the list.</param>
164+
/// <returns>
165+
/// An IIndexableEnumerable&lt;T&gt;
166+
/// </returns>
167+
/// =================================================================================================
168+
public static IIndexableEnumerable<T> Initialize(IEnumerable<T> list) => new IndexableEnumerable<T>(list);
148169

149170
/// <inheritdoc />
150171
public T this[int index] => _list[index];
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// ***********************************************************************
2+
// Assembly : RzR.Shared.Extensions.DomainCommonExtensions
3+
// Author : RzR
4+
// Created On : 2026-01-28 22:01
5+
//
6+
// Last Modified By : RzR
7+
// Last Modified On : 2026-01-28 22:52
8+
// ***********************************************************************
9+
// <copyright file="ObservableEnumerator.cs" company="RzR SOFT & TECH">
10+
// Copyright © RzR. All rights reserved.
11+
// </copyright>
12+
//
13+
// <summary>
14+
// </summary>
15+
// ***********************************************************************
16+
17+
#region U S A G E S
18+
19+
using System;
20+
using System.Collections;
21+
using System.Collections.Generic;
22+
using DomainCommonExtensions.CommonExtensions;
23+
24+
// ReSharper disable PossibleMultipleEnumeration
25+
26+
#endregion
27+
28+
namespace DomainCommonExtensions.Collections
29+
{
30+
/// -------------------------------------------------------------------------------------------------
31+
/// <summary>
32+
/// An observable enumerator. This class cannot be inherited.
33+
/// </summary>
34+
/// <typeparam name="T">Generic type parameter.</typeparam>
35+
/// <seealso cref="T:System.Collections.Generic.IEnumerator{T}"/>
36+
/// =================================================================================================
37+
public sealed class ObservableEnumerator<T> : IEnumerator<T>
38+
{
39+
private readonly IEnumerable<T> _source;
40+
41+
private T _current = default;
42+
private T _peek = default;
43+
44+
private bool _hasCurrent, _hasPeek;
45+
46+
private IEnumerator<T> _inner;
47+
48+
/// -------------------------------------------------------------------------------------------------
49+
/// <summary>
50+
/// Initializes a new instance of the <see cref="ObservableEnumerator{T}"/> class.
51+
/// </summary>
52+
/// <param name="source">The source data.</param>
53+
/// =================================================================================================
54+
internal ObservableEnumerator(IEnumerable<T> source)
55+
{
56+
_source = source;
57+
_inner = source.GetEnumerator();
58+
}
59+
60+
/// -------------------------------------------------------------------------------------------------
61+
/// <summary>
62+
/// Initializes a new instance of the <see cref="ObservableEnumerator{T}"/> class.
63+
/// </summary>
64+
/// <param name="inner">The inner.</param>
65+
/// =================================================================================================
66+
internal ObservableEnumerator(IEnumerator<T> inner)
67+
{
68+
_inner = inner;
69+
}
70+
71+
/// -------------------------------------------------------------------------------------------------
72+
/// <summary>
73+
/// Gets a value indicating whether this object is last.
74+
/// </summary>
75+
/// <value>
76+
/// True if this object is last, false if not.
77+
/// </value>
78+
/// =================================================================================================
79+
public bool IsLast
80+
{
81+
get
82+
{
83+
EnsureStarted();
84+
EnsurePeeked();
85+
86+
return !_hasPeek;
87+
}
88+
}
89+
90+
/// <inheritdoc />
91+
public T Current
92+
{
93+
get
94+
{
95+
EnsureStarted();
96+
97+
return _current;
98+
}
99+
}
100+
101+
/// <inheritdoc />
102+
object IEnumerator.Current => Current;
103+
104+
/// <inheritdoc />
105+
public bool MoveNext()
106+
{
107+
if (_hasPeek)
108+
{
109+
_current = _peek;
110+
_hasPeek = false;
111+
_hasCurrent = true;
112+
113+
return true;
114+
}
115+
116+
if (!_inner.MoveNext())
117+
{
118+
_hasCurrent = false;
119+
120+
return false;
121+
}
122+
123+
_current = _inner.Current;
124+
_hasCurrent = true;
125+
126+
return true;
127+
}
128+
129+
/// <inheritdoc />
130+
public void Reset()
131+
{
132+
if (_source.IsNull())
133+
throw new NotSupportedException("Reset is not supported.");
134+
135+
_inner.Dispose();
136+
_inner = _source.GetEnumerator();
137+
138+
_hasCurrent = false;
139+
_hasPeek = false;
140+
}
141+
142+
/// <inheritdoc />
143+
public void Dispose() => _inner.Dispose();
144+
145+
/// -------------------------------------------------------------------------------------------------
146+
/// <summary>
147+
/// Attempts to peek.
148+
/// </summary>
149+
/// <param name="value">[out] The value.</param>
150+
/// <returns>
151+
/// True if it succeeds, false if it fails.
152+
/// </returns>
153+
/// =================================================================================================
154+
public bool TryPeek(out T value)
155+
{
156+
EnsureStarted();
157+
EnsurePeeked();
158+
159+
if (_hasPeek)
160+
{
161+
value = _peek;
162+
163+
return true;
164+
}
165+
166+
value = default;
167+
168+
return false;
169+
}
170+
171+
/// -------------------------------------------------------------------------------------------------
172+
/// <summary>
173+
/// Ensures that peeked.
174+
/// </summary>
175+
/// =================================================================================================
176+
private void EnsurePeeked()
177+
{
178+
if (_hasPeek)
179+
return;
180+
181+
if (_inner.MoveNext())
182+
{
183+
_peek = _inner.Current;
184+
_hasPeek = true;
185+
}
186+
}
187+
188+
/// -------------------------------------------------------------------------------------------------
189+
/// <summary>
190+
/// Ensures that enumeration started.
191+
/// </summary>
192+
/// <exception cref="InvalidOperationException">
193+
/// Thrown when the requested operation is invalid.
194+
/// </exception>
195+
/// =================================================================================================
196+
private void EnsureStarted()
197+
{
198+
if (!_hasCurrent)
199+
throw new InvalidOperationException("Enumeration has not started.");
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)