patterncsharpMinor
Chunking a list into separate lists of 5, and then iterating through each
Viewed 0 times
iteratingchunkingthrougheachintoseparatethenlistsandlist
Problem
I'm writing a service that receives a list of Latitude/Longitude objects, prepares them for transaction, and then calls a web service to write them to the organization's Mainframe. According to the existing application that I'm replacing, the Mainframe can only accept "chunks" of up to 5 Lat/Long values at a time, however the user is able to enter as many as they want.
After getting the list of Lat/Longs from the UI, if it is more than 5 pairs I break it into separate lists of 5, and then call another developers code to prepare them to transmit. This preparation includes setting up the transaction with the Case File's relevant information (FileType/FileNumber, Username, password, ect), calling
While my code works, I feel like it could be improved. Specifically, I don't care for all of the if checks, but I haven't been able to find another way to work it. I also feel like this is inefficient code, however this specific code only runs once per each case, so efficiency isn't absolutely vital.
```
public void PostLatLong(string fileType, string fileNumber, List latLongList, DateTime? transactionDate = null, string commentText1 = null, string commentText2 = null, string username = null, string password = null)
{
string transactionCode = "LATLONGS";
List> splitList = new List>();
try
{
while (latLongList.Any())
{
splitList.Add(latLongList.Take(5).ToList());
latLongList = latLongList.Skip(5).ToList();
}
foreach (List list in splitList)
{
SetupCaseFileUpdateTransaction(transactionCode, fileType, fileNumber,
After getting the list of Lat/Longs from the UI, if it is more than 5 pairs I break it into separate lists of 5, and then call another developers code to prepare them to transmit. This preparation includes setting up the transaction with the Case File's relevant information (FileType/FileNumber, Username, password, ect), calling
SetCode() or SetMisc() on each individual Latitude and Longitude string, and then calling the web service. I do this for every chunk of 5 Lat/Longs, and if the last chunk is less than 5, I only call SetCode() or SetMisc() for however many there are left. There may also be fewer than 5 records to begin with, and often times will only be 1 Latitude/Longitude.While my code works, I feel like it could be improved. Specifically, I don't care for all of the if checks, but I haven't been able to find another way to work it. I also feel like this is inefficient code, however this specific code only runs once per each case, so efficiency isn't absolutely vital.
```
public void PostLatLong(string fileType, string fileNumber, List latLongList, DateTime? transactionDate = null, string commentText1 = null, string commentText2 = null, string username = null, string password = null)
{
string transactionCode = "LATLONGS";
List> splitList = new List>();
try
{
while (latLongList.Any())
{
splitList.Add(latLongList.Take(5).ToList());
latLongList = latLongList.Skip(5).ToList();
}
foreach (List list in splitList)
{
SetupCaseFileUpdateTransaction(transactionCode, fileType, fileNumber,
Solution
Separate the mechanics and the semantics of the code. Here's my very well used
Now that's the batching taken care of, we need to sort out the loop. What would we like it to look like?
Great! The main method is now much shorter and easier to reason about. However, we've just moved the problem elsewhere. So let's look at the
Note that I've assumed that skipping
Batch extension method on IEnumerable:public static class EnumerableExtensions
{
public static IEnumerable> Batch(this IEnumerable enumerable, int batchSize)
{
if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));
if (batchSize > BatchCore(this IEnumerable enumerable, int batchSize)
{
var c = 0;
var batch = new List();
foreach (var item in enumerable)
{
batch.Add(item);
if (++c % batchSize == 0)
{
yield return batch;
batch = new List();
}
}
if (batch.Count != 0)
{
yield return batch;
}
}
}Now that's the batching taken care of, we need to sort out the loop. What would we like it to look like?
foreach (var batch in latLongList.Batch(5))
{
SetupCaseFileUpdateTransaction(transactionCode, fileType, fileNumber, commentText1, commentText2, transactionDate, username, password);
SetWebServiceBatch(batch.ToList());
CallWebService();
}Great! The main method is now much shorter and easier to reason about. However, we've just moved the problem elsewhere. So let's look at the
SetWebServiceBatch piece:// Typed directly into CR so may not compile/be 100% right.
private void SetWebServiceBatch(List latLongs)
{
if(latLongs.Count > 5) throw new ArgumentException(nameof(latLongs));
for (var i = 0; i < latLongs.Count; i++)
{
if (i < 3)
{
SetCode(1 + i * 2, latLongs[i].Latitude);
SetCode(2 + i * 2, latLongs[i].Longitude);
}
else
{
SetMisc(1 + (i - 3) * 2, latLongs[i].Latitude);
SetMisc(2 + (i - 3) * 2, latLongs[i].Longitude);
}
}
}Note that I've assumed that skipping
SetCode(3 was a mistake and that you want to set code 1,2,3,4,5,6 and set misc 1,2,3,4. If that's not the case you'll need to slightly modify the for loop to take into account this discontinuity.Code Snippets
public static class EnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> enumerable, int batchSize)
{
if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));
if (batchSize <= 0) throw new ArgumentOutOfRangeException(nameof(batchSize));
return enumerable.BatchCore(batchSize);
}
private static IEnumerable<IEnumerable<T>> BatchCore<T>(this IEnumerable<T> enumerable, int batchSize)
{
var c = 0;
var batch = new List<T>();
foreach (var item in enumerable)
{
batch.Add(item);
if (++c % batchSize == 0)
{
yield return batch;
batch = new List<T>();
}
}
if (batch.Count != 0)
{
yield return batch;
}
}
}foreach (var batch in latLongList.Batch(5))
{
SetupCaseFileUpdateTransaction(transactionCode, fileType, fileNumber, commentText1, commentText2, transactionDate, username, password);
SetWebServiceBatch(batch.ToList());
CallWebService();
}// Typed directly into CR so may not compile/be 100% right.
private void SetWebServiceBatch(List<LatLongEntry> latLongs)
{
if(latLongs.Count > 5) throw new ArgumentException(nameof(latLongs));
for (var i = 0; i < latLongs.Count; i++)
{
if (i < 3)
{
SetCode(1 + i * 2, latLongs[i].Latitude);
SetCode(2 + i * 2, latLongs[i].Longitude);
}
else
{
SetMisc(1 + (i - 3) * 2, latLongs[i].Latitude);
SetMisc(2 + (i - 3) * 2, latLongs[i].Longitude);
}
}
}Context
StackExchange Code Review Q#145068, answer score: 6
Revisions (0)
No revisions yet.