2010-07-09 7 views
11

J'ai un tableau d'octets:Diviser un tableau en un tableau de sous-séquence

octets [] octets; // plusieurs éléments

Je dois le diviser en sous-séquence de tableaux d'octets d'éléments X. Par exemple, x = 4.

Si bytes.Length ne se multiplie pas par X, alors ajoutez 0 au dernier tableau de sous-séquence, donc La longueur de toutes les sous-requêtes doit être X.

Linq disponible.

PS: mes tentatives

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

Répondre

29

Ceci est assez mignon:

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

génial! merci – nik

+1

adorable:) <- pas assez de caractères –

+0

Nice one. Cependant, méfiez-vous des éléments de tableau non initialisés lorsque le nombre d'éléments dans "source" n'est pas un multiple de "taille". – 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

Une bonne solution, mais notez qu'il est forcé de mettre en tampon toute la séquence en premier - ceci * peut * être parfaitement bien dans la plupart des cas courants. –

1

Vous pouvez essayer ceci:

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

Demandez si quelque chose ne sait pas.

3

Que diriez-vous ceci:

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

Notez que cela énumérera 'source' plusieurs fois. Ainsi, par exemple, s'il s'agit d'une requête Linq to SQL, vous exécuterez la requête SQL potentiellement des centaines de fois! Lorsque vous écrivez des méthodes comme celle-ci pour 'IEnumerable ', il est souhaitable de n'énumérer la séquence qu'une seule fois. Découvrez [cette implémentation] (http://stackoverflow.com/a/13710023/24874) pour voir ce que je veux dire. Le PO posait des questions sur une collection d'octets matérialisée là où ce n'est pas un problème, mais d'autres personnes visitant cette question peuvent avoir besoin de connaître cette distinction. –

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

Vous voulez prendre la solution bien sûr de Marc Gravell, mais je ne pouvais pas résister le piratage ensemble une version pure LINQ, juste pour voir si elle peut être fait:

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

Et si quelqu'un veut une solution purement fonctionnelle -

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

Cela le fait 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

A voté réponse fonctionne si vous obtenez toujours source.Length % size != 0, mais il est trop bavard. Ici va une plus belle mise en œuvre:

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

Production:

  1. Bonjour
  2. Worl
  3. d!
0

Cette réponse est plus pour le cas de IEnumerable, mais le question est marqué comme doublon de ceci.

Il existe de nombreuses solutions, mais pas assez paresseux pour moi.Celui-ci fait le tour:

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

Vous pouvez trouver des tests unitaires et la source here

0

J'ai résous quelque chose de similaire sur mon projet et je suis venu avec cette belle solution à la recherche:

dataAsIEnumerable => votre source que vous voulez diviser en lots

BatchSize => taille du lotLe code énumère IEnumerate type de collection dans List, qui a une opération GetRange et génère la collection de lots par la suite. Effectue ensuite une sauvegarde en masse sur MyContent (db).

Questions connexes