Readable C# equivalent of Python slice operation
Question:
What is the C# equivalent of Python slice operations?
my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
result1 = my_list[2:4]
result2 = my_list[1:]
result3 = my_list[:3]
result4 = my_list[:3] + my_list[4:]
Some of it is covered here, but it is ugly and doesn’t address all the uses of slicing to the point of it not obviously answering the question.
Answers:
This way you don’t have to subtract
public static IEnumerable<A> Slice<A> (int from, int to, IEnumerable<A> e) {
return e.Take (to).Skip (from);
}
public static T[] slice<T>(T[] l, int from, int to)
{
T[] r = new T[to - from];
for (int i = from; i < to; i++)
{
r[i-from]=l[i];
}
return r;
}
If you have a List
GetRange can come in handy.
From MSDN link:
A shallow copy of a collection of reference types, or a subset of that
collection, contains only the references to the elements of the
collection. The objects themselves are not copied. The references in
the new list point to the same objects as the references in the
original list.
The Slice
function can then be:
public static IEnumerable<T> Slice<T>(this List<T> source, int from, int to) => source.GetRange(from, to - from);
Negative ranges that python slice supports can also be handled with some loss of cleanliness.
Write a custom extension:
public static List<T> Slice<T>(this List<T> li, int start, int end)
{
if (start < 0) // support negative indexing
{
start = li.Count + start;
}
if (end < 0) // support negative indexing
{
end = li.Count + end;
}
if (start > li.Count) // if the start value is too high
{
start = li.Count;
}
if (end > li.Count) // if the end value is too high
{
end = li.Count;
}
var count = end - start; // calculate count (number of elements)
return li.GetRange(start, count); // return a shallow copy of li of count elements
}
Some tests:
[Fact]
public void Slice_list()
{
var li1 = new List<char> {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
Assert.Equal(new List<char> {'c', 'd'}, li1.Slice(2, 4));
Assert.Equal(new List<char> {'b', 'c', 'd', 'e', 'f', 'g'}, li1.Slice(1, li1.Count));
Assert.Equal(new List<char> {'a', 'b', 'c'}, li1.Slice(0, 3));
Assert.Equal(li1, li1.Slice(0, 4).Concat(li1.Slice(4, li1.Count)));
Assert.Equal(li1, li1.Slice(0, 100));
Assert.Equal(new List<char>(), li1.Slice(100, 200));
Assert.Equal(new List<char> {'g'}, li1.Slice(-1, li1.Count));
Assert.Equal(new List<char> {'f', 'g'}, li1.Slice(-2, li1.Count));
Assert.Equal(new List<char> {'a', 'b', 'c', 'd', 'e', 'f'}, li1.Slice(0, -1));
Assert.Equal(new List<char> {'c', 'd', 'e'}, li1.Slice(2, -2));
}
Here’s an extension:
public static IEnumerable<T> Slice<T>(this IEnumerable<T> source, int start = 0, int end = 0)
{
start = (start >= 0) ? start : source.Count() + start;
end = (end > 0) ? end : source.Count() + end;
return source.Skip(start).Take(end - start);
}
Examples:
var input = new[] { 0, 1, 2, 3, 4, 5, 6, 7 };
numbers.Slice(1, 4); // { 1, 2, 3 }
numbers.Slice(-3, -1); // { 5, 6 }
numbers.Slice(5); // { 5, 6, 7 }
numbers.Slice(end:-4); // { 0, 1, 2, 3 }
What is the C# equivalent of Python slice operations?
my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
result1 = my_list[2:4]
result2 = my_list[1:]
result3 = my_list[:3]
result4 = my_list[:3] + my_list[4:]
Some of it is covered here, but it is ugly and doesn’t address all the uses of slicing to the point of it not obviously answering the question.
This way you don’t have to subtract
public static IEnumerable<A> Slice<A> (int from, int to, IEnumerable<A> e) {
return e.Take (to).Skip (from);
}
public static T[] slice<T>(T[] l, int from, int to)
{
T[] r = new T[to - from];
for (int i = from; i < to; i++)
{
r[i-from]=l[i];
}
return r;
}
If you have a List
GetRange can come in handy.
From MSDN link:
A shallow copy of a collection of reference types, or a subset of that
collection, contains only the references to the elements of the
collection. The objects themselves are not copied. The references in
the new list point to the same objects as the references in the
original list.
The Slice
function can then be:
public static IEnumerable<T> Slice<T>(this List<T> source, int from, int to) => source.GetRange(from, to - from);
Negative ranges that python slice supports can also be handled with some loss of cleanliness.
Write a custom extension:
public static List<T> Slice<T>(this List<T> li, int start, int end)
{
if (start < 0) // support negative indexing
{
start = li.Count + start;
}
if (end < 0) // support negative indexing
{
end = li.Count + end;
}
if (start > li.Count) // if the start value is too high
{
start = li.Count;
}
if (end > li.Count) // if the end value is too high
{
end = li.Count;
}
var count = end - start; // calculate count (number of elements)
return li.GetRange(start, count); // return a shallow copy of li of count elements
}
Some tests:
[Fact]
public void Slice_list()
{
var li1 = new List<char> {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
Assert.Equal(new List<char> {'c', 'd'}, li1.Slice(2, 4));
Assert.Equal(new List<char> {'b', 'c', 'd', 'e', 'f', 'g'}, li1.Slice(1, li1.Count));
Assert.Equal(new List<char> {'a', 'b', 'c'}, li1.Slice(0, 3));
Assert.Equal(li1, li1.Slice(0, 4).Concat(li1.Slice(4, li1.Count)));
Assert.Equal(li1, li1.Slice(0, 100));
Assert.Equal(new List<char>(), li1.Slice(100, 200));
Assert.Equal(new List<char> {'g'}, li1.Slice(-1, li1.Count));
Assert.Equal(new List<char> {'f', 'g'}, li1.Slice(-2, li1.Count));
Assert.Equal(new List<char> {'a', 'b', 'c', 'd', 'e', 'f'}, li1.Slice(0, -1));
Assert.Equal(new List<char> {'c', 'd', 'e'}, li1.Slice(2, -2));
}
Here’s an extension:
public static IEnumerable<T> Slice<T>(this IEnumerable<T> source, int start = 0, int end = 0)
{
start = (start >= 0) ? start : source.Count() + start;
end = (end > 0) ? end : source.Count() + end;
return source.Skip(start).Take(end - start);
}
Examples:
var input = new[] { 0, 1, 2, 3, 4, 5, 6, 7 };
numbers.Slice(1, 4); // { 1, 2, 3 }
numbers.Slice(-3, -1); // { 5, 6 }
numbers.Slice(5); // { 5, 6, 7 }
numbers.Slice(end:-4); // { 0, 1, 2, 3 }