Hello is there any efficient method implemented already to get the functionality of Haskell Data.List.span?
span :: (a -> Bool) -> [a] -> ([a], [a])
Basically given a list and a predicate i want to split the list in two after the first occurence of a false predicate.The elements after the pivot element that tests False may or may not respect the predicate , but I do not care.
List: [1,2,3,1,2,3]
Predicate: x<3
Span: `span (x<3) [1,2,3,1,2,3]` => `([1,2],[3,1,2,3])`
Update
I do not care of the elements after the first false predicate.I just want to split the list at the first occurence of False predicate. The sequence can be True after the first False predicate but I still want to split it.
If you are happy with using lists, then you can make a single pass through the source list to create two new lists, like so:
public static (List<T> part1, List<T> part2) SplitListBy<T>(List<T> source, Predicate<T> splitWhen)
{
var part1 = new List<T>();
int i;
for (i = 0; i < source.Count && !splitWhen(source[i]); ++i)
part1.Add(source[i]);
var part2 = source.GetRange(i, source.Count - i);
return (part1, part2);
}
This should be extremely performant. Note that this uses a tuple to return the two lists, which requires C# 7 or later. If you can't use c# 7+, you'll have to change the code to use an out parameter to return one of the lists.
Test code:
var list = new List<int>{ 1, 2, 3, 1, 2, 3 };
var (part1, part2) = SplitListBy(list, item => item >= 3);
Console.WriteLine(string.Join(", ", part1));
Console.WriteLine(string.Join(", ", part2));
Output:
1, 2
3, 1, 2, 3
If you don't need two new lists, but just want to use the original list for one part and a single new list for the other part, you can do it like this:
public static List<T> SplitListBy<T>(List<T> source, Predicate<T> splitWhen)
{
int i;
for (i = 0; i < source.Count && !splitWhen(source[i]); ++i)
;
var part2 = source.GetRange(i, source.Count - i);
source.RemoveRange(i, source.Count - i);
return part2;
}
Test code for this is very similar:
var list = new List<int>{ 1, 2, 3, 1, 2, 3 };
var part2 = SplitListBy(list, item => item >= 3);
Console.WriteLine(string.Join(", ", list));
Console.WriteLine(string.Join(", ", part2));
(Output is the same as the other test code.)
You could make use of TakeWhile and Skip:
public static IEnumerable<IEnumerable<T>> SplitWhen<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
var first = enumerable.TakeWhile(predicate);
yield return first;
var second = enumerable.Skip(first.Count());
yield return second;
}
Update
To avoid multiple iterations, and not requiring the use of a list or array:
public static IEnumerable<IEnumerable<T>> SplitWhen<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
yield return enumerable.TakeWhile(predicate);
yield return enumerable.TakeAfter(predicate);
}
public static IEnumerable<T> TakeAfter<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
bool yielding = false;
foreach (T item in enumerable)
{
if (yielding = yielding || !predicate(item))
{
yield return item;
}
}
}
At the time that I'm writing this answer, I don't think that any of the other answers faithfully replicate Haskell's span function. That's okay, you may actually be looking for something else, but I wanted to add this for completion's sake.
First, you can't necessarily assume that span only iterates over the input list once. It's difficult to reason about Haskell's run-time behaviour because of its lazy evaluation, but consider this list:
xs = [trace "one" 1, trace "two" 2, trace "three" 3,
trace "one" 1, trace "two" 2, trace "three" 3]
Here I've deliberately used trace from Debug.Trace so that we can observe what's going on. Specifically, I want to point your attention to what happens if you iterate over the lists independently, as one would probably do in 'real' code:
Prelude Data.List Debug.Trace> (l, r) = span (< 3) xs
Prelude Data.List Debug.Trace> l
one
[1two
,2three
]
Iterating over the first list stops at the first value that evaluates to False, so that's fine and efficient. That's not the case, however, when you print the second list:
Prelude Data.List Debug.Trace> r
one
two
three
[3,one
1,two
2,three
3]
Notice that while it only prints [3, 1, 2, 3], it iterates over the entire list. How could it do otherwise? It's a function. It doesn't maintain a bookmark over how far it's already iterated the list.
On the other hand, the function does handle infinite lists:
Prelude Data.List> take 10 $ fst $ span (< 3) $ repeat 1
[1,1,1,1,1,1,1,1,1,1]
Prelude Data.List> take 10 $ fst $ span (< 3) $ repeat 3
[]
Prelude Data.List> take 10 $ snd $ span (< 3) $ repeat 3
[3,3,3,3,3,3,3,3,3,3]
As far as I can tell, few of the other answers (as I'm writing this) handle infinite lists.
In C#, lazily evaluated lists are modelled with IEnumerable<T>, so the best I've been able to come up with is this:
public static (IEnumerable<T>, IEnumerable<T>) Span<T>(
this IEnumerable<T> source,
Func<T, bool> pred)
{
return (source.TakeWhile(pred), source.SkipWhile(pred));
}
which, admittedly, is hardly above the Fairbairn threshold. It does, however, handle infinite sequences in the same way as span does:
> var (left, right) = new[] { 1, 2, 3, 1, 2, 3 }.Span(x => x < 3);
> left
TakeWhileIterator { 1, 2 }
> right
SkipWhileIterator { 3, 1, 2, 3 }
> var (left, right) = 1.RepeatInfinite().Span(x => x < 3);
> left.Take(10)
TakeIterator { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
> var (left, right) = 3.RepeatInfinite().Span(x => x < 3);
> right.Take(10)
TakeIterator { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
> left.Take(10)
TakeIterator { }
I don't think there is a native .NET Framework or .NET Core method that does this, so you'll probably have to write your own. Here is my extension method implementation of this:
public static Tuple<IEnumerable<T>, IEnumerable<T>> SplitWhen<T>(this IEnumerable<T> self, Func<T, bool> func)
{
// Enumerate self to an array so we don't do it multiple times
var enumerable = self as T[] ?? self.ToArray();
var matching = enumerable.TakeWhile(func).ToArray();
var notMatching = enumerable.Skip(matching.Length);
return new Tuple<IEnumerable<T>, IEnumerable<T>>(matching, notMatching);
}
This method will return a tuple with tuple.Item1 being the part of the list that matches the predicate, and tuple.Item2 being the rest of the list.
This method needs to be declared in a separate static class as it is an extension method for IEnumerable<T>. You can also use Tuple construction/ deconstruction if you want to name Item1 and Item2 something different
the simplest way to use ToLookup
example
var listInt = new List<int>{1, 2, 3, 4, 5, 6};
var result = listInt.ToLookup(x => x > 3);
Result
[[1,2,3], [4,5,6]]
Edit
var listInt = new List<int> { 1, 2, 3, 1, 2, 3 };
Create an extension method
public static IEnumerable<T> TakeUntil<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (var item in source)
{
if (!predicate(item))
break;
yield return item;
}
}
and call it
var first = listInt.TakeUntil(x => x < 3);
var second = listInt.Skip(first.Count());
Result
first = [1,2]
second = [3, 1, 2, 3]
I believe you are looking for a c# IEnumerable. You could write for example
IEnumerable<int> list = new List<int> { 1,2,3,4,5,6};
var list1 = list.Where(x=>x>3); //deferred execution
var list2 = list.Where(x=>x<=3); //deferred execution
I have accepted #Matthew Watson solution.Though i will also post a little modified version using the Span and ReadOnlyMemory
public static (IEnumerable<T>first,IEnumerable<T> second) Span<T>(this ReadOnlyMemory<T> original,Func<T,bool> predicate) {
List<T> list = new List<T>();
int splitIndex = 0;
for (int i = 0; i < original.Length && !predicate(original.Span[i]); i++) {
list.Add(original.Span[splitIndex=i]);
}
var part2 = original.Slice(splitIndex);
return (list, part2.ToArray());
}
Related
I have a list of ordered numbers in C# and i want to calculate the min and max values that can take according to their secuencial value, with LINQ
The list is always ordered and never is empty.
For example:
My list object:
1060
1061
....
1089
1090
6368
6369
....
6383
6384
30165
30166
....
30214
30215
My expected results:
1060-1090
6368-6384
30165-30215
Thanks.
//Sample list of ordered integers
List<int> lst = new List<int>{101,102,103,104,106,107,108,111,112,114,120,121};
// find minimum element of each sub-sequence within the above list
var minBoundaries = lst.Where(i => !lst.Contains(i-1)).ToList();
// find maximum element of each sub-sequence within the above list
var maxBoundaries = lst.Where(i => !lst.Contains(i+1)).ToList();
//format minimum and maximum elements of each sub-sequence as per the sample output in the question
var result = new List<string>();
for(int i = 0; i < maxBoundaries.Count; i++)
result.Add(minBoundaries[i]+"-"+maxBoundaries[i]);
For problems like these, the Zip method is handy. This is what it does:
Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
It can be used to pair the consecutive elements of a sequence, by ziping the sequence with itself.
var source = new List<int> { 1, 2, 3, 4, 5, 11, 12, 13, 21, 22 };
var gaps = source
.Zip(source.Skip(1), (n1, n2) => (n1, n2, gap: n2 - n1)) // Calculate the gaps
.Where(e => e.gap != 1) // Select non sequential pairs
.ToArray();
var gapsEx = gaps
.Prepend((n1: 0, n2: source.First(), gap: 0)) // Add the first element
.Append((n1: source.Last(), n2: 0, gap: 0)) // Add the last element
.ToArray();
var results = gapsEx
.Zip(gapsEx.Skip(1), (e1, e2) => (from: e1.n2, to: e2.n1)); // Pairwise gaps
Console.WriteLine($"Results: {String.Join(", ", results.Select(r => r.from + "-" + r.to))}");
Output:
Results: 1-5, 11-13, 21-22
Consider creating an extension method for IEnumerable<TSource>, so you can use it as if it was a LINQ function. See Extension Methods Demystified
Your example didn't handle several problems:
What if your input sequence is empty?
What if the input is not ordered?
What if you've got several time the same value: 1 2 3 3 3 3 4 5?
What if you have sub-sequences with only one contiguous number: 1 2 7 18 19?
So let's give a proper requirement:
Given an input sequence of integer numbers, create an output sequence of integer pairs, where the values are the first and the last number of a sequence of contiguous numbers in the input sequence.
Examples:
1060 1061 ... 1089 1090 6368 6369 ... 6384 30165 ... => [1060, 1090] [6369, 6384] [30165
2 3 4 5 17 18 19 4 5 6 7 1 2 3 4 5 => [2, 5] [17, 19] [4, 7] [1 5]
2 3 4 5 6 8 9 => [2, 5] [6, 6] [8, 9]
I'll return the sequence of pairs as a sequence of Tuple<int, int>. If desired you can create a dedicated class for this.
static IEnumerable<Tuple<int, int>> ToMinMaxTuples(this IEnumerable<int> source)
{
// TODO: source == null
var enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{
// there is at least one item in source
int min = enumerator.Current;
int max = min;
while (enumerator.MoveNext())
{
// there is another item in the sequence
if (enumerator.Current == max + 1)
{
// current is part of the current sequence, continue with next number
max = enumerator.Current;
}
else
{
// current is not part of the current sequence,
// it is the start of the next one
// yield return [min, max] as a Tuple:
yield return new Tuple<int, int>(min, max);
// start the next sequence:
min = enumerator.Current;
max = min;
}
}
}
}
usage:
IEnumerable<Tuple<int, int>> result = myInputList.ToMinMaxTuples();
Or in the middle of some big LINQ statement:
var result = Students
.Where(student => student.Country == "Republique Française")
.Select(student => student.Grade)
.ToMinMaxTuples()
.OrderBy(tuple => tuple.Item1)
.ThenBy(tuple => tuple.Item2);
If you implement a simple pair class then you can use the .Aggregate() LINQ method.
The pair class would be necessary since Tuples are immutable, but it can easily be constructed like so...
public class MinMaxPair<T>
{
public MinMaxPair(T min, T max)
{
Min = min;
Max = max;
}
public T Min;
public T Max;
}
Then with that in place, the .Aggregate() call would simply be
nums.Aggregate(
new List<MinMaxPair<int>>(),
(sets, next) =>
{
if (!sets.Any() || next - sets.Last().Max > 1)
{
sets.Add(new MinMaxPair<int>(next, next));
}
else
{
var minMax = sets.Last();
if (next < minMax.Min)
minMax.Min = next;
else
minMax.Max = next;
}
return sets;
});
Using a pair enhanced version of my Scan extension method, which is based on the APL scan operator that is similar to aggregate, but returns the intermediate results, I have created variable generalized grouping methods. Using GroupByPairsWhile I (had previously) created a GroupBySequential method for this sort of problem.
public static class IEnumerableExt {
// TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
// PrevKeyItem.Key = Previous Key
// PrevKeyItem.Value = Previous Item
// curItem = Current Item
// returns new Key
public static IEnumerable<(TKey Key, T Value)> ScanToPairs<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
using (var srce = src.GetEnumerator())
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combineFn(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
// bool testFn(T prevItem, T curItem)
// returns groups by runs of matching bool
public static IEnumerable<IGrouping<int, T>> GroupByPairsWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
src.ScanToPairs(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value);
public static IEnumerable<IGrouping<int, int>> GroupBySequential(this IEnumerable<int> src) => src.GroupByPairsWhile((prev, cur) => prev + 1 == cur);
}
With the extension method, your problem is simple:
var ans = src.GroupBySequential().Select(g => new { Min = g.Min(), Max = g.Max() });
This assumes the list is not ordered. If the list is known to be ordered, you could use First() and Last() instead of Min() and Max().
NOTE: The extension methods may seem complicated, but they provide the basis for multiple different types of grouping, including grouping by runs of equal items, grouping by generalized test functions, and with various seed and ending strategies for dealing with the first and last element when working in pairs.
This question already has answers here:
How to check if list contains another list in same order
(2 answers)
Closed 4 years ago.
Is there any elegant way in c# to check whether a List<T> contains a sub-List<T> similar to string.Contains(string)?
Let's say e.g. I want to test for example whether List A is contained in List B
List<int> A = new List<int>{ 1, 2, 3, 4, 3, 4, 5 };
List<int> B = new List<int>{ 3, 4, 5 };
important is that all elements have to match in exactly that order.
I know I could possibly do something like
bool Contains(List<Sampletype> source, List<Sampletype> sample)
{
// sample has to be smaller or equal length
if (sample.Count > source.Count) return false;
// doesn't even contain first element
if (!source.Contains(sample[0])) return false;
// get possible starts
// see https://stackoverflow.com/a/10443540/7111561
int[] possibleStartIndexes = source.Select((b, i) => b == sample[0] ? i : -1).Where(i => i != -1).ToArray();
foreach (int possibleStartIndex in possibleStartIndexes)
{
// start is too late -> can't match
if (possibleStartIndex + sample.Count - 1 > source.Count - 1) return false;
for (int index = possibleStartIndex; index < possibleStartIndex + sample.Count; index++)
{
// if one element does not match the whole sample doesn't match
if (source[index] != sample[index]) return false;
}
// if this is reached all elements of the sample matched
Debug.Log("Match found starting at index " + possibleStartIndex);
return true;
}
return false;
}
But I hope there is a better way to do so.
Here's a oneliner:
var result = A.Select(a => $"{a}").Aggregate((c, n) => $"{c};{n}").Contains(B.Select(b => $"{b}").Aggregate((c, n) => $"{c};{n}"));
It basically creates a string from each list, and checks whether the A string contains the B string. This way you won't just get a method like string.Contains, you actually get to use just that.
EDIT
Added separator to the string aggregations, as {1, 2, 3} would result in the same string as {1, 23}
EDIT 2
Re-adding my first approach which identifies if list B is present in list A, perhaps scattered, but still ordered:
var result = B.Intersect(A).SequenceEqual(B)
Essentially you want to slide over A and check each element of that window with the B. The last part is actually SequenceEqual and I do recommend to use it but this is just an alternative to explain the point:
bool equal = Enumerable.Range(0, A.Count() - B.Count() + 1)
.Select(i => A.Skip(i).Take(B.Count))
.Any(w => w.Select((item, i) => item.Equals(B[i])).All(item => item));
I've to create an extension method jump<T> that, taken an arbitrary sequence s, returns an infinite sequence whose elements are obtained visiting in a circular way s and skipping n elements. So, if step == 0, then all the sequence is returned (infinite times), if step == 1, let's take all the numbers in the interval [0-10] as an example, will return 0,2,4,6,8,10,1,3,5 ecc. If step==2 then the result will be 0,3,6,9,1,4,7,10.
Obviously this is only an example with an ordered list of int, i need to do so with a generic sequence of T elements.
How can i achieve that?
To test it, i created a nunit test as following:
[Test]
public void Jumping_validArg_IsOk()
{
var start = new[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
.Jumping(3)
.ToList()//remove this to make it work
.Take(4);
var expected = new[] {1, 5, 9, 2};
CollectionAssert.AreEquivalent(start, expected);
}
but it seems to never end and it throws a System.OutOfMemoryException.
The solution:
I modified a little what i selected as best answer to make it more general in this way:
public static IEnumerable<T> Jump<T>(this IEnumerable<T> sequence, int step)
{
var pos = 0;
var list = sequence.ToList();
while (true)
{
yield return list[pos];
pos = (pos + step + 1) % list.Count;
}
}
In this way it should work with all the IEnumerable. I added a comment on the test to show what to delete to make it work. I hope it's all correct.
You can implement this very easily with a yield return statement:
public static IEnumerable<T> Jump<T>(this IList<T> data, int step) {
int pos = 0;
while(true) {
yield return data[pos];
pos = (pos + step) % data.Count;
}
}
I have an IEnumerable<Point> collection. Lets say it contains 5 points (in reality it is more like 2000)
I want to order this collection so that a specifc point in the collection becomes the first element, so it's basically chopping a collection at a specific point and rejoining them together.
So my list of 5 points:
{0,0}, {10,0}, {10,10}, {5,5}, {0,10}
Reordered with respect to element at index 3 would become:
{5,5}, {0,10}, {0,0}, {10,0}, {10,10}
What is the most computationally efficient way of resolving this problem, or is there an inbuilt method that already exists... If so I can't seem to find one!
var list = new[] { 1, 2, 3, 4, 5 };
var rotated = list.Skip(3).Concat(list.Take(3));
// rotated is now {4, 5, 1, 2, 3}
A simple array copy is O(n) in this case, which should be good enough for almost all real-world purposes. However, I will grant you that in certain cases - if this is a part deep inside a multi-level algorithm - this may be relevant. Also, do you simply need to iterate through this collection in an ordered fashion or create a copy?
Linked lists are very easy to reorganize like this, although accessing random elements will be more costly. Overall, the computational efficiency will also depend on how exactly you access this collection of items (and also, what sort of items they are - value types or reference types?).
The standard .NET linked list does not seem to support such manual manipulation but in general, if you have a linked list, you can easily move around sections of the list in the way you describe, just by assigning new "next" and "previous" pointers to the endpoints.
The collection library available here supports this functionality: http://www.itu.dk/research/c5/.
Specifically, you are looking for LinkedList<T>.Slide() the method which you can use on the object returned by LinkedList<T>.View().
Version without enumerating list two times, but higher memory consumption because of the T[]:
public static IEnumerable<T> Rotate<T>(IEnumerable<T> source, int count)
{
int i = 0;
T[] temp = new T[count];
foreach (var item in source)
{
if (i < count)
{
temp[i] = item;
}
else
{
yield return item;
}
i++;
}
foreach (var item in temp)
{
yield return item;
}
}
[Test]
public void TestRotate()
{
var list = new[] { 1, 2, 3, 4, 5 };
var rotated = Rotate(list, 3);
Assert.That(rotated, Is.EqualTo(new[] { 4, 5, 1, 2, 3 }));
}
Note: Add argument checks.
Another alternative to the Linq method shown by ulrichb would be to use the Queue Class (a fifo collection) dequeue to your index, and enqueue the ones you have taken out.
The naive implementation using linq would be:
IEnumerable x = new[] { 1, 2, 3, 4 };
var tail = x.TakeWhile(i => i != 3);
var head = x.SkipWhile(i => i != 3);
var combined = head.Concat(tail); // is now 3, 4, 1, 2
What happens here is that you perform twice the comparisons needed to get to your first element in the combined sequence.
The solution is readable and compact but not very efficient.
The solutions described by the other contributors may be more efficient since they use special data structures as arrays or lists.
You can write a user defined extension of List that does the rotation by using List.Reverse(). I took the basic idea from the C++ Standard Template Library which basically uses Reverse in three steps: Reverse(first, mid) Reverse(mid, last) Reverse(first, last)
As far as I know, this is the most efficient and fastest way. I tested with 1 billion elements and the rotation Rotate(0, 50000, 800000) takes 0.00097 seconds. (By the way: adding 1 billion ints to the List already takes 7.3 seconds)
Here's the extension you can use:
public static class Extensions
{
public static void Rotate(this List<int> me, int first, int mid, int last)
{
//indexes are zero based!
if (first >= mid || mid >= lastIndex)
return;
me.Reverse(first, mid - first + 1);
me.Reverse(mid + 1, last - mid);
me.Reverse(first, last - first + 1);
}
}
The usage is like:
static void Main(string[] args)
{
List<int> iList = new List<int>{0,1,2,3,4,5};
Console.WriteLine("Before rotate:");
foreach (var item in iList)
{
Console.Write(item + " ");
}
Console.WriteLine();
int firstIndex = 0, midIndex = 2, lastIndex = 4;
iList.Rotate(firstIndex, midIndex, lastIndex);
Console.WriteLine($"After rotate {firstIndex}, {midIndex}, {lastIndex}:");
foreach (var item in iList)
{
Console.Write(item + " ");
}
Console.ReadKey();
}
I am a LINQ newbie trying to use it to acheive the following:
I have a list of ints:-
List<int> intList = new List<int>(new int[]{1,2,3,3,2,1});
Now, I want to compare the sum of the first three elements [index range 0-2] with the last three [index range 3-5] using LINQ. I tried the LINQ Select and Take extension methods as well as the SelectMany method, but I cannot figure out how to say something like
(from p in intList
where p in Take contiguous elements of intList from index x to x+n
select p).sum()
I looked at the Contains extension method too, but that doesn't see to get me what I want. Any suggestions? Thanks.
Use Skip then Take.
yourEnumerable.Skip(4).Take(3).Select( x=>x )
(from p in intList.Skip(x).Take(n) select p).sum()
You can use GetRange()
list.GetRange(index, count);
For larger lists, a separate extension method could be more appropriate for performance. I know this isn't necessary for the initial case, but the Linq (to objects) implementation relies on iterating the list, so for large lists this could be (pointlessly) expensive. A simple extension method to achieve this could be:
public static IEnumerable<TSource> IndexRange<TSource>(
this IList<TSource> source,
int fromIndex,
int toIndex)
{
int currIndex = fromIndex;
while (currIndex <= toIndex)
{
yield return source[currIndex];
currIndex++;
}
}
Starting from .NET 6 it is possible to use range syntax for Take method.
List<int> intList = new List<int>(new int[]{1, 2, 3, 3, 2, 1});
// Starting from index 0 (including) to index 3 (excluding) will select indexes (0, 1, 2)
Console.WriteLine(intList.Take(0..3).Sum()); // {1, 2, 3} -> 6
// By default is first index 0 and can be used following shortcut.
Console.WriteLine(intList.Take(..3).Sum()); // {1, 2, 3} -> 6
// Starting from index 3 (including) to index 6 (excluding) will select indexes (3, 4, 5)
Console.WriteLine(intList.Take(3..6).Sum()); // {3, 2, 1} -> 6
// By default is last index lent -1 and can be used following shortcut.
Console.WriteLine(intList.Take(3..).Sum()); // {3, 4, 5} -> 6
// Reverse index syntax can be used. Take last 3 items.
Console.WriteLine(intList.Take(^3..).Sum()); // {3, 2, 1} -> 6
// No exception will be raised in case of range is exceeded.
Console.WriteLine(intList.Take(^100..1000).Sum());
So simply put, intList.Take(..3).Sum() and intList.Take(3..).Sum() can be used with .NET 6.
To filter by specific indexes (not from-to):
public static class ListExtensions
{
public static IEnumerable<TSource> ByIndexes<TSource>(this IList<TSource> source, params int[] indexes)
{
if (indexes == null || indexes.Length == 0)
{
foreach (var item in source)
{
yield return item;
}
}
else
{
foreach (var i in indexes)
{
if (i >= 0 && i < source.Count)
yield return source[i];
}
}
}
}
For example:
string[] list = {"a1", "b2", "c3", "d4", "e5", "f6", "g7", "h8", "i9"};
var filtered = list.ByIndexes(5, 8, 100, 3, 2); // = {"f6", "i9", "d4", "c3"};