Linq : Checking how many times the same value consecutively - c#

it's my first question so if it's not quite clear , you can ask for extra information. Keep in mind that english is not my native language :).
I was wondering if it's possible to have an elegant way for next specification.
I think linq could be a possibility but i haven't got enough experience with the technology to get it to work:).
Remark this is not a homework assignment it's just a way to get an new angle to solve these kind of problems.
I've tried with the aggegrate function, maybe an action could help.
I want to keep track of:
the max times a value occurs in an array consecutively.
Per value it should display the maximum times the value occured consecutively
for example:
we have an array of 6 elements with elements either 0 or 1
0 , 0 , 0 , 1 , 1 ,0 result : 3 times 0 , 2 times 1
0 , 0 , 1 , 1 , 1 ,0 result : 2 times 0 , 3 times 1
0 , 1 , 0 , 1 , 1 ,0 result : 1 time 0 , 2 times 1
0 , 0 , 1 , 1 , 0 ,0 result : 2 times 0 , 2 times 1
Thanks in advance

I don't think Linq being a good way out; but a simple method will do:
// Disclamer: Dictionary can't have null key; so source must not coтtain nulls
private static Dictionary<T, int> ConsequentCount<T>(IEnumerable<T> source) {
if (null == source)
throw new ArgumentNullException("source");
Dictionary<T, int> result = new Dictionary<T, int>();
int count = -1;
T last = default(T);
foreach (T item in source) {
count = count < 0 || !object.Equals(last, item) ? 1 : count + 1;
last = item;
int v;
if (!result.TryGetValue(last, out v))
result.Add(last, count);
else if (v < count)
result[item] = count;
}
return result;
}
Tests:
int[][] source = new int[][] {
new[] { 0, 0, 0, 1, 1, 0 },
new[] { 0, 0, 1, 1, 1, 0 },
new[] { 0, 1, 0, 1, 1, 0 },
new[] { 0, 0, 1, 1, 0, 0 }, };
string report = string.Join(Environment.NewLine, source
.Select(array => $"{string.Join(" , ", array)} result : " +
string.Join(", ",
ConsequentCount(array)
.OrderBy(pair => pair.Key)
.Select(pair => $"{pair.Value} times {pair.Key}"))));
Console.Write(report);
Outcome:
0 , 0 , 0 , 1 , 1 , 0 result : 3 times 0, 2 times 1
0 , 0 , 1 , 1 , 1 , 0 result : 2 times 0, 3 times 1
0 , 1 , 0 , 1 , 1 , 0 result : 1 times 0, 2 times 1
0 , 0 , 1 , 1 , 0 , 0 result : 2 times 0, 2 times 1

You can write your own method for groupping by consecutive
public static class Extension
{
public static IEnumerable<IEnumerable<int>> GroupConsecutive(this IEnumerable<int> list)
{
var group = new List<int>();
foreach (var i in list)
{
if (group.Count == 0 || i - group[group.Count - 1] == 0)
group.Add(i);
else
{
yield return group;
group = new List<int> {i};
}
}
yield return group;
}
}
And then use it like that:
var groups = new[] { 0, 0, 1, 1, 0, 0 }.GroupConsecutive();
var maxGroupped = groups.GroupBy(i => i.First()).Select(i => new
{
i.Key,
Count = i.Max(j => j.Count())
});
foreach (var g in maxGroupped)
Console.WriteLine(g.Count + " times " + g.Key);

Here is a lazy inefficient way (seems like linear complexity to me):
int[] arr = { 0, 0, 0, 1, 1, 0 };
string str = string.Concat(arr); // "000110"
int max0 = str.Split('1').Max(s => s.Length); // 3
int max1 = str.Split('0').Max(s => s.Length); // 2
and here is the efficient O(n) version:
int[] arr = { 0, 1, 1, 0, 0, 0 };
int i1 = 0, i = 1;
int[] max = { 0, 0 };
for (; i < arr.Length; i++)
{
if (arr[i] != arr[i1])
{
if (i - i1 > max[arr[i1]]) max[arr[i1]] = i - i1;
i1 = i;
}
}
if (i - i1 > max[arr[i1]]) max[arr[i1]] = i - i1;
Debug.Print(max[0] + "," + max[1]); // "3,2"

