With C# 8 we got ranges to get "sub lists".
While this works:
var array = new string[] { "abc", "def", "ghi" };
var subArray = array[0..1]; // works
This does not:
var list = new List<string> { "abc", "def", "ghi" };
var subList = list[0..1]; // does not work
How can I use ranges with lists?
You can use ToArray()
method to solve the problem:
var list = new List<string> { "abc", "def", "ghi" };
var subList = list.ToArray()[0..1]; // works
But note that List<T>
has GetRange
method that does the same thing:
var list = new List<string> { "abc", "def", "ghi" };
var subList = list.GetRange(0,1);//abc
List does not itself support Ranges. However, if all you have is a Range, you can create a helpful extension method like so:
public static class ListExtensions
{
public static List<T> GetRange<T>(this List<T> list, Range range)
{
var (start, length) = range.GetOffsetAndLength(list.Count);
return list.GetRange(start, length);
}
}
Then you can use it to get your sub-List:
var list = new List<string> { "abc", "def", "ghi" };
var subList = list.GetRange(0..1);
Just for completeness, I know this isn't exactly what OP is asking for.
List<T>
has a GetRange
-Method.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.getrange?view=net-5.0
List<?> range = list.GetRange(0,5);
It doesn't have much to do with the new language feature but it does what it says.
Please do not use ToArray
only to use that fancy new language feature. It's not worth the overhead.
Unfortunately you can't. From the language specification:
For example, the following .NET types support both indices and ranges: String, Span, and ReadOnlySpan. The List supports indices but doesn't support ranges.
Type Support for ranges and indices.
So the only way would be something like this, which is a bit clunky:
var subList = list.ToArray()[0..1];
Edit: I will leave the rest of my answer here for the record, but I think the answer using Range
is more elegant, and will be significantly faster, especially for large slices.
The following demonstrates how much slower using ToArray()
can be (using LINQPad - F.Rnd.Str
is my own helper method for generating random strings):
var list = new List<string>();
for (int i = 0; i < 100000; i++)
{
list.Add(F.Rnd.Str);
}
// Using ToArray()
var timer = new Stopwatch();
timer.Start();
var subList0 = list.ToArray()[42..142];
timer.Stop();
timer.ElapsedTicks.Dump("ToArray()");
// Using Indices
timer.Reset();
timer.Start();
var subList1 = new List<string>();
for (int i = 42; i < 142; i++)
{
subList1.Add(list[i]);
}
timer.Stop();
timer.ElapsedTicks.Dump("Index");
// ToArray()
// 3136
// Index
// 28
Therefore an extension method like this will be very quick:
public static class ListExtensions
{
public static List<T> GetSlice<T>(this List<T> @this, int first, int last)
{
var slice = new List<T>();
for (int i = first; i < last; i++)
{
slice.Add(@this[i]);
}
return slice;
}
}
Then you can add this to the benchmark:
// Using Extension Method
timer.Reset();
timer.Start();
var subList2 = list.GetSlice(42, 142);
timer.Stop();
timer.ElapsedTicks.Dump("Extension");
Benchmark results:
// ToArray()
// 2021
// Index
// 16
// Extension
// 15
One of the answers uses Range
to achieve the same thing, so for comparison:
timer.Reset();
timer.Start();
var range = new Range(42, 142);
var (start, length) = range.GetOffsetAndLength(list.Count);
var subList3 = list.GetRange(start, length);
timer.Stop();
timer.ElapsedTicks.Dump("Range");
Benchmark results:
// ToArray()
// 2056
// Index
// 21
// Extension
// 16
// Range
// 17
You can see the bottom three methods are essentially the same in terms of speed (as I run it multiple times they all vary between 15-21 ticks).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With