57

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.

Community
  • 1
  • 1
LJNielsenDk
  • 1,414
  • 1
  • 16
  • 32
  • I actually implemented Python-type slicing in ArraySlice. Check it out: https://github.com/henon/SliceAndDice – henon May 04 '19 at 18:34

7 Answers7

87

The closest is really LINQ .Skip() and .Take()

Example:

var result1 = myList.Skip(2).Take(2);
var result2 = myList.Skip(1);
var result3 = myList.Take(3);
var result4 = myList.Take(3).Concat(myList.Skip(4));
Jakub Wasilewski
  • 2,916
  • 22
  • 27
Ben
  • 1,913
  • 15
  • 16
  • +1 You beat me to the trickier examples while I was grabbing MSDN URLs :-) This one is yours. Might I suggest using `result1`, `result2` to match the OP's examples? – Jonathon Reinhart Dec 19 '13 at 10:12
  • 2
    I wish I could use Python (work project), but I guess I'll have to settle for this ugly stuff where I actually have to think when reading it. – LJNielsenDk Dec 19 '13 at 10:23
  • 1
    While the answer is very helpful, example is totally wrong except 2nd line. I've suggested an edit. – Necronomicron Jun 12 '16 at 13:57
  • 5
    This solution is incomplete as it doesn't support negative indexes (e.g., [-5:-4]). – kazuoua Jan 22 '17 at 20:08
  • 3
    LINQ `SklpLast` and `TakeLast` methods imitate negative indexes. – BHC Aug 01 '19 at 08:57
45

As of C#8 slicing becomes a lot easier for indexed data structures.

var result1 = myList[2..5]; // end (5) is exclusive
var result2 = myList[1..^0]; // from index 1 to the end 
var result3 = myList[0..3]; // end (3) exclusive

Read more about Ranges and indices here and here.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • 7
    `List` does not implement indexing with `Range` and the Range pattern requires a member named `Slice` and sadly doesn't support extension methods. – NetMage May 12 '21 at 23:52
16

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.

bashrc
  • 4,725
  • 1
  • 22
  • 49
3

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);
}
Cristian Garcia
  • 9,630
  • 6
  • 54
  • 75
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 }
Alex
  • 31
  • 2
1
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;
}
Amnesh Goel
  • 2,617
  • 3
  • 28
  • 47
0

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));
}
Jabba
  • 19,598
  • 6
  • 52
  • 45