Linq will be a bit ugly, but it is possible though, your choice of Aggregate is the way to go, but it won't be a one liner in any case,
Something like this will work,
static void Main(string[] args)
{
var list = new List<int>() { 0, 0, 1, 1, 1, 0, 0 };
var result = list.Aggregate(new
{
Last = (int?)null,
Counts = new Dictionary<int, int>(),
Max = new Dictionary<int, int>()
}, (context, current) =>
{
int count;
if (!context.Counts.TryGetValue(current, out count))
count = 1;
if (context.Last == current)
count += 1;
int lastMax;
context.Max.TryGetValue(current, out lastMax);
context.Max[current] = Math.Max(lastMax, count);
if (context.Last != current)
count = 1;
context.Counts[current] = count;
return new { Last = (int?)current, context.Counts, context.Max };
});
Console.WriteLine(string.Join(",", list) + " Result: ");
var output = string.Join(", ", result.Max.Select(x => string.Format("{0} times {1}", x.Value, x.Key)));
Console.WriteLine(output);
Console.ReadKey();
}

Like others said, performance wise Linq might not be the right tool for the job.
My linq only version would be:
from array in arrayOfArrays
let result = new {
Zeroes = array.TakeWhile(x => x == 0).Count(),
Ones = array.SkipWhile(x => x == 0).TakeWhile(x => x == 1).Count()
}
select $"{String.Join(", ", array)} result : {result.Zeroes} times 0, {result.Ones} times 1"
I'm not sure if Linq2Objects will be smart here to optimize the query internally. We ARE within the query iterating mutliple times over the array. So like i said in advance there may be a performance hit if you execute this over a lot of arrays. If anyone would care to check performance of this in regards to other non linq solutions.

