I have a query which I get as:
var query = Data.Items
.Where(x => criteria.IsMatch(x))
.ToList<Item>();
This works fine.
However now I want to break up this list into x number of lists, for example 3. Each list will therefore contain 1/3 the amount of elements from query.
Can it be done using LINQ?
You can use PLINQ partitioners to break the results into separate enumerables.
var partitioner = Partitioner.Create<Item>(query);
var partitions = partitioner.GetPartitions(3);
You'll need to reference the System.Collections.Concurrent namespace. partitions will be a list of IEnumerable<Item> where each enumerable returns a portion of the query.
I think something like this could work, splitting the list into IGroupings.
const int numberOfGroups = 3;
var groups = query
.Select((item, i) => new { item, i })
.GroupBy(e => e.i % numberOfGroups);
You can use Skip and Take in a simple for to accomplish what you want
var groupSize = (int)Math.Ceiling(query.Count() / 3d);
var result = new List<List<Item>>();
for (var j = 0; j < 3; j++)
result.Add(query.Skip(j * groupSize).Take(groupSize).ToList());
If the order of the elements doesn't matter using an IGrouping as suggested by Daniel Imms is probably the most elegant way (add .Select(gr => gr.Select(e => e.item)) to get an IEnumerable<IEnumerable<T>>).
If however you want to preserve the order you need to know the total number of elements. Otherwise you wouldn't know when to start the next group. You can do this with LINQ but it requires two enumerations: one for counting and another for returning the data (as suggested by Esteban Elverdin).
If enumerating the query is expensive you can avoid the second enumeration by turning the query into a list and then use the GetRange method:
public static IEnumerable<List<T>> SplitList<T>(List<T> list, int numberOfRanges)
{
int sizeOfRanges = list.Count / numberOfRanges;
int remainder = list.Count % numberOfRanges;
int startIndex = 0;
for (int i = 0; i < numberOfRanges; i++)
{
int size = sizeOfRanges + (remainder > 0 ? 1 : 0);
yield return list.GetRange(startIndex, size);
if (remainder > 0)
{
remainder--;
}
startIndex += size;
}
}
static void Main()
{
List<int> list = Enumerable.Range(0, 10).ToList();
IEnumerable<List<int>> result = SplitList(list, 3);
foreach (List<int> values in result)
{
string s = string.Join(", ", values);
Console.WriteLine("{{ {0} }}", s);
}
}
The output is:
{ 0, 1, 2, 3 }
{ 4, 5, 6 }
{ 7, 8, 9 }
You can create an extension method:
public static IList<List<T>> GetChunks<T>(this IList<T> items, int numOfChunks)
{
if (items.Count < numOfChunks)
throw new ArgumentException("The number of elements is lower than the number of chunks");
int div = items.Count / numOfChunks;
int rem = items.Count % numOfChunks;
var listOfLists = new List<T>[numOfChunks];
for (int i = 0; i < numOfChunks; i++)
listOfLists[i] = new List<T>();
int currentGrp = 0;
int currRemainder = rem;
foreach (var el in items)
{
int currentElementsInGrp = listOfLists[currentGrp].Count;
if (currentElementsInGrp == div && currRemainder > 0)
{
currRemainder--;
}
else if (currentElementsInGrp >= div)
{
currentGrp++;
}
listOfLists[currentGrp].Add(el);
}
return listOfLists;
}
then use it like this :
var chunks = query.GetChunks(3);
N.B.
in case of number of elements not divisible by the number of groups, the first groups will be bigger. e.g. [0,1,2,3,4] --> [0,1] - [2,3] - [4]
Related
User can pass any number of list with same number of elements in it. Example- user has passed below 3 (could be dynamic with same number of elements in it) list -
hospitalId - H11, H12, H13...n
patientId - P11, P12, P13...n
statusId - S11, S13, S11...n
What is the efficient way of creating a set out of it and storing it as a string in below format? Need a c# code for it.
expected output -
"((H11,P11, S11), (H12, P12, S13), (H13, P13, S11))"
You should iterate through your list and append them index wise to prepare the result.
StringBuilder builder = new StringBuilder();
builder.Append("(");
for(var index = 0; index < n; index++)
{
builder.AppendFormat("({0}, {1}, {2})", hospitalId[index], patientId[index], statusId[index]);
}
builder.Append(")");
var result = builder.ToString();
If you have n number of List<T> items with the same length, a basic loop will do the trick. Here's a version as an extension method that will take any number of lists as an input:
public static IEnumerable<IEnumerable<T>> ZipMultiple<T>(this List<List<T>> source)
{
var counts = source.Select(s => s.Count).Distinct();
if (counts.Count() != 1)
{
throw new ArgumentException("Lists aren't the same length");
}
for (var i = 0; i < counts.First(); i++)
{
var item = new List<T>();
for (var j = 0; j < source.Count; j++)
{
item.Add(source[j][i]);
}
yield return item;
}
}
After that, it's pretty simple to convert the result into a string in another loop, or you can do it as a single liner:
var zipped = lists.ZipMultiple();
var output = $"({string.Join(", ", zipped.Select(x => $"({string.Join(",", x)})"))})";
I have an unknown number of ordered lists that I need to do paging on.
For example, the pages for these 3 lists should look like this when the page size is 6.
List1: 01,02,03,04,05,06,07,08,09,10
List2: 11,12,13,14,15
List3: 16,17,18,19,20,21,22,23,24,25,26,27,28
Result Pages:
Page1: 01,11,16,02,12,17
Page2: 03,13,18,04,14,19
Page3: 05,15,20,06,21,07
Page4: 22,08,23,09,24,10
page5: 25,26,27,28
What will be the most efficient way to get which items should I take from each list (start index and number of items) when given the page number?
Take in consideration that each list can have a few hundred thousand of items so iterating through all of them will not be efficient.
I can't say if it's the most efficient way or not, but here is an algorithm with O(M*Log2(M)) time complexity where M is the number of the lists. It works as follows. The input set is grouped and sorted in ascending order by the item Count, which is iterated until the effective start index fits into current range, skipping the previous ranges. This is possible because at every step we know that it is the minimum count, hence all the remaining lists have items in that range. Once we are done with that, we emit the page items from the remaining lists.
Here is the function:
static IEnumerable<T> GetPageItems<T>(List<List<T>> itemLists, int pageSize, int pageIndex)
{
int start = pageIndex * pageSize;
var counts = new int[itemLists.Count];
for (int i = 0; i < counts.Length; i++)
counts[i] = itemLists[i].Count;
Array.Sort(counts);
int listCount = counts.Length;
int itemIndex = 0;
for (int i = 0; i < counts.Length; i++)
{
int itemCount = counts[i];
if (itemIndex < itemCount)
{
int rangeLength = listCount * (itemCount - itemIndex);
if (start < rangeLength) break;
start -= rangeLength;
itemIndex = itemCount;
}
listCount--;
}
if (listCount > 0)
{
var listQueue = new List<T>[listCount];
listCount = 0;
foreach (var list in itemLists)
if (itemIndex < list.Count) listQueue[listCount++] = list;
itemIndex += start / listCount;
int listIndex = 0;
int skipCount = start % listCount;
int nextCount = 0;
int yieldCount = 0;
while (true)
{
var list = listQueue[listIndex];
if (skipCount > 0)
skipCount--;
else
{
yield return list[itemIndex];
if (++yieldCount >= pageSize) break;
}
if (itemIndex + 1 < list.Count)
{
if (nextCount != listIndex)
listQueue[nextCount] = list;
nextCount++;
}
if (++listIndex < listCount) continue;
if (nextCount == 0) break;
itemIndex++;
listIndex = 0;
listCount = nextCount;
nextCount = 0;
}
}
}
and test:
static void Main(string[] args)
{
var data = new List<List<int>>
{
new List<int> { 01, 02, 03, 04, 05, 06, 07, 08, 09, 10 },
new List<int> { 11, 12, 13, 14, 15 },
new List<int> { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 },
};
int totalCount = data.Sum(list => list.Count);
int pageSize = 6;
int pageCount = 1 + (totalCount - 1) / pageSize;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
Console.WriteLine("Page #{0}: {1}", pageIndex + 1, string.Join(", ", GetPageItems(data, pageSize, pageIndex)));
Console.ReadLine();
}
I think it could be done nicely in two steps:
Flatten your lists to a single list (ordered in the way you describe).
Take items from that list for the desired page.
To accomplish step 1, I'd do something like what was suggested here: Merging multiple lists
So, (assuming your page items are ints, as in your example), here's a nice method that finds exactly the ones you want:
static IEnumerable<int> GetPageItems(IEnumerable<List<int>> itemLists, int pageSize, int page)
{
var mergedOrderedItems = itemLists.SelectMany(x => x.Select((s, index) => new { s, index }))
.GroupBy(x => x.index)
.SelectMany(x => x.Select(y => y.s));
// assuming that the first page is page 1, not page 0:
var startingIndex = pageSize * (page - 1);
var pageItems = mergedOrderedItems.Skip(startingIndex)
.Take(pageSize);
return pageItems;
}
Note - you don't have to worry about passing in a page# that exceeds the total number of pages that could exist given the total item count... Thanks to the magic of Linq, this method will simply return an empty IEnumerable. Likewise, if Take(pageSize) results in less than "pageSize" items, it simply returns the items that it did find.
I'll submit another implementation, based on Bear.S' feedback on my first answer. This one's pretty low-level and very performant. There are two major parts to it:
Figure out which item should appear first on the page (specifically what is the index of the list that contains it, and what is the item's index within that list).
Take items from all lists, in the correct order, as needed (until we have all that we need or run out of items).
This implementation doesn't iterate the individual lists during step 1. It does use List.Count property, but that is an O(1) operation.
Since we're going for performance here, the code isn't necessarily as self-descriptive as I'd like, so I put in some comments to help explain the logic:
static IEnumerable<T> GetPageItems<T>(List<List<T>> itemLists, int pageSize, int page)
{
if (page < 1)
{
return new List<T>();
}
// a simple copy so that we don't change the original (the individual Lists inside are untouched):
var lists = itemLists.ToList();
// Let's find the starting indexes for the first item on this page:
var currItemIndex = 0;
var currListIndex = 0;
var itemsToSkipCount = pageSize * (page - 1); // <-- assuming that the first page is page 1, not page 0
// I'll just break out of this loop manually, because I think this configuration actually makes
// the logic below a little easier to understand. Feel free to change it however you see fit :)
while (true)
{
var listsCount = lists.Count;
if (listsCount == 0)
{
return new List<T>();
}
// Let's consider a horizontal section of items taken evenly from all lists (based on the length of
// the shortest list). We don't need to iterate any items in the lists; Rather, we'll just count
// the total number of items we could get from this horizontal portion, and set our indexes accordingly...
var shortestListCount = lists.Min(x => x.Count);
var itemsWeAreConsideringCount = listsCount * (shortestListCount - currItemIndex);
// Does this horizontal section contain at least as many items as we must skip?
if (itemsWeAreConsideringCount >= itemsToSkipCount)
{ // Yes: So mathematically find the indexes of the first page item, and we're done.
currItemIndex += itemsToSkipCount / listsCount;
currListIndex = itemsToSkipCount % listsCount;
break;
}
else
{ // No: So we need to keep going. Let's increase currItemIndex to the end of this horizontal
// section, remove the shortest list(s), and the loop will continue with the remaining lists:
currItemIndex = shortestListCount;
lists.RemoveAll(x => x.Count == shortestListCount);
itemsToSkipCount -= itemsWeAreConsideringCount;
}
}
// Ok, we've got our starting indexes, and the remaining lists that still have items in the index range.
// Let's get our items from those lists:
var pageItems = new List<T>();
var largestListCount = lists.Max(x => x.Count);
// Loop until we have enough items to fill the page, or we run out of items:
while (pageItems.Count < pageSize && currItemIndex < largestListCount)
{
// Taking from one list at a time:
var currList = lists[currListIndex];
// If the list has an element at this index, get it:
if (currItemIndex < currList.Count)
{
pageItems.Add(currList[currItemIndex]);
}
// else... this list has no more elements.
// We could throw away this list, since it's pointless to iterate over it any more, but that might
// change the indices of other lists... for simplicity, I'm just gonna let it be... since the above
// logic simply ignores an empty list.
currListIndex++;
if (currListIndex == lists.Count)
{
currListIndex = 0;
currItemIndex++;
}
}
return pageItems;
}
Here's some test code, using three lists. I can grab 6 items off of page 1,000,000 in just a few milliseconds :)
var list1 = Enumerable.Range(0, 10000000).ToList();
var list2 = Enumerable.Range(10000000, 10000000).ToList();
var list3 = Enumerable.Range(20000000, 10000000).ToList();
var lists = new List<List<int>> { list1, list2, list3 };
var timer = new Stopwatch();
timer.Start();
var items = GetPageItems(lists, 6, 1000000).ToList();
var count = items.Count();
timer.Stop();
For a given a space separated list of numbers, what is the most effecient way of counting the total pairs of numbers which have a difference of N.
e.g. command line in put would be:
5 2
where 5 is the count of numbers to follow and 2 is the difference required
1 5 3 4 2
the 5 numbers to be considered
Output should be
3
because (5,3), (4,2) and (3,1) all have a diff of 2
I can get this algorithm to work, but is there a more efficient way of doing this if you have large sets of numbers to work with? I have incluced three comparison options and the second one should be better than the third but is there something I'm forgetting which could make it much quicker?
private static void Difference()
{
string[] firstInput = SplitInput(Console.ReadLine());
int numberOfNumbers = int.Parse(firstInput[0]);
int diffOfNumbers = int.Parse(firstInput[1]);
string[] secondInput = SplitInput(Console.ReadLine());
List<int> numbers = secondInput.Select(x => Int32.Parse(x)).ToList();
int possibleCombinations = 0;
// Option 1
foreach (int firstNumber in numbers)
{
List<int> compareTo = numbers.GetRange(numbers.IndexOf(firstNumber) + 1, numbers.Count - numbers.IndexOf(firstNumber) - 1);
foreach (int secondNumber in compareTo)
{
int diff = firstNumber - secondNumber;
if (Math.Abs(diff) == diffOfNumbers)
{
possibleCombinations++;
}
}
}
// Option 2
foreach (int firstNumber in numbers)
{
if (numbers.Contains(firstNumber + diffOfNumbers))
{
possibleCombinations++;
}
}
// Option 3
foreach (int firstNumber in numbers)
{
foreach (int secondNumber in numbers)
{
int diff = firstNumber - secondNumber;
if(Math.Abs(diff) == diffOfNumbers)
{
possibleOptions++;
}
}
}
Console.WriteLine(string.Format("Possible number of options are: {0}", possibleCombinations));
Console.ReadLine();
}
private static string[] SplitInput(string input)
{
return input.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
If duplicate numbers are not allowed or to be ignored (only count unique pairs), you could use a HashSet<int>:
HashSet<int> myHashSet = ...
int difference = ...
int count;
foreach (int number in myHashSet)
{
int counterpart = number - difference;
if (myHashSet.Contains(counterpart))
{
count++;
}
}
Given the constraints of the problem, where N is the "count of numbers to follow" [1..N], and M is the difference (N=5 and M=2 in the example), why not just return N - M ?
This is done easily with LINQ, allowing for duplicates:
var dict = numbers.GroupBy(n => n).ToDictionary(g => g.Key, g => g.Count());
return dict.Keys.Where(n => dict.ContainsKey(difference-n)).Select(n => dict[difference - n]).Sum();
In the first line we create a dictionary where the keys are the distinct numbers in the input list (numbers) and the values are how many times they appear.
In the second, for each distinct number in the list (equivalent to the keys of the dictioanry) we look to see if the dictionary contains a key for the target number. If so, we add the number of times that target number appeared, which we previously stored as the value for that key. If not we add 0. Finally we sum it all up.
Note in theory this could cause arithmetic overflows if there's no bound other than Int.MinValue and Int.MaxValue on the items in the list. To get around this we need to do a "safe" check, which first makes sure that the difference won't be out of bounds before we try to calculate it. That might look like:
int SafeGetCount(int difference, int number, Dictionary<int,int> dict)
{
if(difference < 0 && number < 0 && int.MinValue - difference > number)
return 0;
if(difference > 0 && number > 0 && int.MaxValue - difference < number)
return 0;
return dict.ContainsKey(difference-number) ? dict[difference - number] : 0;
}
Update
There are a couple of things note entirely clear from your question, like whether you actually want to count duplicate pairs multiple times, and does swapping the numbers count as two different pairs. e.g. if (1,4) is a pair, is (4,1)? My answer above assumes that the answer to both of those questions is yes.
If you don't want to count duplicate pairs multiple times, then go with the HashSet solution from other answers. If you do want to count duplicate pairs but don't want to count twice by swapping the values in the pair, you have to get slightly more complex. E.g.:
var dict = numbers.GroupBy(n => n).ToDictionary(g => g.Key, g => g.Count());
var sum = dict.Keys.Where(n => n*2 != difference)
.Where(n => dict.ContainsKey(difference-n))
.Select(n => dict[difference - n]).Sum()/2;
if(n%2 == 0)
{
sum += dict.ContainsKey(n/2) ? dict[n/2] : 0
}
return sum;
how about sorting the list then iterating over it.
int PairsWithMatchingDifferenceCount(
IEnumerable<int> source,
int difference)
{
var ordered = source.OrderBy(i => i).ToList();
var count = ordered.Count;
var result = 0;
for (var i = 0; i < count - 1; i++)
{
for (var j = i + 1; j < count; j++)
{
var d = Math.Abs(ordered[j] - ordered[i]);
if (d == difference)
{
result++;
}
else if (d > difference)
{
break;
}
}
}
return result;
}
so, as per the example you would call it like this,
PairsWithMatchingDifferenceCount(Enumerable.Range(1, 5), 2);
but, if the sequence generation is a simple as the question suggests why not just.
var m = 5;
var n = 2;
var result = Enumerable.Range(n + 1, m - n)
.Select(x => Tuple.Create(x, x - n)).Count();
or indeed,
var result = m - n;
There is a list called cardReaderHistory . That contains some time records in ordered fashion as follows,
InTime1
OutTime1
InTime2
OutTime2
InTime3
OutTime3
InTime4
OutTime4.....furthermore..
What I need is Calculate Working time by (OutTime1 - Intime1) + (OutTime1 - Intime1).....
double
How could I do this in C#...????
double hr = ((outTime1 - inTime1)+(OutTime2 - Intime2)+...);
Thank you..
Poor Beginner
You can filter the input sequence based on the index, and then zip the two sequences:
var inTimes = source.Where((x, index) => index % 2 == 0);
var outTimes = source.Where((x, index) => index % 2 == 1);
var result = inTimes.Zip(outTimes, (inTime, outTime) => outTime - inTime).Sum();
If you don't need the intermediary values, you can also do this:
var result = source.Select((x, index) => (index % 2 == 0) ? -x : x).Sum();
Assuming your cardReaderHistory list is a list of doubles:
List<double> cardReaderHistory = new List<double>(); //fill it somewhere
double result;
for(int i = 0; i < cardReaderHistory.Count(); i++)
{
if(i%2==0) //If it is equal
result -= cardReaderHistory[i];
else //its unequal
result += cardReaderHistory[i];
}
You just loop over your values and add or subtract based on if its even or not.
Something like this...
List<double> l = new List<double> { 1, 2, 3, 4, 5, 6, 7, 8 };
double hr = 0;
for (int i = 0; i < l.Count; i++)
{
hr += i%2 == 0 ? -l[i] : l[i];
}
It seems plausible that the list contains datetimes and not hours. Currently none of the other answers handles that. Here's my take.
var cardReaderHistory = new List<DateTime> {DateTime.Now.AddMinutes(-120), DateTime.Now.AddMinutes(-100), DateTime.Now.AddMinutes(-20), DateTime.Now};
var hours = cardReaderHistory.Split(2).Select(h => (h.Last() - h.First()).TotalHours).Sum();
Split is an extension method
static class ExtentionsMethods
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> seq, int size)
{
while (seq.Any())
{
yield return seq.Take(size);
seq = seq.Skip(size);
}
}
}
I know how to do this in an ugly way, but am wondering if there is a more elegant and succinct method.
I have a string array of e-mail addresses. Assume the string array is of arbitrary length -- it could have a few items or it could have a great many items. I want to build another string consisting of say, 50 email addresses from the string array, until the end of the array, and invoke a send operation after each 50, using the string of 50 addresses in the Send() method.
The question more generally is what's the cleanest/clearest way to do this kind of thing. I have a solution that's a legacy of my VBScript learnings, but I'm betting there's a better way in C#.
You want elegant and succinct, I'll give you elegant and succinct:
var fifties = from index in Enumerable.Range(0, addresses.Length)
group addresses[index] by index/50;
foreach(var fifty in fifties)
Send(string.Join(";", fifty.ToArray());
Why mess around with all that awful looping code when you don't have to? You want to group things by fifties, then group them by fifties.
That's what the group operator is for!
UPDATE: commenter MoreCoffee asks how this works. Let's suppose we wanted to group by threes, because that's easier to type.
var threes = from index in Enumerable.Range(0, addresses.Length)
group addresses[index] by index/3;
Let's suppose that there are nine addresses, indexed zero through eight
What does this query mean?
The Enumerable.Range is a range of nine numbers starting at zero, so 0, 1, 2, 3, 4, 5, 6, 7, 8.
Range variable index takes on each of these values in turn.
We then go over each corresponding addresses[index] and assign it to a group.
What group do we assign it to? To group index/3. Integer arithmetic rounds towards zero in C#, so indexes 0, 1 and 2 become 0 when divided by 3. Indexes 3, 4, 5 become 1 when divided by 3. Indexes 6, 7, 8 become 2.
So we assign addresses[0], addresses[1] and addresses[2] to group 0, addresses[3], addresses[4] and addresses[5] to group 1, and so on.
The result of the query is a sequence of three groups, and each group is a sequence of three items.
Does that make sense?
Remember also that the result of the query expression is a query which represents this operation. It does not perform the operation until the foreach loop executes.
Seems similar to this question: Split a collection into n parts with LINQ?
A modified version of Hasan Khan's answer there should do the trick:
public static IEnumerable<IEnumerable<T>> Chunk<T>(
this IEnumerable<T> list, int chunkSize)
{
int i = 0;
var chunks = from name in list
group name by i++ / chunkSize into part
select part.AsEnumerable();
return chunks;
}
Usage example:
var addresses = new[] { "a#example.com", "b#example.org", ...... };
foreach (var chunk in Chunk(addresses, 50))
{
SendEmail(chunk.ToArray(), "Buy V14gr4");
}
It sounds like the input consists of separate email address strings in a large array, not several email address in one string, right? And in the output, each batch is a single combined string.
string[] allAddresses = GetLongArrayOfAddresses();
const int batchSize = 50;
for (int n = 0; n < allAddresses.Length; n += batchSize)
{
string batch = string.Join(";", allAddresses, n,
Math.Min(batchSize, allAddresses.Length - n));
// use batch somehow
}
Assuming you are using .NET 3.5 and C# 3, something like this should work nicely:
string[] s = new string[] {"1", "2", "3", "4"....};
for (int i = 0; i < s.Count(); i = i + 50)
{
string s = string.Join(";", s.Skip(i).Take(50).ToArray());
DoSomething(s);
}
I would just loop through the array and using StringBuilder to create the list (I'm assuming it's separated by ; like you would for email). Just send when you hit mod 50 or the end.
void Foo(string[] addresses)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < addresses.Length; i++)
{
sb.Append(addresses[i]);
if ((i + 1) % 50 == 0 || i == addresses.Length - 1)
{
Send(sb.ToString());
sb = new StringBuilder();
}
else
{
sb.Append("; ");
}
}
}
void Send(string addresses)
{
}
I think we need to have a little bit more context on what exactly this list looks like to give a definitive answer. For now I'm assuming that it's a semicolon delimeted list of email addresses. If so you can do the following to get a chunked up list.
public IEnumerable<string> DivideEmailList(string list) {
var last = 0;
var cur = list.IndexOf(';');
while ( cur >= 0 ) {
yield return list.SubString(last, cur-last);
last = cur + 1;
cur = list.IndexOf(';', last);
}
}
public IEnumerable<List<string>> ChunkEmails(string list) {
using ( var e = DivideEmailList(list).GetEnumerator() ) {
var list = new List<string>();
while ( e.MoveNext() ) {
list.Add(e.Current);
if ( list.Count == 50 ) {
yield return list;
list = new List<string>();
}
}
if ( list.Count != 0 ) {
yield return list;
}
}
}
I think this is simple and fast enough.The example below divides the long sentence into 15 parts,but you can pass batch size as parameter to make it dynamic.Here I simply divide using "/n".
private static string Concatenated(string longsentence)
{
const int batchSize = 15;
string concatanated = "";
int chanks = longsentence.Length / batchSize;
int currentIndex = 0;
while (chanks > 0)
{
var sub = longsentence.Substring(currentIndex, batchSize);
concatanated += sub + "/n";
chanks -= 1;
currentIndex += batchSize;
}
if (currentIndex < longsentence.Length)
{
int start = currentIndex;
var finalsub = longsentence.Substring(start);
concatanated += finalsub;
}
return concatanated;
}
This show result of split operation.
var parts = Concatenated(longsentence).Split(new string[] { "/n" }, StringSplitOptions.None);
Extensions methods based on Eric's answer:
public static IEnumerable<IEnumerable<T>> SplitIntoChunks<T>(this T[] source, int chunkSize)
{
var chunks = from index in Enumerable.Range(0, source.Length)
group source[index] by index / chunkSize;
return chunks;
}
public static T[][] SplitIntoArrayChunks<T>(this T[] source, int chunkSize)
{
var chunks = from index in Enumerable.Range(0, source.Length)
group source[index] by index / chunkSize;
return chunks.Select(e => e.ToArray()).ToArray();
}