Je développe un programme c # qui a un "utilisateurs IEnumerable" qui stocke les identifiants de 4 millions d'utilisateurs. Je dois parcourir le tableau et extraire un lot 1000 ID à chaque fois pour effectuer certaines opérations avec une autre méthode.
Comment extraire 1000 identifiants à la fois à partir du début de Ienumerable ... faire autre chose, puis récupérer le prochain lot de 1000 et ainsi de suite?
Est-ce possible?
On dirait que vous devez utiliser les méthodes Skip et Take de votre objet. Exemple:
users.Skip(1000).Take(1000)
cela éviterait les 1 000 premiers et les 1 000 suivants.
Vous pouvez utiliser une variable entière avec le paramètre Skip et vous pouvez ajuster le nombre de sauts. Vous pouvez ensuite l'appeler dans une méthode.
public IEnumerable<user> GetBatch(int pageNumber)
{
return users.Skip(pageNumber * 1000).Take(1000);
}
Vous pouvez utiliser L'opérateur de lot de MoreLINQ (disponible auprès de NuGet):
foreach(IEnumerable<User> batch in users.Batch(1000))
// use batch
Si l'utilisation simple de la bibliothèque n'est pas une option, vous pouvez réutiliser l'implémentation:
public static IEnumerable<IEnumerable<T>> Batch<T>(
this IEnumerable<T> source, int size)
{
T[] bucket = null;
var count = 0;
foreach (var item in source)
{
if (bucket == null)
bucket = new T[size];
bucket[count++] = item;
if (count != size)
continue;
yield return bucket.Select(x => x);
bucket = null;
count = 0;
}
// Return the last bucket with all remaining elements
if (bucket != null && count > 0)
yield return bucket.Take(count);
}
BTW pour la performance, vous pouvez simplement renvoyer un seau sans appeler Select(x => x)
. Select est optimisé pour les tableaux, mais le délégué du sélecteur serait toujours invoqué pour chaque élément. Donc, dans votre cas, il vaut mieux utiliser
yield return bucket;
La méthode la plus simple consiste probablement à utiliser la méthode GroupBy
dans LINQ:
var batches = myEnumerable
.Select((x, i) => new { x, i })
.GroupBy(p => (p.i / 1000), (p, i) => p.x);
Mais pour une solution plus sophistiquée, consultez cet article blog sur la manière de créer votre propre méthode d’extension. Dupliqué ici pour la postérité:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> collection, int batchSize)
{
List<T> nextbatch = new List<T>(batchSize);
foreach (T item in collection)
{
nextbatch.Add(item);
if (nextbatch.Count == batchSize)
{
yield return nextbatch;
nextbatch = new List<T>();
// or nextbatch.Clear(); but see Servy's comment below
}
}
if (nextbatch.Count > 0)
yield return nextbatch;
}
essayez d'utiliser ceci:
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IEnumerable<TSource> source,
int batchSize)
{
var batch = new List<TSource>();
foreach (var item in source)
{
batch.Add(item);
if (batch.Count == batchSize)
{
yield return batch;
batch = new List<TSource>();
}
}
if (batch.Any()) yield return batch;
}
et d'utiliser la fonction ci-dessus:
foreach (var list in Users.Batch(1000))
{
}
Vous pouvez y parvenir en utilisant la méthode d’extension Take and Skip Enumerable. Pour plus d’informations sur le contrôle de l’utilisation linq 101
Quelque chose comme ça marcherait:
List<MyClass> batch = new List<MyClass>();
foreach (MyClass item in items)
{
batch.Add(item);
if (batch.Count == 1000)
{
// Perform operation on batch
batch.Clear();
}
}
// Process last batch
if (batch.Any())
{
// Perform operation on batch
}
Et vous pouvez généraliser ceci dans une méthode générique, comme ceci:
static void PerformBatchedOperation<T>(IEnumerable<T> items,
Action<IEnumerable<T>> operation,
int batchSize)
{
List<T> batch = new List<T>();
foreach (T item in items)
{
batch.Add(item);
if (batch.Count == batchSize)
{
operation(batch);
batch.Clear();
}
}
// Process last batch
if (batch.Any())
{
operation(batch);
}
}
Que diriez-vous
int batchsize = 5;
List<string> colection = new List<string> { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
for (int x = 0; x < Math.Ceiling((decimal)colection.Count / batchsize); x++)
{
var t = colection.Skip(x * batchsize).Take(batchsize);
}
Vous pouvez utiliser Take operator linq
Lien: http://msdn.Microsoft.com/fr-fr/library/vstudio/bb503062.aspx