How to select values within a provided index range from a List using LINQ

C#LinqListRange

C# Problem Overview


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.

C# Solutions


Solution 1 - C#

Use Skip then Take.

yourEnumerable.Skip(4).Take(3).Select( x=>x )

(from p in intList.Skip(x).Take(n) select p).sum()

Solution 2 - C#

You can use GetRange()

list.GetRange(index, count);

Solution 3 - C#

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++;
    }
}

Solution 4 - C#

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"};

Solution 5 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPunit VoraView Question on Stackoverflow
Solution 1 - C#Adam SillsView Answer on Stackoverflow
Solution 2 - C#OnuralpView Answer on Stackoverflow
Solution 3 - C#TaoView Answer on Stackoverflow
Solution 4 - C#stomyView Answer on Stackoverflow
Solution 5 - C#Jaroslav KubacekView Answer on Stackoverflow