Create a sliding window from a list - c#

I need to iterate over a list while getting the next and previous Items. I'm currently doing it like so:
var items = collection.ToList();
for (int index = 0; index < items.Count; index++)
{
var prevItem = index == 0 ? null : items[index - 1];
var currentItem = items[index];
var nextItem = (index + 1) == items.Count ? null : items[index + 1];
// do work
}
This work but it is not as nice and readable as I would like. Is there a more readable way of creating a sliding window? which doesn't involve all of the ugly ternary checks. I feel like there is a more friendly way using select, skip, take, and default if empty

This sort of problem is best solved using generators if you want it to be reusable IMHO.
public static IEnumerable<(T PrevItem, T CurrentItem, T NextItem)>
SlidingWindow<T>(this IEnumerable<T> source, T emptyValue = default)
{
using (var iter = source.GetEnumerator())
{
if (!iter.MoveNext())
yield break;
var prevItem = emptyValue;
var currentItem = iter.Current;
while (iter.MoveNext())
{
var nextItem = iter.Current;
yield return (prevItem, currentItem, nextItem);
prevItem = currentItem;
currentItem = nextItem;
}
yield return (prevItem, currentItem, emptyValue);
}
}
Then use it:
foreach (var (prevItem, currentItem, nextItem) in collection.SlidingWindow())
{
// do stuff with prevItem, currentItem, nextItem
}

Generic window size:
public IEnumerable<IEnumerable<T>> CreateSlidingWindow<T>(IEnumerable<T> input, int windowSize)
{
if (input == null || input.Count() < windowSize)
{
return new List<IEnumerable<T>>();
}
var first = new[] { input.Take(windowSize) };
var rest = CreateSlidingWindow(input.Skip(1), windowSize);
return first.Union(rest);
}

One way to make it more nicer is to transform your collection in a List of objects which contain the item, prevItem and nextItem:
static void Main(string[] args)
{
var collection = new List<string> { "A", "B", "C", "D", "E" };
var items = collection.Select((item, index) => new
{
Item = item,
PreVItem = index > 0 ? collection[index - 1] : null,
NextItem = index < collection.Count-1 ? collection[index + 1] : null
});
foreach (var item in items)
{
Console.WriteLine($"{item.PreVItem} \t {item.Item} \t {item.NextItem}");
}
Console.ReadLine();
}

Created the following solution that is using an enumerator.
public static IEnumerable<IList<T>> SlidingWindowValues<T>(this IEnumerable<T> source, int windowSize)
{
var windows = Enumerable.Range(0, windowSize)
.Select(_ => new List<T>())
.ToList();
int i = 0;
using (var iter = source.GetEnumerator())
{
while (iter.MoveNext())
{
var c = Math.Min(i + 1, windowSize);
for (var j = 0; j < c; j++)
{
windows[(i - j) % windowSize].Add(iter.Current);
}
if (i >= windowSize - 1)
{
var previous = (i + 1) % windowSize;
yield return windows[previous];
windows[previous] = new List<T>();
}
i++;
}
}
}

Related

What is wrong with this approach in comparing two rows in mastermind?

