ExtraLINQ is a set of extension methods for IEnumerable<T>
.
Why do we actually need some extra IEnumerable<T>
extensions? Imagine, you have a collection of Products and you need to get the most expensive product (the product with the highest price). Unfortunately for me, what I usually see in the code:
var mostExpensiveProduct = products.OrderByDescending(p => p.Price).First();
Complexity of this code is O(n log n)
.
Better approach would be:
var maxPrice = products.Max(p => p.Price);
var mostExpensiveProduct = products.First(p => p.Price == maxPrice);
which is O(n)
but in the worst case might iterate twice.
Obviously this operation can be done in true O(n)
. For this operation ExtraLINQ has MaxBy
method:
var mostExpensiveProduct = products.MaxBy(p => p.Price);
Or imagine another case: you need to check whether sequence length is less than or equal to 5
. What you normally see is this:
bool checkCount = products.Count() <= 5;
This code is O(1)
if products
is actually some sort of collection which implements IReadOnlyCollection<T>
or ICollection<T>
. But if it does not, the code above will iterate through all items, becoming O(n)
.
If products
doesn't implement these interfaces, better approach is iterate through first 6
items and check Count()
, i.e.:
bool checkCount = products.Take(6).Count() <= 5;
To omit these drawbacks ExtraLINQ has AtMost
method:
bool checkCount = products.AtMost(5);
Additionally ExtraLINQ provides overloads of some methods (e.g. Sum
) for the most commonly used collections: T[]
and List<T>
. These methods work faster and allocate less than LINQ built-in methods. For benchmarks see Benchmark project.
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
SumArrayLinq | 8,868.799 us | 647.1369 us | 1,897.9404 us | 8,311.950 us |
SumArrayExtraLinq | 1,027.563 us | 60.4231 us | 176.2571 us | 971.487 us |
AverageArrayLinq | 8,006.273 us | 243.3820 us | 666.2542 us | 7,839.090 us |
AverageArrayExtraLinq | 1,141.601 us | 47.5776 us | 133.4128 us | 1,097.886 us |
SumListLinq | 10,215.340 us | 361.8532 us | 1,032.3874 us | 9,840.073 us |
SumListExtraLinq | 3,231.168 us | 64.2775 us | 177.0390 us | 3,193.271 us |
MaxLinq | 7,362.926 us | 146.3407 us | 367.1402 us | 7,237.511 us |
MaxOrderByLinq | 13,921.340 us | 268.3250 us | 237.8632 us | 13,831.188 us |
MaxByExtraLinq | 7,101.532 us | 174.6859 us | 495.5549 us | 7,093.998 us |
ExactlyLinq | 5.094 us | 0.2096 us | 0.6012 us | 4.925 us |
ExactlyExtraLinq | 6.925 us | 0.1935 us | 0.5551 us | 6.842 us |
Summarizing: all this sort of improvements are micro-optimizations, which can be very beneficial for a large enterprise project.
- AtLeast
- AtMost
- Average
- CountBetween
- DistinctBy
- Exactly
- ExceptBy
- ForEach
- IntersectBy
- MaxBy
- MinBy
- OrderBy / ThenBy
- Pairwise
- Random
- RandomDouble
- Shuffle
- Sum
- TakeLast
- ToHashSet
Checks whether the number of elements is greater or equal to the given integer. Complexity is O(1)
or O(m)
where m
is number supplied into the method.
bool result = source.AtLeast(5);
Checks whether the number of elements is less or equal to the given integer. Complexity is O(1)
or O(m)
where m
is number supplied into the method.
bool result = source.AtMost(5);
Returns the average of a sequence of numeric values. Has overloads for int
, uint
, long
, ulong
, float
, double
, decimal
, corresponding Nullable<T>
, and overloads for T[]
and List<T>
which are faster than LINQ Average()
method.
var result = source.Average();
Checks whether the number of elements is between an inclusive range of minimum and maximum integers. Complexity is O(1)
or O(max)
where max
is the second parameter supplied into the method.
bool result = source.CountBetween(4, 6);
Returns distinct elements of the given source using keySelector
and comparer (can be null
). Complexity is O(n)
where n
is number of elements in the sequence.
var result = source.DistinctBy(p => p.Category);
Checks whether the number of elements in the source is equal to the given integer. Complexity is O(1)
or O(m)
where m
is number supplied into the method.
bool result = source.Exactly(5);
Returns the set of elements in the first sequence which aren't in the second sequence, according to a given key selector and comparer (can be null
). Parameter includeDuplicates
specifies whether to return duplicates from the first sequence. Complexity is O(n)
where n
is total number of elements in both sequences.
var result = first.ExceptBy(second, i => i.Id);
ExceptBy
has an overload which accepts sequences with different generic T
types. In this case you need to specify two key selectors: one for the first sequence and another one for the second sequence. These two selectors must return the same type.
var result = first.ExceptBy(firstItem => firstItem.Property, second, secondItem => secondItem.Property);
Performs the specified action on each element of the a sequence.
source.ForEach((x, i) => { Console.WriteLine($"Item: {x}; Index: {i}"); });
Returns the set intersection of two sequences according to a given key selector and comparer (can be null
). Complexity is O(n)
where n
is total number of elements in both sequences.
var result = first.IntersectBy(second, i => i.Id);
Returns the maximal element of the given source based on the given selector and comparer (can be null
). Complexity is O(n)
where n
is number of elements in the sequence. Has overloads for T[]
and List<T>
which are faster than LINQ Max()
method.
var result = source.MaxBy(p => p.Price);
Returns the minimal element of the given source based on the given selector and comparer (can be null
). Complexity is O(n)
where n
is number of elements in the sequence. Has overloads for T[]
and List<T>
which are faster than LINQ Min()
method.
var result = source.MinBy(p => p.Price);
Sorts the elements of a sequence in ascending or descending order according to a key. Internaly uses standard LINQ OrderBy
, OrderByDescending
, ThenBy
and ThenByDescending
. Complexity is O(n log n)
where n
is number of elements in the sequence.
var result = source.OrderBy(p => p.Price, SortOrder.Descending).ThenBy(p => p.Name, SortOrder.Ascending);
Returns a sequence of each element in the input sequence and its predecessor, with the exception of the first element which is only returned as the predecessor of the second element. Complexity is O(n)
where n
is number of elements in the sequence.
var result = source.Pairwise((first, second) => $"{first.Id} {second.Id}");
Returns an infinite sequence of random integers using the standard .NET random number generator. If Random
instance is not supplied into the method, thread-safe implementation of Random
is used.
var result = ExtraEnumerable.Random(1, 10);
Returns an infinite sequence of random floating-point number that is greater than or equal to 0.0, and less than 1.0. If Random
instance is not supplied into the method, thread-safe implementation of Random
is used.
var result = ExtraEnumerable.RandomDouble();
Returns an infinite sequence of a random permutation of a finite sequence using Fisher–Yates algorithm. If Random
instance is not supplied into the method, thread-safe implementation of Random
is used. Complexity is O(n)
where n
is number of elements in the sequence.
var result = source.Shuffle();
Returns the sum of a sequence of numeric values. Has overloads for int
, uint
, long
, ulong
, float
, double
, decimal
, corresponding Nullable<T>
, and overloads for T[]
and List<T>
which are faster than LINQ Sum()
method.
var result = source.Sum();
Returns a specified number of elements from the end of a sequence.
var result = source.TakeLast(5);
Creates a HashSet<T>
from an IEnumerable<T>
.
var result = source.ToHashSet();