tags:

views:

109

answers:

7

I have a byte array:

byte[] bytes; // many elements

I need to divide it into subsequence of byte arrays of X elements. For example, x = 4.

If bytes.Length does not multiply by X, then add 0 to last subsequence array so Length of all subsequnce must be X.

Linq available.

PS: my attempts

    static void Main(string[] args)
    {
        List<byte> bytes = new List<byte>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

        int c = bytes.Count / 4;

        for (int i = 0; i <= c; i+=4)
        {
            int diff = bytes.Count - 4;

            if (diff < 0)
            {

            }
            else
            {
                List<byte> b = bytes.GetRange(i, 4);
            }
        }


        Console.ReadKey();
    }
+1  A: 
    const int x = 4;
var bytes = new List<byte>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
var groups = bytes.Select((b, index) => new { b, index }).GroupBy(obj => obj.index / x).Select(group => new List<byte>(group.Select(i => i.b)));
var last = groups.Last();   
while (last.Count < x)
{
    last.Add(0);
}
Steck
A nice solution, but note that it is forced to buffer the entire sequence first - this *may* be perfectly fine in most common cases.
Marc Gravell
+6  A: 

This is quite cute:

static class ChunkExtension
{
    public static IEnumerable<T[]> Chunkify<T>(
        this IEnumerable<T> source, int size)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (size < 1) throw new ArgumentOutOfRangeException("size");
        using (var iter = source.GetEnumerator())
        {
            while (iter.MoveNext())
            {
                var chunk = new T[size];
                chunk[0] = iter.Current;
                for (int i = 1; i < size && iter.MoveNext(); i++)
                {
                    chunk[i] = iter.Current;
                }
                yield return chunk;
            }
        }
    }
}
static class Program
{
    static void Main(string[] args)
    {
        List<byte> bytes = new List<byte>() {
              1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
        var chunks = bytes.Chunkify(4);
        foreach (byte[] chunk in chunks)
        {
            foreach (byte b in chunk) Console.Write(b.ToString("x2") + " ");
            Console.WriteLine();
        }
    }
}
Marc Gravell
great! thank you
nik
+1  A: 

You could try this:

    List<byte> bytes = new List<byte>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

    int partLength = 4;
    int c = bytes.Count / partLength;

    if((c % partLength) != 0)
        c++; // we need one last list which will have to be filled with 0s

    List<List<byte>> allLists = new List<List<byte>>();

    for (int i = 0; i <= c; i++)
        allLists.Add(bytes.Take(partLength).ToList());

    int zerosNeeded = partLength - allLists.Last().Count;

    for (int i = 0; i < zerosNeeded; i++)
        allLists.Last().Add(0);

Ask if anything is unclear.

Koynov
+1  A: 

How about this:

var bytes = new List<byte>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

var result = Chunkify(bytes, 4);

IEnumerable<IEnumerable<T>> Chunkify<T>(IEnumerable<T> source, int chunkSize)
{
    var indicies = 
        Enumerable.Range(0, source.Count()).Where(i => i%chunkSize==0);

    var chunks = 
            indicies
            .Select( i => source.Skip(i).Take(chunkSize) )
            .Select( chunk => new { Chunk=chunk, Count=chunk.Count() } )
            .Select( c => c.Count < chunkSize ? c.Chunk.Concat( Enumerable.Repeat( default(T), chunkSize - c.Count ) ) : c.Chunk )
            ;

    return chunks;      
}
Winston Smith
A: 
//without LINQ

List<byte> bytes = new List<byte>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
int x = 4;
int initialLength = bytes.Count;
for (int i = 0; i < (x - (initialLength % x)); i++) // adds enough 0's to list
{
    bytes.Add(0);
}

List<byte[]> byteList= new List<byte[]>(); // contains answers

for (int i=0;i<bytes.Count;i+=4)
{
    byteList.Add(bytes.GetRange(i,4).ToArray());
}   
simonalexander2005
A: 

You'll want to take Marc Gravell's solution of course, but I couldn't resist hacking together a pure LINQ version, just to see if it can be done:

static IEnumerable<T[]> LinqChunks<T>(IEnumerable<T> input, int chunkSize)
{
  return input
    //assign chunk numbers to elements by integer division
    .Select((x, index) => new {ChunkNr = index / chunkSize, Value = x})

    //group by chunk number
    .GroupBy(item => item.ChunkNr)

    //convert chunks to arrays, and pad with zeroes if necessary
    .Select(group =>
              {
                var block = group.Select(item => item.Value).ToArray();

                //if block size = chunk size -> return the block
                if (block.Length == chunkSize) return block;

                //if block size < chunk size -> this is the last block, pad it
                var lastBlock= new T[chunkSize];
                for (int i = 0; i < block.Length; i++) lastBlock[i] = block[i];
                return lastBlock;
              });
}
cfern
A: 

And if somebody wants purely functional solution -

static IEnumerable<T[]> Chunkify<T>(IEnumerable<T> input, int size)
{
    return input    
        .Concat(Enumerable.Repeat(default(T), size - input.Count() % size))
        .Select((x, i) => new { Value = x, Chunk = i / size })
        .GroupBy(x => x.Chunk, x => x.Value)
        .Select(x => x.ToArray());
}
Matajon