Array slices in C#

Viewed 361912

How do you do it? Given a byte array:

byte[] foo = new byte[4096];

How would I get the first x bytes of the array as a separate array? (Specifically, I need it as an IEnumerable<byte>)

This is for working with Sockets. I figure the easiest way would be array slicing, similar to Perls syntax:

@bar = @foo[0..40];

Which would return the first 41 elements into the @bar array. Is there something in C# that I'm just missing, or is there some other thing I should be doing?

LINQ is an option for me (.NET 3.5), if that helps any.

19 Answers

You could use ArraySegment<T>. It's very light-weight as it doesn't copy the array:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

Arrays are enumerable, so your foo already is an IEnumerable<byte> itself. Simply use LINQ sequence methods like Take() to get what you want out of it (don't forget to include the Linq namespace with using System.Linq;):

byte[] foo = new byte[4096];

var bar = foo.Take(41);

If you really need an array from any IEnumerable<byte> value, you could use the ToArray() method for that. That does not seem to be the case here.

You could use the arrays CopyTo() method.

Or with LINQ you can use Skip() and Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

Starting from C# 8.0/.Net Core 3.0

Array slicing will be supported, along with the new types Index and Range being added.

Range Struct docs
Index Struct docs

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

var slice = a[i1..i2]; // { 3, 4, 5 }

Above code sample taken from the C# 8.0 blog.

note that the ^ prefix indicates counting from the end of the array. As shown in the docs example

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Range and Index also work outside of slicing arrays, for example with loops

Range range = 1..4; 
foreach (var name in names[range])

Will loop through the entries 1 through 4


note that at the time of writing this answer, C# 8.0 is not yet officially released
C# 8.x and .Net Core 3.x are now available in Visual Studio 2019 and onwards

static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

//

var myslice = SliceMe(sourcearray,41);

C# 8 now (since 2019) supports Ranges which allows you to achieve Slice much easier (similar to JS syntax):

var array = new int[] { 1, 2, 3, 4, 5 };
var slice1 = array[2..^3];    // array[new Range(2, new Index(3, fromEnd: true))]
var slice2 = array[..^3];     // array[Range.EndAt(new Index(3, fromEnd: true))]
var slice3 = array[2..];      // array[Range.StartAt(2)]
var slice4 = array[..];       // array[Range.All]

You can use ranges instead of the well known LINQ functions: Skip(), Take(), Count().

If you want IEnumerable<byte>, then just

IEnumerable<byte> data = foo.Take(x);

You could use a wrapper around the original array (which is IList), like in this (untested) piece of code.

public class SubList<T> : IList<T>
{
    #region Fields

    private readonly int startIndex;
    private readonly int endIndex;
    private readonly int count;
    private readonly IList<T> source;

    #endregion

    public SubList(IList<T> source, int startIndex, int count)
    {
        this.source = source;
        this.startIndex = startIndex;
        this.count = count;
        this.endIndex = this.startIndex + this.count - 1;
    }

    #region IList<T> Members

    public int IndexOf(T item)
    {
        if (item != null)
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (item.Equals(this.source[i]))
                    return i;
            }
        }
        else
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (this.source[i] == null)
                    return i;
            }
        }
        return -1;
    }

    public void Insert(int index, T item)
    {
        throw new NotSupportedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotSupportedException();
    }

    public T this[int index]
    {
        get
        {
            if (index >= 0 && index < this.count)
                return this.source[index + this.startIndex];
            else
                throw new IndexOutOfRangeException("index");
        }
        set
        {
            if (index >= 0 && index < this.count)
                this.source[index + this.startIndex] = value;
            else
                throw new IndexOutOfRangeException("index");
        }
    }

    #endregion

    #region ICollection<T> Members

    public void Add(T item)
    {
        throw new NotSupportedException();
    }

    public void Clear()
    {
        throw new NotSupportedException();
    }

    public bool Contains(T item)
    {
        return this.IndexOf(item) >= 0;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i=0; i<this.count; i++)
        {
            array[arrayIndex + i] = this.source[i + this.startIndex];
        }
    }

    public int Count
    {
        get { return this.count; }
    }

    public bool IsReadOnly
    {
        get { return true; }
    }

    public bool Remove(T item)
    {
        throw new NotSupportedException();
    }

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = this.startIndex; i < this.endIndex; i++)
        {
            yield return this.source[i];
        }
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}

You can use Take extension method

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

I do not think C# supports the Range semantics. You could write an extension method though, like:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

But like others have said if you do not need to set a start index then Take is all you need.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace data_seniens
{
    class Program
    {
        static void Main(string[] args)
        {
            //new list
            float [] x=new float[]{11.25f,18.0f,20.0f,10.75f,9.50f, 11.25f, 18.0f, 20.0f, 10.75f, 9.50f };

            //variable
            float eat_sleep_area=x[1]+x[3];
            //print
            foreach (var VARIABLE in x)
            {
                if (VARIABLE < x[7])
                {
                    Console.WriteLine(VARIABLE);
                }
            }



            //keep app run
        Console.ReadLine();
        }
    }
}
Related