Created a function with two ints and two lists.
This approach still double counts.
Example: row 1: 1,2,2,3
row 2: 1,4,2,5
It shows wrongPosition: 1 and samePlace: 2 instead of samePlace: 2 and wrongPosition:0.
Also, do you have any suggestion as to how to optimise this code?
private void CompareRows()
{
if (_resaultPawns.Length != _actualRowPawns.Length)
{
Debug.LogError("Arrays have different length");
return;
}
int swrongPosition = 0;
int samePlace = 0;
List<int> alreadyCounted = new List<int>();
List<int> alreadyCountedColors = new List<int>();
// Check for same position with same color
for (int i = 0; i < _resaultPawns.Length; i++)
{
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[i].GetComponent<MeshRenderer>().material.color)
{
if (!alreadyCounted.Contains(i))
{
samePlace++;
alreadyCounted.Add(i);
}
}
}
// Check for wrong position with same color
for (int i = 0; i < _resaultPawns.Length; i++)
{
for (int j = 0; j < _actualRowPawns.Length; j++)
{
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[j].GetComponent<MeshRenderer>().material.color && !alreadyCounted.Contains(i) && !alreadyCountedColors.Contains(j))
{
if (!alreadyCounted.Contains(i) && !alreadyCountedColors.Contains(j))
{
swrongPosition++;
alreadyCountedColors.Add(j);
alreadyCounted.Add(i);
}
}
}
}
}
See https://dotnetfiddle.net/91GVVe
I looked at this from optimizations/maintainability first and refactoring the code a bit seems to already have solved the issue as well ^^
var samePlace = new List<int>(4);
var wrongPosition = new List<int>(4);
for (var i = 0; i < _resaultPawns.Length; i++)
{
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[i].GetComponent<MeshRenderer>().material.color)
{
// Check for 'alreadyCounted.Contains' is redundan at this stage
// No additional counter needed - simply add items and use the list Count later on
samePlace.Add(i);
}
}
for (var i = 0; i < _resaultPawns.Length; i++)
{
// Can directly skip as already counted
if (samePlace.Contains(i)) continue;
for (var j = 0; j < _actualRowPawns.Length; j++)
{
// can directly skip as would have been upper loop match
// slightly chaper though than using the 'samePlace.Contains(j)' below
if (i == j) continue;
// skip as well to not re-use already exact matches
if (samePlace.Contains(j)) continue;
// skip to not use twice
if (wrongPosition.Contains(j)) continue;
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[j].GetComponent<MeshRenderer>().material.color)
{
wrongPosition.Add(j);
break;
}
}
}
var wrongPositionCount = wrongPosition.Count;
var samePlaceCount = samePlace.Count;
I guess the main bit I added was
if (samePlace.Contains(j)) continue;
=> you also want to ignore if a j index was already seen before by the first loop as it is already an exact match.
In your example
1,2,2,3
1,4,2,5
even though the index 3 (=value 2) was already counted as an exact match it was used again as an alternative position for index 1, a case you didn't check for as it is neither in your alreadyCounted nor in alreadyCountedColour
It seems pretty simple to just do this:
var hidden = new[] { 1, 2, 2, 3 };
var first = new[] { 1, 4, 2, 5 };
int sp = 0;
int wp = 0;
for (int i = 0; i < 4; i++)
{
if (first[i] == hidden[i])
sp++;
else
for (int j = 0; j < 4; j++)
if (first[i] == hidden[j] && first[j] != hidden[j])
{
wp++;
break;
}
}
Console.WriteLine($"Same Place: {sp}, Wrong Place: {wp}");
That outputs Same Place: 2, Wrong Place: 0.
It looks like a more complicated approach is needed to cover all cases:
var hidden = new[] { 1, 2, 2, 3 };
var first = new[] { 1, 4, 2, 5 };
var paired =
first
.Zip(hidden, (f, h) => (first: f, hidden: h))
.ToLookup(x => x.first == x.hidden);
int sp = paired[true].Count();
var unmatched_hidden = paired[false].ToLookup(x => x.first);
var unmatched_first = paired[false].ToLookup(x => x.hidden);
IEnumerable<int> query =
from uf in unmatched_first
from x in unmatched_first[uf.Key].Zip(unmatched_hidden[uf.Key])
select uf.Key;
int wp = query.Count();
Console.WriteLine($"Same Place: {sp}, Wrong Place: {wp}");
That seems to work now.
I really like InBetween's answer. I did a bit of refactoring to make it use standard operators.
private static (int SamePlace, int WrongPlace) CompareRows<T>
(IEnumerable<T> guess, IEnumerable<T> master) where T : IEquatable<T>
{
var guessWithIndex = guess.Select((g, i) => (value: g, i)).ToArray();
var masterWithIndex = master.Select((m, i) => (value: m, i)).ToArray();
var samePlaces = masterWithIndex.Intersect(guessWithIndex);
var guessNotSamePlaces = guessWithIndex.Except(samePlaces).Select(x => x.value);
var masterNotSamePlaces = masterWithIndex.Except(samePlaces).Select(x => x.value);
var wrongPlaces = guessNotSamePlaces.Intersect(masterNotSamePlaces);
return (samePlaces.Count(), wrongPlaces.Count());
}
Linq solution just for the fun of it... instructive (allthough overkill) as it shows use of generics, tuples and how you can tweak built-in linq operations with custom comparers.
private static (int Guessed, int Misplaced) CompareRows<T>
(IEnumerable<T> guesses, IEnumerable<T> master)
where T: IEquatable<T>
{
var guessesWithIndex = guesses.Select((g, i) => (g, i));
var masterWithIndex = master.Select((m, i) => (m, i));
var guessed = masterWithIndex.Intersect(guessesWithIndex);
var potentiallyMisplacedGuesses = guessesWithIndex.Except(guessed);
var masterNotGuessed = masterWithIndex.Except(guessed);
var misplaced = potentiallyMisplacedGuesses.Intersect(
masterNotGuessed, new OnlyFirstValueComparer<T, int>());
return (guessed.Count(), misplaced.Count());
}
class OnlyFirstValueComparer<T, K> : IEqualityComparer<(T, K)> where T: IEquatable<T>
{
public bool Equals((T, K) x, (T, K) y)
{
return x.Item1.Equals(y.Item1);
}
public int GetHashCode((T, K) obj)
{
return obj.Item1.GetHashCode();
}
}

