In C#, using Linq, if I want to check if a collection has any elements, I can do this:
someCollection.Any()
which is preferable over
someCollection.Count() > 0
since the latter will count all the items in the collection, when I don't really care how many items there are, I just want to know if there are any.
Is there an equivalent for checking if a collection has more than one item? Something like:
someCollection.Many()
instead of having to do
someCollection.Count() > 1
Silly question, I know, and from my research, it doesn't look like there is. But since it's something I use frequently, I thought I'd double check with the community.
Thanks
CodePudding user response:
As per my comment:
First, you should check to see if IEnumerable<T> source
actually is an IReadOnlyCollection<T>
or ICollection<T>
because that has a .Count
property you can use - which would be preferable to any iteration.
Assuming your IEnumerable<T>
does not have an O(1)
.Count
property, if you want to see if there's at least 1 element (i.e. "at least 2 or more") then use source.Take(2).Count() == 2
or source.Skip(1).Any()
.
Like so:
public static Boolean Many<T>( this IEnumerable<T> source )
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( source is ICollection<T> col ) return col.Count >= 2;
else if( source is IReadOnlyCollection<T> roCol ) return roCol.Count >= 2;
return source.Take(2).Count() == 2;
}
If you want to be more efficient about it, do manual iteration:
public static Boolean Many<T>( this IEnumerable<T> source )
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( source is ICollection<T> col ) return col.Count >= 2;
else if( source is IReadOnlyCollection<T> roCol ) return roCol.Count >= 2;
Int32 count = 0;
using( IEnumerator<T> iter = source.GetEnumerator() )
{
while( iter.MoveNext() && count < 2 )
{
count = 1;
}
}
return count == 2;
}
If you want to be even more efficient about it, allow consumers to supply non-boxed enumerators (e.g. List<T>.Enumerator
):
public static Boolean Many<TEnumerable,TEnumerator,TElement>( this IEnumerable<T> source, Func<TEnumerable,TEnumerator> getEnumerator )
where TEnumerable : IEnumerable<T>
where TEnumerator : IEnumerator<T>
{
if( source is null ) throw new ArgumentNullException(nameof(source));
if( getEnumerator is null ) throw new ArgumentNullException(nameof(getEnumerator));
if( source is ICollection<T> col ) return col.Count >= 2;
else if( source is IReadOnlyCollection<T> roCol ) return roCol.Count >= 2;
Int32 count = 0;
using( TEnumerator iter = getEnumerator( source ) )
{
while( iter.MoveNext() && count < 2 )
{
count = 1;
}
}
return count == 2;
}
Used like so:
List<String> listOfStrings = new List<String>() { ... };
if( listOfStrings.Many</*TEnumerable:*/ List<String>, /*TEnumerator:*/ List<String>.Enumerator, /*TElement:*/ String >( l => l.GetEnumerator() ) )
{
}
- Yes, it's ugly... it's unfortunate, but C# still doesn't support this level of generic type-inference - nor does it support partial generic parameter application.
- The
list.GetEnumerator()
part is needed as it's the only way to pass astruct
-based enumerator into generic code without boxing (at least, not without reflection).