Reputation: 1933
I've a list like this:
var query = Enumerable.Range(0, 999).Select((n, index) =>
{
if (index <= 333 || index >=777)
return 0;
else if (index <= 666)
return 1;
else
return 2;
});
So, Can I find how much indexes have same value continuously? For example;
query[0]=query[1]=query[2]=query[3]... = 0, query[334] = 1, query[777]=query[778]... = 0.
First 334 indexes have 0, so first answer is 333. Also Last 223 indexes have 0, so second answer is 223..
How can I find these and their indexes?
Thanks in advance.
Upvotes: 0
Views: 1134
Reputation: 35716
erm, how about this, most efficient implementation I can think of.
IEnuemrable<KeyValuePair<T, int>> RepeatCounter<T>(
IEnumerable<T> source,
IEqualityComparer<T> comparer = null)
{
var e = source.GetEnumerator();
if (!e.MoveNext())
{
yield break;
}
comparer = comparer ?? EqualityComparer<T>.Default;
var last = e.Current;
var count = 1;
while (e.MoveNext())
{
if (comparer.Equals(last, e.Current))
{
count++;
continue;
}
yield return new KeyValuePair<T, int>(last, count);
last = e.Current;
count = 1;
}
yield return new KeyValuePair<T, int>(last, count);
}
enumerates the sequence exactly once and only allocates variables when necessary.
Upvotes: 0
Reputation: 236268
You can create extension for consecutive grouping of items by some key:
public static IEnumerable<IGrouping<TKey, T>> GroupConsecutive<T, TKey>(
this IEnumerable<T> source, Func<T, TKey> keySelector)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
else
{
List<T> list = new List<T>();
var comparer = Comparer<TKey>.Default;
list.Add(iterator.Current);
TKey groupKey = keySelector(iterator.Current);
while (iterator.MoveNext())
{
var key = keySelector(iterator.Current);
if (!list.Any() || comparer.Compare(groupKey, key) == 0)
{
list.Add(iterator.Current);
continue;
}
yield return new Group<TKey, T>(groupKey, list);
list = new List<T> { iterator.Current };
groupKey = key;
}
if (list.Any())
yield return new Group<TKey, T>(groupKey, list);
}
}
}
Of course you can return IEnumerable<IList<T>>
but that will be a little different from concept of group, which you want to have, because you also want to know which value was used to group sequence of items. Unfortunately there is no public implementation of IGrouping<TKey, TElement>
interface, and we should create our own:
public class Group<TKey, TElement> : IGrouping<TKey, TElement>
{
private TKey _key;
private IEnumerable<TElement> _group;
public Group(TKey key, IEnumerable<TElement> group)
{
_key = key;
_group = group;
}
public TKey Key
{
get { return _key; }
}
public IEnumerator<TElement> GetEnumerator()
{
return _group.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Now usage is very simple:
var groups = query.GroupConsecutive(i => i) // produces groups
.Select(g => new { g.Key, Count = g.Count() }); // projection
Result:
[
{ Key: 0, Count: 334 },
{ Key: 1, Count: 333 },
{ Key: 2, Count: 110 },
{ Key: 0, Count: 222 }
]
Upvotes: 1
Reputation: 152596
Using the GroupConsecutive
extension method from here you can just get the counts of each group:
query.GroupConsecutive((n1, n2) => n1 == n2)
.Select(g => new {Number = g.Key, Count = g.Count()})
Upvotes: 1
Reputation: 11903
public static IEnumerable<int> GetContiguousCounts<T>(this IEnumerable<T> l, IEqualityComparer<T> cmp)
{
var last = default(T);
var count = 0;
foreach (var e in l)
{
if (count > 0 && !cmp.Equals(e, last))
{
yield return count;
count = 0;
}
count++;
last = e;
}
if (count > 0)
yield return count;
}
public static IEnumerable<int> GetContiguousCounts<T>(this IEnumerable<T> l)
{
return GetContiguousCounts(l, EqualityComparer<T>.Default);
}
static void Main(string[] args)
{
var a = new[] { 1, 2, 2, 3, 3, 3 };
var b = a.GetContiguousCounts();
foreach (var x in b)
Console.WriteLine(x);
}
For the simple test case, it outputs 1, 2, 3. For your case 334, 333, 110, 222 (the last value is not 223 as you asked in your question, because you only have 999 elements, not 1000).
Upvotes: 0