Spliting a int array on a specific element

so I was solving some unrelated coding challenge and I needed to split an int array based on an element.
similar to how String.Split works.
for example:
var arr1 = new int [] { 3, 3, 0, 2, 4, 3, 2 };
var arr2 = new int [] { 8, 8, 5, 7, 9, 8, 7, 4, 8 };
var results1 = Split(arr1, 0);
foreach (var arr in results1)
Console.WriteLine(string.Join(",", arr));
var results2 = Split(arr2, 8);
foreach (var arr in results2)
Console.WriteLine(string.Join(",", arr));
the output here is:
3,3
2,4,3,2
5,7,9
7,4
I was surprised, I could not find any answers for this, String.Split kept coming up, but no integer!
so I wrote this:
public int[][] Split(int[] arr, int element)
{
List<int[]> arrays = new List<int[]>();
int skip = 0;
int take = 0;
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == element && take != 0)
{
arrays.Add(arr.Skip(skip).Take(take).ToArray());
skip = i + 1;
take = 0;
continue;
}
if (arr[i] != element)
take++;
if (take == 0)
skip = i + 1;
if (arr.Length - 1 == i && take != 0)
arrays.Add(arr.Skip(skip).Take(take).ToArray());
}
return arrays.ToArray();
}
this works (I think), but it is very messy and I don't like it
Any other solutions?
In general case, you can implement it (String.Split like routine but for IEnumerable<T>) as follows:
public static IEnumerable<T[]> Split<T>(IEnumerable<T> source,
T delimiter,
StringSplitOptions options = StringSplitOptions.None,
IEqualityComparer<T> comparer = null) {
if (null == source)
yield break; // Or throw new ArgumentNullException(nameof(source));
if (null == comparer)
comparer = EqualityComparer<T>.Default;
List<T> current = new List<T>();
foreach (T item in source) {
if (comparer.Equals(item, delimiter)) {
if (current.Count > 0 || !options.HasFlag(StringSplitOptions.RemoveEmptyEntries))
yield return current.ToArray();
current.Clear();
}
else
current.Add(item);
}
if (current.Count > 0 || !options.HasFlag(StringSplitOptions.RemoveEmptyEntries))
yield return current.ToArray();
}
Then
var arr1 = new int [] { 3, 3, 0, 2, 4, 3, 2 };
var results1 = Split(arr1, 0);
foreach (var arr in results1)
Console.WriteLine(string.Join(", ", arr));
public static IEnumerable<List<TValue>> SplitBy<TValue>(List<TValue> source, TValue by)
{
int start = 0;
int count = 0;
foreach (TValue item in source)
{
if (item.Equals(by))
{
List<TValue> part = source.GetRange(start, count);
start = start + count + 1;
count = 0;
if (part.Any())
{
yield return part;
}
}
else
{
count++;
}
}
List<TValue> rest = source.GetRange(start, count);
yield return rest;
}
https://dotnetfiddle.net/Widget/Preview?url=/Widget/7ORu8p

Can't track down cause of "Index was out of range" exception

