将数组划分为子序列数组

时间:2010-07-09 07:58:40

标签: c#

我有一个字节数组:

byte[] bytes;  // many elements

我需要将其划分为X元素的字节数组的子序列。例如,x = 4。

如果bytes.Length没有乘以X,则将0添加到最后一个子序列数组,因此所有子序列的长度必须为X

Linq可用。

PS:我的尝试

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();
}

13 个答案:

答案 0 :(得分:30)

这很可爱:

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();
        }
    }
}

答案 1 :(得分:6)

如果总是得到source.Length % size != 0,则投票答案有效,尽管它过于冗长。这是一个更好的实现:

public static IEnumerable<T[]> AsChunks<T>(IEnumerable<T> source, int size)
{
    var chunk = new T[size];
    var i = 0;
    foreach(var e in source)
    {
        chunk[i++] = e;
        if (i==size)
        {
            yield return chunk;
            i=0;
        }
    }
    if (i>0) // Anything left?
    {
        Array.Resize(ref chunk, i);
        yield return chunk;
    }
}

void Main()
{
    foreach(var chunk in AsChunks("Hello World!",5))
        Console.WriteLine(new string(chunk));
}

产:

  1. 你好
  2. WORL
  3. d <!/ LI>

答案 2 :(得分:3)

这个怎么样:

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;      
}

答案 3 :(得分:2)

这很好:

    public static IEnumerable<IEnumerable<T>> GetBatches<T>(this IEnumerable<T> items, int batchsize) {
        var itemsCopy = items;
        while (itemsCopy.Any()) {
            yield return itemsCopy.Take(batchsize);
            itemsCopy = itemsCopy.Skip(batchsize);
        }
    }

答案 4 :(得分:1)

    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);
}

答案 5 :(得分:1)

你可以试试这个:

    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);

询问是否有任何不清楚的地方。

答案 6 :(得分:1)

你当然想要采用Marc Gravell的解决方案,但是我无法抗拒将纯粹的LINQ版本混合在一起,只是为了看看它是否可以完成:

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;
              });
}

答案 7 :(得分:1)

如果有人想要纯功能解决方案 -

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());
}

答案 8 :(得分:1)

/// <summary>
/// Splits an array of bytes into a List<byte[]> holding the
/// chunks of the original array. If the size of the chunks is bigger than
/// the array it will return the original array to be split.
/// </summary>
/// <param name="array">The array to split</param>
/// <param name="size">the size of the chunks</param>
/// <returns></returns>
public static List<byte[]> SplitArray(byte[] array, int size)
{
    List<byte[]> chunksList = new List<byte[]>();
    int skipCounter = 0;

    while (skipCounter < array.Length)
    {
        byte[] chunk = array.Skip(skipCounter).Take(size).ToArray<byte>();
        chunksList.Add(chunk);
        skipCounter += chunk.Length;
    }
    return chunksList;
}

答案 9 :(得分:0)

//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());
}   

答案 10 :(得分:0)

static IEnumerable<T[]> Chunkify<T>(IEnumerable<T> items, int size)
    {
    var chunk = new List<T>(size);
    foreach (T item in items)
        {
        chunk.Add(item);
        if (chunk.Count == size)
            {
            yield return chunk.ToArray();
            chunk.Clear();
            }
        }
    if (chunk.Count > 0)
        {
        yield return chunk.ToArray();
        }
    }

答案 11 :(得分:0)

这个答案更适用于IEnumerable,但question被标记为重复。

