2010-07-09 12 views
11

Tengo una matriz de bytes:Divide matriz en una matriz de matriz subsecuencia

byte [] bytes; // muchos elementos

Necesito dividirlo en subsecuencia de matrices de bytes de elementos X. Por ejemplo, x = 4.

Si bytes.Length no se multiplica por X, entonces agregue 0 a la última matriz de subsidencias así que La longitud de todas las subsequnce debe ser X.

Linq disponible.

PD: mis intentos

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

Respuesta

29

Esto es muy linda:

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

¡genial! gracias – nik

+1

adorable:) <- no hay suficientes caracteres –

+0

Niza. Sin embargo, tenga cuidado con los elementos de matriz no inicializados cuando la cantidad de elementos en "fuente" no es un múltiplo de "tamaño". – mkoertgen

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

Una buena solución, pero tenga en cuenta que está obligado a almacenar en búfer la secuencia completa primero - esto * puede * estar perfectamente bien en la mayoría de los casos comunes. –

1

Usted podría intentar esto:

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

preguntar si algo no está claro.

3

¿Qué tal esto:

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

Tenga en cuenta que esto enumerará 'fuente' muchas veces. Entonces, por ejemplo, si se trata de una consulta de Linq a SQL, ¡ejecutarás la consulta SQL potencialmente cientos de veces! Al escribir métodos como este para 'IEnumerable ', es conveniente enumerar la secuencia solo una vez. Consulte [esta implementación] (http://stackoverflow.com/a/13710023/24874) para ver a qué me refiero. El OP estaba preguntando sobre una colección materializada de bytes donde esto no es un problema, pero otros que visiten esta pregunta pueden necesitar estar al tanto de esta distinción. –

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

Usted querrá tener solución, por supuesto, de Marc Gravell, pero no pude resistir la piratería juntos una versión pura de LINQ, sólo para ver si puede ser hecho:

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

Y si alguien quiere una solución puramente funcional -

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

Esto lo hace muy bien:

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

respuesta Votado funciona si usted consigue siempre source.Length % size != 0, aunque es demasiado prolijo. Aquí va una aplicación más agradable:

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

Producir:

  1. Hola
  2. Worl
  3. d!
0

Esta respuesta es más para el caso de IEnumerable, pero el question está marcado como duplicado de esto.

Hay muchas soluciones, pero ninguna lo suficientemente floja para mí.Éste es el truco:

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

puede encontrar pruebas de la unidad y la fuente here

0

he estado resolviendo algo similar en mi proyecto y me encontré con este bonito solución en busca:

dataAsIEnumerable => su fuente que desea dividir en lotes

BatchSize => el tamaño del lote

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

El código enumera IEnumerar el tipo de colección en la lista, que tiene una operación GetRange y genera la colección de lotes después. A continuación, realiza el almacenamiento masivo en MyContent (db).

Cuestiones relacionadas