So, i have this frankenstein:
var unsorted = new List<(Hand, List<Card>)>();
And when I try to add smt like this:
unsorted.Add((hand, _tempList));
I receive and error
Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
How to properly initialize this List if I do not know in advance how many items will be stored?
at the request of comments:
_tempList = IsStraightFlush(theValueHand);
if (_tempList.Count == 5)
{
unsorted.Add((hand, _tempList));
continue;
}
A function from where i add a List to the Tuple
private List<Card> IsStraightFlush(List<Card> hList)
{
var st = 0;
_tempList.Clear();
foreach (var t in hList)
{
...
}
if (_tempList.Count < 5)
{
return new List<Card>();
}
hList = _tempList.ToList();
_tempList.Clear();
for (var i = 0; i < hList.Count - 2; i++)
{
...
if (st == 4)
{
_tempList.Add(hList[i + 1]);
return _tempList;
}
}
st = 0;
_tempList.Clear();
if (hList[0].Value == 13) //Ace through 5
for (int i = hList.Count - 1, j = 0; i > 0; i--, j++)
{
...
if (st == 4)
{
_tempList.Add(hList.First());
return _tempList.OrderBy(x => x.Value).ToList();
}
}
return new List<Card>();
}
And when i use it
resultHands.AddRange(SortStraightFlushes(unsorted));
...
private List<(int, Hand)> SortStraightFlushes(List<(Hand, List<Card>)> hList)
{
var sortedHList = hList.OrderBy(x => x.Item2[0].Value).ToList();
List<(int, Hand)> output = new List<(int, Hand)>();
for (int i = 0; i < sortedHList.Count; i++)
{
if(i != sortedHList.Count - 1)
if (sortedHList[i].Item2[0].Value == sortedHList[i + 1].Item2[0].Value)
output.Add((1, sortedHList[i].Item1));
else
output.Add((0, sortedHList[i].Item1));
else
output.Add((0, sortedHList[i].Item1));
}
return output;
}
I don't know how beautiful and accurate it is, but it all worked out the way it did:
unsorted.Add((hand, new List<Card>().Concat(_tempList).ToList()));

c# permutation without repetition when order is important [duplicate]