First of all thanks to everyone who put the time and effort in answering the question.
I've choosen Dmitry Bychenko as a valid answer , he was the first to provide an answer , and it was an elegant answer.
Matthew diserves also credit because he has shown me how the aggregate function works with conditionals.
last but not least the answer of victor was the simplest one. I did enhance it to work with generics .
void Main()
{
var groups = new[] { 0, 0, 1, 1, 0,30,1,1,1,1,1 , 22, 22, 15,15,0,0,0,0,0,0 }.GroupConsecutive();
groups.Dump();
var maxGroupped = groups.GroupBy(i => i.First()).Select(i => new
{
i.Key,
Count = i.Max(j => j.Count())
});
foreach (var g in maxGroupped)
Console.WriteLine(g.Count + " times " + g.Key);
}
public static class Extension
{
public static IEnumerable<IEnumerable<T>> GroupConsecutive<T>(this IEnumerable<T> list)
{
var group = new List<T>();
foreach (var value in list)
{
if (group.Count == 0 || value.Equals(group[group.Count-1]))
group.Add(value);
else
{
yield return group;
group = new List<T> {value};
}
}
yield return group;
}

Related

Concat/union 3 Array together in C#

i have a question how to concat or union 3 arrays in one line of the code?
I've got Visual Studio 2015 and it looks like
int[] array1 = {1 ,2 ,3 ,-5 ,2 ,0 };
int[] array2 = {1 ,2 ,3 ,-1 ,5 ,0 };
int[] array3 = {1 ,2 ,3 ,-6 ,2 ,9 };
and i wanna on button click to have like:
Console.WriteLine(array1.Union(array2.Union(array3)).Where((x) => x >=0).Count)
Dunno rly how to Union 3 array in single line
The problem with your code is that after the Where clause the Count is a function and not a property.
In addition a neater way will be to chain the Union instead. Also You can place the predicate in the Count instead:
Console.WriteLine(array1.Union(array2).Union(array3).Count(x => x >= 0));
To print only positive values of all arrays use string.Join:
Console.WriteLine(string.Join(",", array1.Union(array2).Union(array3).Where(x => x >= 0)));
//Prints: 1, 2, 3, 0, 5, 9
For a Union you just need
array1.Union(array2).Union(array3)
and then you can write and/or count the resulting IEnumerable. A Union disregards duplicates: if you want to preserve all the values from all 3 arrays (and continuing arrays instead of some list/collection type) you could do:
var concatenated = new int[array1.Length + array2.Length + array3.Length];
array1.CopyTo(concatenated, 0);
array2.CopyTo(concatenated, array1.Length);
array3.CopyTo(concatenated, array1.Length + array2.Length);
The following will concatenate the arrays by calling Concat.
Then remove the replicated numbers by calling Distinct.
Lastly filter out the numbers by calling Where with the predicate greater or equal to zero.
var arr1 = new[] {1, 2, 3, -5, 2, 0};
var arr2 = new[] {1, 2, 3, -1, 5, 0};
var arr3 = new[] {1, 2, 3, -6, 2, 9};
var result = arr1
.Concat(arr2)
.Concat(arr3)
.Distinct()
.Where(i => i >= 0)
.ToArray();
Can be done in one line of code.
Use linq SelectMany to flatten arrays or lists.
// Set up array of arrays sample.
var a1 = new string[] { "z","x","d" };
var a2 = new string[] { "a","e","i" };
var a3 = new string[] { "q","m","w" };
var arrays = new string[][] { a1, a2, a3 };
var r = arrays.SelectMany(s => s);
Generic concatenation of multiple arrays.
//
//
///<summary>Create a new array as concatenation of all given arrays.</summary>
public static T[] Concatenate<T>( params T[][] args ) {
if ( null == args ) return null;
// Get argument lengths
var count = args.Length;
if ( 0 == count ) return null;
var lengths = new int[ count ];
// Compute all and total lengths
var total = 0;
for ( int i = 0; i < count; i++ ) {
lengths[ i ] = null == args[ i ] ? 0 : args[ i ].Length;
total += lengths[ i ];
}
if ( 1 > total ) return null;
// Create target array
T[] a = new T[ total ];
// Copy all together
total = 0;
for ( int i = 0; i < count; i++ ) {
if ( null != args[ i ] ) {
args[ i ].CopyTo( a, total );
}
total += lengths[ i ];
}
return a;
}

Compare 2 Lists of numbers in LINQ Method Syntax

I have 2 lists of numbers.
public int[] numbersA = { 0, 2, 4 };
public int[] numbersB = { 1, 3, 5 };
I need output like below
Expected Result
0 is less than 1
0 is less than 3
0 is less than 5
2 is less than 3
2 is less than 5
4 is less than 5
How is it possible through LINQ method syntax?
With Method syntax:
var result = numbersA.SelectMany(c => numbersB, (c, o) => new { c, o })
.Where(d => d.c < d.o)
.Select(v=> v.c + "is less than"+ v.o);
At times, verbosity take precedence over brevity as it is clearer and easier to read in most situation, although maybe a bit longer to type.
There is no direct way to achieve what you want as you use Array instead of List (List has ForEach)
But if you want with Arrays I suggest using Array.ForEach.
int[] numbersA = new int[] { 0, 2, 4 };
int[] numbersB = new int[] { 1, 3, 5 };
Array.ForEach(numbersA, x =>
{
Array.ForEach(numbersB, y =>
{
if (x < y)
Console.WriteLine(x + " is less than " + y);
});
Console.WriteLine(Environment.NewLine);
});
Although this question is nothing more than useless business logic, it looks funny to have a try. My solution is List.Foreach rather than Linq, but it is in only one statement.
static void Main(string[] args)
{
int[] numsA = { 0, 2, 4 };
int[] numsB = { 1, 3, 5 };
numsA.ToList().ForEach((a) =>
{
numsB.Where(b => b > a).ToList()
.ForEach((x) =>
{
Console.WriteLine("{0}>{1}", a, x);
});
});
}
Give this a try:
int[] numbersA = { 0, 2, 4 };
int[] numbersB = { 1, 3, 5 };
var result = numbersA.Select(a => numbersB.Where(b => a < b)
.Select(b => a + " is less than " + b))
.SelectMany(arr => arr)
.ToArray();

Split list by element

I have list of 1 and 0 like this:
var list = new List<int>{1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1}
between two items, can be only one zero.
How to split that list into sublists by 0?
Other words: if I have string like this: string myString = "111011011110111111011101" then it is easy to split it by 0 into few strings.
But how to do it with list? This example shoudl produce these sublists:
1,1,1
1,1
1,1,1,1
1,1,1,1,1,1
1,1,1
1
so is there better way then casting each element into string, joining them and doing what I show what can be done with string ?
You can solve your problem by transforming the input sequence into a sequence of sequences just like the LINQ GroupBy does. However, in your case you are grouping on a change in the input sequence. There is perhaps the possibility of combining existing LINQ operators like GroupBy, Zip and Skip into something that does what you want but I think it is easier (and performs better) to create an iterator block that looks at pairs of items in the input sequence:
static class EnumerableExtensions {
public static IEnumerable<IEnumerable<T>> GroupOnChange<T>(
this IEnumerable<T> source,
Func<T, T, Boolean> changePredicate
) {
if (source == null)
throw new ArgumentNullException("source");
if (changePredicate == null)
throw new ArgumentNullException("changePredicate");
using (var enumerator = source.GetEnumerator()) {
if (!enumerator.MoveNext())
yield break;
var firstValue = enumerator.Current;
var currentGroup = new List<T>();
currentGroup.Add(firstValue);
while (enumerator.MoveNext()) {
var secondValue = enumerator.Current;
var change = changePredicate(firstValue, secondValue);
if (change) {
yield return currentGroup;
currentGroup = new List<T>();
}
currentGroup.Add(secondValue);
firstValue = secondValue;
}
yield return currentGroup;
}
}
}
GroupOnChange will take the items in the input sequence and group them into a sequence of sequences. A new group is started when changePredicate is true.
You can use GroupOnChange to split your input sequence exactly as you want to. You then have to remove the groups that have zero as a value by using Where.
var groups = items
.GroupOnChange((first, second) => first != second)
.Where(group => group.First() != 0);
You can also use this approach if the input are class instances and you want to group by a property of that class. You then have to modify the predicate accordingly to compare the properties. (I know you need this because you asked a now deleted question that was slightly more complicated where the input sequence was not simply numbers but classes with a number property.)
You could write an extension method like this:
public static class Extensions
{
public static IEnumerable<IEnumerable<TSource>> Split<TSource>(this IEnumerable<TSource> source, TSource splitOn, IEqualityComparer<TSource> comparer = null)
{
if (source == null)
throw new ArgumentNullException("source");
return SplitIterator(source, splitOn, comparer);
}
private static IEnumerable<IEnumerable<TSource>> SplitIterator<TSource>(this IEnumerable<TSource> source, TSource splitOn, IEqualityComparer<TSource> comparer)
{
comparer = comparer ?? EqualityComparer<TSource>.Default;
var current = new List<TSource>();
foreach (var item in source)
{
if (comparer.Equals(item, splitOn))
{
if (current.Count > 0)
{
yield return current;
current = new List<TSource>();
}
}
else
{
current.Add(item);
}
}
if (current.Count > 0)
yield return current;
}
}
And use it like this:
var list = new List<int>{1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1};
var result = list.Split(0);
int c = 0;
var list = new List<int>{1,1,1,0,1,1,0,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1};
var res = list
// split in groups and set their numbers
// c is a captured variable
.Select(x=>new {Item = x, Subgroup = x==1 ? c : c++})
// remove zeros
.Where(x=>x.Item!=0)
// create groups
.GroupBy(x=>x.Subgroup)
// convert to format List<List<int>>
.Select(gr=>gr.Select(w=>w.Item).ToList())
.ToList();
You can just group by the index of the next zero:
static void Main(string[] args)
{
var list = new List<int> { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
var result = list.Select((e, i) => new { Element = e, Index = i })
.Where(e => e.Element == 1)
.GroupBy(e => list.IndexOf(0, e.Index));
}
Maybe something simpler:
IEnumerable<IEnumerable<int>> Split(IEnumerable<int> items)
{
List<int> result = new List<int>();
foreach (int item in items)
if (item == 0 && result.Any())
{
yield return result;
result = new List<int>();
}
else
result.Add(item);
if (result.Any())
yield return result;
}
The code below splits by iterating over the list and storing sub-sequences of '1' in a list of lists.
using System;
using System.Collections.Generic;
namespace SplitList
{
class Program
{
static void Main(string[] args)
{
var list = new List<int> { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
List<List<int>> splitSequences = new List<List<int>>();
List<int> curSequence = new List<int>();
foreach (var item in list)
{
if (item == 1) {
curSequence.Add(item);
} else {
//Only push the current sequence onto the list of sequences if it is not empty,
//which could happen if multiple zeroes are encountered in a row
if (curSequence.Count > 0) {
splitSequences.Add(curSequence);
curSequence = new List<int>();
}
}
}
//push any final list
if (curSequence.Count > 0)
{
splitSequences.Add(curSequence);
}
foreach (var seq in splitSequences) {
String line = String.Join(",", seq);
Console.WriteLine(line);
}
Console.WriteLine("");
Console.WriteLine("Press any key to exit");
var discard = Console.ReadKey();
}
}
}
I did like ASh's solution most. I've used it with a slight change. There is no captured variable in "my" variant:
var list = new List<int> { 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
var res = list
// Step 1: associate every value with an index
.Select((x, i) => (Value: x, Index: i))
// Step 2: remove separator values
.Where(x => x.Value != 0)
// Step 3: adjacent items will have the same result
// subtracting real position from the index marked during the 1st step
.Select((tuple, realIndex) => (tuple.Value, GroupId: tuple.Index - realIndex))
// Step 4: group by groupId
.GroupBy(tuple => tuple.GroupId)
// Step 5: convert to List<List<int>>
.Select(group => group.Select(tuple => tuple.Value).ToList())
.ToList();
More on step 3:
Let's say, we have 5 items with indices:
[ 0 1 2 3 4 ]
{ 1, 1, 0, 1, 1 }
After filtration from the step 2, list of numbers looks next:
[ 0 1 3 4 ]
{ 1, 1, 1, 1 }
What does step 3:
real indices (ri): [ 0 1 2 3 ]
indices from the 1st step (i): [ 0 1 3 4 ]
numbers: { 1, 1, 1, 1 }
i - ri: [ 0, 0, 1, 1 ]
And step 4 just groups by result of the subtraction i - ri, so called GroupID.

How to split an Observable stream in chunks, dependent on second stream?

I thought that this is easy, but my brain is melting right now..
The problem
Given the following IObservable<int> Stream:
1
1
0
0
0
1
0
0
1
0
1
I want to split it into an IObservable<IEnumerable<int>> Stream of the form
1
1 0 0 0
1 0 0
1 0
1
so whenever there is a 0, it just gets added to the IEnumerable, and when a 1 occurs, a new List is started; This is a bit cleaner definition to what my real problem is.
My approach so far
I thought a good solution would be to first convert it into an IObservable<IObservable<int>>via the Window method and then use ToEnumerable, but somehow I don't get it to work.. I used Zip and Skip(1) to get a diff to last element, I used DistinctUntilChanged(), too. I spare you all the variantes I tried...
Probably the closest I came was this code:
int[] ints = new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 };
var observable = Observable.Interval(TimeSpan.FromMilliseconds(1000)).Take(11).Select(i => ints[i]);
Subject<int> subject = new Subject<int>();
observable.Subscribe(subject);
var observableDiff = subject.Skip(1).Zip(subject, (n, p) => new { Previous = p, Next = n });
var windows = observable.Window(() => observableDiff.Where(x => x.Next == 1));
int index = 0;
windows.Subscribe(window =>
{
Console.WriteLine(string.Format("new window [{0}] ", index++));
window.Subscribe(number => Console.WriteLine(number));
});
That returns good results, but unfortunately it crashes at the end..
new window [0]
1
new window [1]
1
0
0
0
new window [2]
1
0
0
new window [3]
1
0
new window [4]
new window [5]
new window [6]
new window [7]
new window [8]
new window [9]
<-- it goes on here until window ~ [80] with a stackoverflow exception
If that bug in my code wouldn't exist, I would have achieved it...
Any help would be much appreciated. :)
Edit: I use Rx-Experimental, but it doesn't make a difference (checked with LinqPad). Also removed the Subject, it didn't influence anything. It seems with my new approach (Edit2), you need a subject, otherwise the start of the windows is totally weird.
Edit2: changed the problem slightly, to better highlight my problem, sorry. Also updated my solution.
This worked for me:
var ints = (new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 }).ToObservable();
var result =
ints
.Publish(ns =>
ns
.Where(n => n == 1)
.Select(n =>
ns.TakeWhile(m => m == 0).StartWith(n).ToArray())
).Merge();
I've used Publish in to make sure that the ints observable is treated as "hot" rather than "cold".
My results look like this:
The built-in Buffer seems pretty close to what you need. An intermediate subscription between the source and the Buffer call will let you get the closings observables you need for Buffer.
IObservable<IList<T>> Buffer<T>(IObservable<T> source,
Func<T, bool> startNew)
{
return Observable.Create<IList<T>>(
obs =>
{
var starts = new Subject<Unit>();
return source.Do(v =>
{
if (startNew(v))
starts.OnNext(Unit.Default);
})
.Buffer(() => starts)
.Where(v => v != null && v.Count > 0)
.Subscribe(obs);
});
}
Ok, these are good answers, too, from the Rx forums:
James Miles suggestion:
var source = new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 }.ToObservable();
var windows =
from window in source
.Buffer(2,1) // create an overlapping buffer with 2 items
.Publish(xs => xs.Window(() => xs.Where(x => x.Last() == 1))) // close the window if the 2nd item is == 1
from result in window
.Select(buffer => buffer.First()) // we are only interested in the first item (the 2nd item might be the 1!)
.ToArray() // aggregate the results of the window
where result.Any() // filter out final (empty) window
select result;
int index = 0;
windows.Subscribe(window =>
{
Console.WriteLine(string.Format("new window [{0}] ", index++));
foreach(var x in window)Console.WriteLine(x);
});
Dave Sexton suggested using the Parser class from Extensions for Reactive Extensions (Rxx), which seems to be a more semantic approach:
using Rxx.Parsers.Reactive.Linq;
public sealed class SplitLab : BaseConsoleLab
{
protected override void Main()
{
var xs = Observable.Generate(
new[] { 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 }.GetEnumerator(),
e => e.MoveNext(),
e => e,
e => (int) e.Current,
e => TimeSpan.FromSeconds(.5));
var query = xs.Parse(parser =>
from next in parser
let one = next.Where(value => value == 1)
let other = next.Not(one)
let window = from start in one
from remainder in other.NoneOrMore()
select remainder.StartWith(start)
let windowAsString = window.Join()
select windowAsString);
using (query.Subscribe(TraceLine))
{
WaitForKey();
}
}
}
So many roads to rome..