有很多解决方案,但对我来说都不够懒。这个可以解决问题:

  private class CachedEnumeration<T> : IEnumerable<T>  
  {  
    /// <summary>  
    /// enumerator for the cachedEnumeration class  
    /// </summary>  
    class CachedEnumerator : IEnumerator<T>  
    {  
      private readonly CachedEnumeration<T> m_source;  
      private int m_index;  
      public CachedEnumerator(CachedEnumeration<T> source)  
      {  
        m_source = source;  
        // start at index -1, since an enumerator needs to start with MoveNext before calling current  
        m_index = -1;  
      }  
      public T Current { get { return m_source.m_items[m_index]; } }  
      public void Dispose() { }  
      object System.Collections.IEnumerator.Current { get { return Current; } } 
      public bool MoveNext()  
      {  
        // if we have cached items, just increase our index  
        if (m_source.m_items.Count > m_index + 1)  
        {  
          m_index++;  
          return true;  
        }  
        else 
        {  
          var result = m_source.FetchOne();  
          if (result) m_index++;  
          return result;  
        }  
      }  
      public void Reset()  
      {  
        m_index = -1;  
      }  
    }  
    /// <summary>  
    /// list containing all the items  
    /// </summary>  
    private readonly List<T> m_items;  
    /// <summary>  
    /// callback how to fetch an item  
    /// </summary>  
    private readonly Func<Tuple<bool, T>> m_fetchMethod;  
    private readonly int m_targetSize;  
    public CachedEnumeration(int size, T firstItem, Func<Tuple<bool, T>> fetchMethod)  
    {  
      m_items = new List<T>(size);  
      m_items.Add(firstItem);  
      m_fetchMethod = fetchMethod;  
      m_targetSize = size;  
    }  
    public IEnumerator<T> GetEnumerator()  
    {  
      return new CachedEnumerator(this);  
    }  
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()  
    {  
      return GetEnumerator();  
    }  
    private bool FetchOne()  
    {  
      if (IsFull) return false;  
      var result = m_fetchMethod();  
      if (result.Item1) m_items.Add(result.Item2);  
      return result.Item1;  
    }  
    /// <summary>  
    /// fetches all items to the cached enumerable  
    /// </summary>  
    public void FetchAll()  
    {  
      while (FetchOne()) { }  
    }  
    /// <summary>  
    /// tells weather the enumeration is already full  
    /// </summary>  
    public bool IsFull { get { return m_targetSize == m_items.Count; } }  
  }  
  /// <summary>  
  /// partitions the <paramref name="source"/> to parts of size <paramref name="size"/>  
  /// </summary>  
  public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int size)  
  {  
    if (source == null) throw new ArgumentNullException("source");  
    if (size < 1) throw new ArgumentException(string.Format("The specified size ({0}) is invalid, it needs to be at least 1.", size), "size");  
    var enumerator = source.GetEnumerator();  
    while (enumerator.MoveNext())  
    {  
      var lastResult = new CachedEnumeration<T>(size, enumerator.Current, () => Tuple.Create(enumerator.MoveNext(), enumerator.Current));  
      yield return lastResult;  
      lastResult.FetchAll();  
    }  
  }  

您可以找到单元测试和来源here

答案 12 :(得分:0)

我一直在为我的项目解决类似问题,我想出了这个漂亮的解决方案:

dataAsIEnumerable =&gt;您想要拆分为批次的来源

BatchSize =&gt;您的批量大小

            var batchSize = dataAsIEnumerable.Count / BatchSize;

            // not enought items, create at least one batch
            if (batchSize < 1)
                batchSize = 1;

            var dataAsList = dataAsIEnumerable.ToList();
            var batchAsSplit = new List<List<Model>>();

            for (int j = 0; j < batchSize; j++)
            {
                batchAsSplit.Add(dataAsList.GetRange(j * BatchSize, (dataAsList.Count - (j * BatchSize)) - BatchSize > 0 ? BatchSize : dataAsList.Count - (j * BatchSize)));
            }

            Parallel.ForEach(batchAsSplit, item =>
            {
                lock (MyContent)
                    MyContent.InsertBulk(item);
            });

代码将IEnumerate类型的集合枚举到List中,该类具有GetRange操作并在之后生成批量集合。然后执行批量保存到MyContent(db)。