I have a list of Offers, from which I want to create "chains" (e.g. permutations) with limited chain lengths.
I've gotten as far as creating the permutations using the Kw.Combinatorics project.
However, the default behavior creates permutations in the length of the list count. I'm not sure how to limit the chain lengths to 'n'.
Here's my current code:
private static List<List<Offers>> GetPerms(List<Offers> list, int chainLength)
{
List<List<Offers>> response = new List<List<Offers>>();
foreach (var row in new Permutation(list.Count).GetRows())
{
List<Offers> innerList = new List<Offers>();
foreach (var mix in Permutation.Permute(row, list))
{
innerList.Add(mix);
}
response.Add(innerList);
innerList = new List<Offers>();
}
return response;
}
Implemented by:
List<List<AdServer.Offers>> lst = GetPerms(offers, 2);
I'm not locked in KWCombinatorics if someone has a better solution to offer.
Here's another implementation which I think should be faster than the accepted answer (and it's definitely less code).
public static IEnumerable<IEnumerable<T>> GetVariationsWithoutDuplicates<T>(IList<T> items, int length)
{
if (length == 0 || !items.Any()) return new List<List<T>> { new List<T>() };
return from item in items.Distinct()
from permutation in GetVariationsWithoutDuplicates(items.Where(i => !EqualityComparer<T>.Default.Equals(i, item)).ToList(), length - 1)
select Prepend(item, permutation);
}
public static IEnumerable<IEnumerable<T>> GetVariations<T>(IList<T> items, int length)
{
if (length == 0 || !items.Any()) return new List<List<T>> { new List<T>() };
return from item in items
from permutation in GetVariations(Remove(item, items).ToList(), length - 1)
select Prepend(item, permutation);
}
public static IEnumerable<T> Prepend<T>(T first, IEnumerable<T> rest)
{
yield return first;
foreach (var item in rest) yield return item;
}
public static IEnumerable<T> Remove<T>(T item, IEnumerable<T> from)
{
var isRemoved = false;
foreach (var i in from)
{
if (!EqualityComparer<T>.Default.Equals(item, i) || isRemoved) yield return i;
else isRemoved = true;
}
}
On my 3.1 GHz Core 2 Duo, I tested with this:
public static void Test(Func<IList<int>, int, IEnumerable<IEnumerable<int>>> getVariations)
{
var max = 11;
var timer = System.Diagnostics.Stopwatch.StartNew();
for (int i = 1; i < max; ++i)
for (int j = 1; j < i; ++j)
getVariations(MakeList(i), j).Count();
timer.Stop();
Console.WriteLine("{0,40}{1} ms", getVariations.Method.Name, timer.ElapsedMilliseconds);
}
// Make a list that repeats to guarantee we have duplicates
public static IList<int> MakeList(int size)
{
return Enumerable.Range(0, size/2).Concat(Enumerable.Range(0, size - size/2)).ToList();
}
Unoptimized
GetVariations 11894 ms
GetVariationsWithoutDuplicates 9 ms
OtherAnswerGetVariations 22485 ms
OtherAnswerGetVariationsWithDuplicates 243415 ms
With compiler optimizations
GetVariations 9667 ms
GetVariationsWithoutDuplicates 8 ms
OtherAnswerGetVariations 19739 ms
OtherAnswerGetVariationsWithDuplicates 228802 ms
You're not looking for a permutation, but for a variation. Here is a possible algorithm. I prefer iterator methods for functions that can potentially return very many elements. This way, the caller can decide if he really needs all elements:
IEnumerable<IList<T>> GetVariations<T>(IList<T> offers, int length)
{
var startIndices = new int[length];
var variationElements = new HashSet<T>(); //for duplicate detection
while (startIndices[0] < offers.Count)
{
var variation = new List<T>(length);
var valid = true;
for (int i = 0; i < length; ++i)
{
var element = offers[startIndices[i]];
if (variationElements.Contains(element))
{
valid = false;
break;
}
variation.Add(element);
variationElements.Add(element);
}
if (valid)
yield return variation;
//Count up the indices
startIndices[length - 1]++;
for (int i = length - 1; i > 0; --i)
{
if (startIndices[i] >= offers.Count)
{
startIndices[i] = 0;
startIndices[i - 1]++;
}
else
break;
}
variationElements.Clear();
}
}
The idea for this algorithm is to use a number in offers.Count base. For three offers, all digits are in the range 0-2. We then basically increment this number step by step and return the offers that reside at the specified indices. If you want to allow duplicates, you can remove the check and the HashSet<T>.
Update
Here is an optimized variant that does the duplicate check on the index level. In my tests it is a lot faster than the previous variant:
IEnumerable<IList<T>> GetVariations<T>(IList<T> offers, int length)
{
var startIndices = new int[length];
for (int i = 0; i < length; ++i)
startIndices[i] = i;
var indices = new HashSet<int>(); // for duplicate check
while (startIndices[0] < offers.Count)
{
var variation = new List<T>(length);
for (int i = 0; i < length; ++i)
{
variation.Add(offers[startIndices[i]]);
}
yield return variation;
//Count up the indices
AddOne(startIndices, length - 1, offers.Count - 1);
//duplicate check
var check = true;
while (check)
{
indices.Clear();
for (int i = 0; i <= length; ++i)
{
if (i == length)
{
check = false;
break;
}
if (indices.Contains(startIndices[i]))
{
var unchangedUpTo = AddOne(startIndices, i, offers.Count - 1);
indices.Clear();
for (int j = 0; j <= unchangedUpTo; ++j )
{
indices.Add(startIndices[j]);
}
int nextIndex = 0;
for(int j = unchangedUpTo + 1; j < length; ++j)
{
while (indices.Contains(nextIndex))
nextIndex++;
startIndices[j] = nextIndex++;
}
break;
}
indices.Add(startIndices[i]);
}
}
}
}
int AddOne(int[] indices, int position, int maxElement)
{
//returns the index of the last element that has not been changed
indices[position]++;
for (int i = position; i > 0; --i)
{
if (indices[i] > maxElement)
{
indices[i] = 0;
indices[i - 1]++;
}
else
return i;
}
return 0;
}
If I got you correct here is what you need
this will create permutations based on the specified chain limit
public static List<List<T>> GetPerms<T>(List<T> list, int chainLimit)
{
if (list.Count() == 1)
return new List<List<T>> { list };
return list
.Select((outer, outerIndex) =>
GetPerms(list.Where((inner, innerIndex) => innerIndex != outerIndex).ToList(), chainLimit)
.Select(perms => (new List<T> { outer }).Union(perms).Take(chainLimit)))
.SelectMany<IEnumerable<IEnumerable<T>>, List<T>>(sub => sub.Select<IEnumerable<T>, List<T>>(s => s.ToList()))
.Distinct(new PermComparer<T>()).ToList();
}
class PermComparer<T> : IEqualityComparer<List<T>>
{
public bool Equals(List<T> x, List<T> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<T> obj)
{
return (int)obj.Average(o => o.GetHashCode());
}
}
and you'll call it like this
List<List<AdServer.Offers>> lst = GetPerms<AdServer.Offers>(offers, 2);
I made this function is pretty generic so you may use it for other purpose too
eg
List<string> list = new List<string>(new[] { "apple", "banana", "orange", "cherry" });
List<List<string>> perms = GetPerms<string>(list, 2);
result