Iterating through c# array & placing objects sequentially into other arrays

Okay, so this seems simple, but I can't think of a straightforward solution;
Basically I have an object array in C# that contains, say, 102 elements. I then also have 4 other empty arrays. I want to iterate through the original array and distribute the 100 elements evenly, then distribute 101 and 102 to the 1st and 2nd new arrays respectively.
int i = 1,a=0, b=0, c=0, d = 0;
foreach (ReviewStatus data in routingData)
{
if (i == 1)
{
threadOneWork[a] = data;
a++;
}
if (i == 2)
{
threadTwoWork[b] = data;
b++;
}
if (i == 3)
{
threadThreeWork[c] = data;
c++;
}
if (i == 4)
{
threadFourWork[d] = data;
d++;
i = 0;
}
i++;
}
Now the above code definitely works, but I was curious, does anybody know of a better way to do this??
var workArrays = new[] {
threadOneWork,
threadTwoWork,
threadThreeWork,
threadFourWork,
};
for(int i=0; i<routingData.Length; i++) {
workArrays[i%4][i/4] = routingData[i];
}
Put the four arrays into an array of arrays, and use i%4 as an index. Assuming that thread###Work arrays have enough space to store the data, you can do this:
var tw = new[] {threadOneWork, threadTwoWork, threadThreeWork, threadFourWork};
var i = 0;
foreach (ReviewStatus data in routingData) {
tw[i%4][i/tw.Length] = data;
i++;
}
Linq is your friend! Use modulo to group the items via the total number of arrays in your case 4.
For example the code splits them up into four different lists:
var Items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Items.Select( ( i, index ) => new {
category = index % 4,
value = i
} )
.GroupBy( itm => itm.category, itm => itm.value )
.ToList()
.ForEach( gr => Console.WriteLine("Group {0} : {1}", gr.Key, string.Join(",", gr)));
/* output
Group 0 : 1,5,9
Group 1 : 2,6,10
Group 2 : 3,7
Group 3 : 4,8
*/

Categories