Copying part of a list
Sometimes you need only part of a List<T>: the first few items, or everything after a certain position. GetRange(start, count) copies that part into a new list. Its two arguments are a start index and a count, how many items to copy. They are not a start and an end index.
List<string> letters = new List<string>(new[] {"a", "b", "c", "d", "e"});
List<string> middle = letters.GetRange(1, 3);
Console.WriteLine(string.Join(" ", middle));
Console.WriteLine(string.Join(" ", letters));
b c d
a b c d e
GetRange(1, 3) means three items, starting at index 1: the items at indexes 1, 2 and 3. The original list is untouched. In a file of your own, add using System; and using System.Collections.Generic;.
From an end index to a count
If you think in terms of "from index i up to, but not including, index j", the count is j - i. For i = 1 and j = 4, the count is 3, and GetRange(i, j - i) gives b c d again.
To slice to the very end, count what is left after the start:
List<string> tail = letters.GetRange(3, letters.Count - 3);
That gives d e. The start index 3 plus the count 2 reaches exactly the end of the list.
A LINQ alternative
With using System.Linq;, Skip and Take do similar jobs, and they do not throw when the list is too short. letters.Skip(3).ToList() gives d e, and letters.Take(10).ToList() on five items simply returns all five. The lesson uses GetRange because it is a method of List<T> itself, so it needs no extra using, and it states the start and the count explicitly.
The exercise, step by step
runners holds five runners in the order they finished: Ana, Ben, Cleo, Dan, Eve.
- The podium. The first three runners start at index 0, and there are 3 of them:
runners.GetRange(0, 3). Store the result inpodium. - Everyone else. They start at index 3. The count is what is left after the podium:
runners.Count - 3, which is 2. Store it inrest.
List<string> runners = new List<string>(new[] {"Ana", "Ben", "Cleo", "Dan", "Eve"});
List<string> podium = runners.GetRange(0, 3);
List<string> rest = runners.GetRange(3, runners.Count - 3);
Console.WriteLine(podium[0] + " " + podium[1] + " " + podium[2]);
Console.WriteLine(rest[0] + " " + rest[1]);
Console.WriteLine(runners.Count);
Ana Ben Cleo
Dan Eve
5
The last line shows that runners still has five items: slicing copies, it does not remove.
Traps to avoid
- Treating the second argument as an end index.
runners.GetRange(1, 3)returns three items, not the items before index 3. Remember it as "how many". - Asking for too many. The range must fit inside the list.
runners.GetRange(3, 4)on five items throws anArgumentException, and a negative start or count throws anArgumentOutOfRangeException. Unlike some other languages, C# does not shorten the range for you. - An empty slice is allowed.
GetRange(5, 0)on five items starts at the end and returns an empty list. - The copy is shallow. The new list is separate, so changing
podium[0]does not changerunners[0]. But if the items are themselves objects, such as arrays, both lists point to the same objects. - Forgetting the copy exists. Assign the result to a variable, or the new list is lost.
Key takeaways
list.GetRange(i, count)returns a new list withcountitems from indexi.- For items from
iup toj(not included), the count isj - i. - To the end of the list, use
list.Count - i. - The original list does not change, and a range that does not fit in the list throws an exception.
