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;
}
I have an array of lists:
private List<int>[] Graph = new List<int>[n];
For example:
Graph[0] = new List<int>() { 1, 2 };
Graph[1] = new List<int>() { 0 };
Graph[2] = new List<int>() { 0, 1, 3, 4 };
Graph[3] = new List<int>() { 2, 4, 1 };
Graph[4] = new List<int>() { 2, 3 };
Graph[0].Count // give 2
Graph[1].Count // give 1
Graph[2].Count // give 4
Graph[3].Count // give 3
Graph[4].Count // give 2
And I want to get an array (or list) which includes the indexes of the lists sorted by the count of the elements in each list. So for this example it will be:
orderList[0] -> 2 //(because Graph[2].Count give 4)
orderList[1] -> 3 //(because Graph[3].Count give 3)
orderList[2] -> 0 //(because Graph[0].Count give = 2)
orderList[3] -> 4 //(because Graph[4].Count give = 2)
orderList[4] -> 1 //(because Graph[1].Count give = 1)
orderList is an n-elements array.
You can use the select method that incorporates an index to combine the list count with the index
int[] orderList = Graph.Select((list, index) => new { Count = list.Count, Index = index }).OrderByDescending(a => a.Count).Select(a => a.Index).ToArray();
More readable query syntax
int[] orderList = (from pair in Graph.Select((list, index) => new { Count = list.Count, Index = index })
orderby pair.Count descending
select pair.Index).ToArray();
All you need is:
Graph = Graph.OrderByDescending(x => x.Count).ToArray();
You can use LINQ:
List<int>[] orderedGraph = graph.OrderByDescending(x => x.Count).ToArray();
This will order array descending using list "count" property.
Remember to add using:
using System.Linq;
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..
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
*/