break a collection up into smaller collections, but repeat items

I can't get my head around how to do this.
I have a collection of objects
{ object1, object2, object3, object4 }
I want to break up this collection into a collection of collections, so that I end up with something that looks like
{ { object1, object2}, {object2, object3}, {object3, object4} }
I've found how to chunk the collection into smaller ones, but it is the repeating of the previous item in each collection that is doing my head in.
Any help greatly appreciated!
My current chunk method (taken from another question on here) is
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int size)
{
return source.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / size)
.Select(x => x.Select(v => v.Value));
}
EDIT
This works, but is there a better way?
public static ICollection<ICollection<T>> BreakUp<T>(this IEnumerable<T> polylines, int size)
{
var results = new Collection<ICollection<T>>();
results.Add(new Collection<T>());
var x = 0;
for (var i = 0; i < polylines.Count(); i++)
{
results[x].Add(polylines.ElementAt(i));
if (results[x].Count() % size == 0 && i != polylines.Count() - 1)
{
x++;
results.Add(new Collection<T>());
results[x].Add(polylines.ElementAt(i));
}
}
return results;
}
You can simplify your code like this:
public static IEnumerable<IEnumerable<T>> BreakUp<T>(IEnumerable<T> this source, int size)
{
var max = source.Count();
int i = 0;
while (i < max)
{
var current = source.Skip(i).Take(size);
if (current.Count() > 1)
yield return current;
i += size -1;
}
}
Test:
void Main()
{
Console.WriteLine("Example 1");
var source = new Int32[] {1, 2, 3, 4, 5};
foreach (var i in BreakUp(source, 2))
Console.WriteLine(i);
Console.WriteLine("Example 2");
foreach (var i in BreakUp(source, 4))
Console.WriteLine(i);
}
Here's a solution that iterates source only once:
public static IEnumerable<IEnumerable<T>> BreakUp<T>(IEnumerable<T> this source, int size)
{
using(var e = source.GetEnumerator())
{
T last = default(T);
bool has_last = false;
while(e.MoveNext())
{
var current = new List<T>(size);
if(has_last)
current.Add(last);
last = (T)e.Current;
current.Add(last);
while(current.Count < size && e.MoveNext())
{
last = (T)e.Current;
current.Add(last);
has_last = true;
}
yield return current;
}
}
}
Results:
If you only need to group like this
{ { object1, object2}, {object2, object3}, {object3, object4} }
then following code would suffice
return source.Zip(source.Skip(1), (o1, o2) => new List<T> { o1, o2 });
EDIT:
Solution for a variable number of elements:
{ { object1, object2, object3}, {object2, object3, object4}, {object3, object4, object5} }
source.TakeWhile((o,i) => i <= source.Count() - size)
.Select((o,i) => source.Where((lo,li) => li >= i && li < i + size));
This may not be as concise as other solutions, but it iterates through source only once.
public static IEnumerable<List<T>> BreakUp<T>(this IEnumerable<T> source, int size)
{
var max = source.Count();
int start = 0;
var enumerator = source.GetEnumerator();
enumerator.MoveNext();
// We use "max - 1" instead of "max" to avoid adding a list of length 1
while (start < max - 1)
{
int current = 0;
List<T> list = new List<T>();
list.Add(enumerator.Current);
current++;
while(current < size && enumerator.MoveNext())
{
list.Add(enumerator.Current);
current++;
}
yield return list;
start += size - 1;
}
}

Categories

Resources