What is the logic behind the .. range selection operator?

Viewed 79

I am trying to understand why the .. operator works the way it does, for example:

var data = new []{0,1,2,3,4,5,6,7,8,9,10};
var test = data[1..4]; // This returns array with 1,2,3 

Logically, I would assume the result would be either 1,2,3,4 or 2,3 (if the last index isn't included then the first one shouldn't also)

or

var test = data[0..]; // This returns array with 0,1,2,3,4,5,6,7,8,9,10 (zero at index 0 is also included)
var test = data[^0..]; // This returns array with nothing,  where i would expect 10, since 10 is at index zero if we traverse the array backwards

I know there must be a reason why it was designed to work like that, but I can't seem to figure it out, so what is the purpose of this behavior?

Thank you.

2 Answers

Why is the end index not included, but the start index is?

This is known as a half open range, and there are already questions asking about this in Python and C++. Essentially, the main advantage of this is that:

  • The length of the range is exactly (end - start).
  • You don't need to add/minus 1 as much in range-based algorithms
  • To slice something in half at an index, you can use the same index: x[..i] gives you the first half, and x[i..] gives you the second half. i.e. x[..i] concatenated with i[i..] is equal to x itself.
  • If a range's end is equal to another's start, the two ranges are immediately next to each other, and no overlapping.

Why is data[^0..] empty?

This is documented clearly. ^n means Length - n, so ^0 means data.Length - 0 here, which is just data.Length. data[data.Length..] is clearly empty.

It's simple enough. Consider the following:

var data = new []{0,1,2,3,4,5,6,7,8,9,10};
var test = data[0..10];

The 0 to 10, implies that it will select 11 items, if the last was inclusive. You have to start at 0, as C# indexes are zero based, and you'd need a -1 to include the first, if they were both exlusive.

As mentioned in the documentation:

A range specifies the start and end of a range. Ranges are exclusive, meaning the end isn't included in the range. The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.

The data[^0..]; statements means, starting from the 0 position, bring whatever is below it. The ^ operator works inversly for the same reasons. (^0 is not inclusive of 0)

Related