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.
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 }
]
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()})
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).
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.
Related
Let's take a class called Cls:
public class Cls
{
public int SequenceNumber { get; set; }
public int Value { get; set; }
}
Now, let's populate some collection with following elements:
Sequence
Number Value
======== =====
1 9
2 9
3 15
4 15
5 15
6 30
7 9
What I need to do, is to enumerate over Sequence Numbers and check if the next element has the same value. If yes, values are aggregated and so, desired output is as following:
Sequence Sequence
Number Number
From To Value
======== ======== =====
1 2 9
3 5 15
6 6 30
7 7 9
How can I perform this operation using LINQ query?
You can use Linq's GroupBy in a modified version which groups only if the two items are adjacent, then it's easy as:
var result = classes
.GroupAdjacent(c => c.Value)
.Select(g => new {
SequenceNumFrom = g.Min(c => c.SequenceNumber),
SequenceNumTo = g.Max(c => c.SequenceNumber),
Value = g.Key
});
foreach (var x in result)
Console.WriteLine("SequenceNumFrom:{0} SequenceNumTo:{1} Value:{2}", x.SequenceNumFrom, x.SequenceNumTo, x.Value);
DEMO
Result:
SequenceNumFrom:1 SequenceNumTo:2 Value:9
SequenceNumFrom:3 SequenceNumTo:5 Value:15
SequenceNumFrom:6 SequenceNumTo:6 Value:30
SequenceNumFrom:7 SequenceNumTo:7 Value:9
This is the extension method to to group adjacent items:
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
TKey last = default(TKey);
bool haveLast = false;
List<TSource> list = new List<TSource>();
foreach (TSource s in source)
{
TKey k = keySelector(s);
if (haveLast)
{
if (!k.Equals(last))
{
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource>();
list.Add(s);
last = k;
}
else
{
list.Add(s);
last = k;
}
}
else
{
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
}
and the class used:
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
{
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
}
System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
{
foreach (var s in GroupList)
yield return s;
}
public GroupOfAdjacent(List<TSource> source, TKey key)
{
GroupList = source;
Key = key;
}
}
You can use this linq query
Demo
var values = (new[] { 9, 9, 15, 15, 15, 30, 9 }).Select((x, i) => new { x, i });
var query = from v in values
let firstNonValue = values.Where(v2 => v2.i >= v.i && v2.x != v.x).FirstOrDefault()
let grouping = firstNonValue == null ? int.MaxValue : firstNonValue.i
group v by grouping into v
select new
{
From = v.Min(y => y.i) + 1,
To = v.Max(y => y.i) + 1,
Value = v.Min(y => y.x)
};
MoreLinq provides this functionality out of the box
It's called GroupAdjacent and is implemented as extension method on IEnumerable:
Groups the adjacent elements of a sequence according to a specified key selector function.
enumerable.GroupAdjacent(e => e.Key)
There is even a Nuget "source" package that contains only that method, if you don't want to pull in an additional binary Nuget package.
The method returns an IEnumerable<IGrouping<TKey, TValue>>, so its output can be processed in the same way output from GroupBy would be.
You can do it like this:
var all = new [] {
new Cls(1, 9)
, new Cls(2, 9)
, new Cls(3, 15)
, new Cls(4, 15)
, new Cls(5, 15)
, new Cls(6, 30)
, new Cls(7, 9)
};
var f = all.First();
var res = all.Skip(1).Aggregate(
new List<Run> {new Run {From = f.SequenceNumber, To = f.SequenceNumber, Value = f.Value} }
, (p, v) => {
if (v.Value == p.Last().Value) {
p.Last().To = v.SequenceNumber;
} else {
p.Add(new Run {From = v.SequenceNumber, To = v.SequenceNumber, Value = v.Value});
}
return p;
});
foreach (var r in res) {
Console.WriteLine("{0} - {1} : {2}", r.From, r.To, r.Value);
}
The idea is to use Aggregate creatively: starting with a list consisting of a single Run, examine the content of the list we've got so far at each stage of aggregation (the if statement in the lambda). Depending on the last value, either continue the old run, or start a new one.
Here is a demo on ideone.
I was able to accomplish it by creating a custom extension method.
static class Extensions {
internal static IEnumerable<Tuple<int, int, int>> GroupAdj(this IEnumerable<Cls> enumerable) {
Cls start = null;
Cls end = null;
int value = Int32.MinValue;
foreach (Cls cls in enumerable) {
if (start == null) {
start = cls;
end = cls;
continue;
}
if (start.Value == cls.Value) {
end = cls;
continue;
}
yield return Tuple.Create(start.SequenceNumber, end.SequenceNumber, start.Value);
start = cls;
end = cls;
}
yield return Tuple.Create(start.SequenceNumber, end.SequenceNumber, start.Value);
}
}
Here's the implementation:
static void Main() {
List<Cls> items = new List<Cls> {
new Cls { SequenceNumber = 1, Value = 9 },
new Cls { SequenceNumber = 2, Value = 9 },
new Cls { SequenceNumber = 3, Value = 15 },
new Cls { SequenceNumber = 4, Value = 15 },
new Cls { SequenceNumber = 5, Value = 15 },
new Cls { SequenceNumber = 6, Value = 30 },
new Cls { SequenceNumber = 7, Value = 9 }
};
Console.WriteLine("From To Value");
Console.WriteLine("===== ===== =====");
foreach (var item in items.OrderBy(i => i.SequenceNumber).GroupAdj()) {
Console.WriteLine("{0,-5} {1,-5} {2,-5}", item.Item1, item.Item2, item.Item3);
}
}
And the expected output:
From To Value
===== ===== =====
1 2 9
3 5 15
6 6 30
7 7 9
Here is an implementation without any helper methods:
var grp = 0;
var results =
from i
in
input.Zip(
input.Skip(1).Concat(new [] {input.Last ()}),
(n1, n2) => Tuple.Create(
n1, (n2.Value == n1.Value) ? grp : grp++
)
)
group i by i.Item2 into gp
select new {SequenceNumFrom = gp.Min(x => x.Item1.SequenceNumber),SequenceNumTo = gp.Max(x => x.Item1.SequenceNumber), Value = gp.Min(x => x.Item1.Value)};
The idea is:
Keep track of your own grouping indicator, grp.
Join each item of the collection to the next item in the collection (via Skip(1) and Zip).
If the Values match, they are in the same group; otherwise, increment grp to signal the start of the next group.
Untested dark magic follows. The imperative version seems like it would be easier in this case.
IEnumerable<Cls> data = ...;
var query = data
.GroupBy(x => x.Value)
.Select(g => new
{
Value = g.Key,
Sequences = g
.OrderBy(x => x.SequenceNumber)
.Select((x,i) => new
{
x.SequenceNumber,
OffsetSequenceNumber = x.SequenceNumber - i
})
.GroupBy(x => x.OffsetSequenceNumber)
.Select(g => g
.Select(x => x.SequenceNumber)
.OrderBy(x => x)
.ToList())
.ToList()
})
.SelectMany(x => x.Sequences
.Select(s => new { First = s.First(), Last = s.Last(), x.Value }))
.OrderBy(x => x.First)
.ToList();
Let me propose another option, which yields lazily both sequence of groups and
elements inside groups.
Demonstration in .NET Fiddle
Implementation:
public static class EnumerableExtensions
{
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey>? comparer = null)
{
var comparerOrDefault = comparer ?? EqualityComparer<TKey>.Default;
using var iterator = new Iterator<TSource>(source.GetEnumerator());
iterator.MoveNext();
while (iterator.HasCurrent)
{
var key = keySelector(iterator.Current);
var elements = YieldAdjacentElements(iterator, key, keySelector, comparerOrDefault);
yield return new Grouping<TKey, TSource>(key, elements);
while (iterator.HasCurrentWithKey(key, keySelector, comparerOrDefault))
{
iterator.MoveNext();
}
}
}
static IEnumerable<TSource> YieldAdjacentElements<TKey, TSource>(
Iterator<TSource> iterator,
TKey key,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
while (iterator.HasCurrentWithKey(key, keySelector, comparer))
{
yield return iterator.Current;
iterator.MoveNext();
}
}
private static bool HasCurrentWithKey<TKey, TSource>(
this Iterator<TSource> iterator,
TKey key,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer) =>
iterator.HasCurrent && comparer.Equals(keySelector(iterator.Current), key);
private sealed class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
{
public Grouping(TKey key, IEnumerable<TElement> elements)
{
Key = key;
Elements = elements;
}
public TKey Key { get; }
public IEnumerable<TElement> Elements { get; }
public IEnumerator<TElement> GetEnumerator() => Elements.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Elements.GetEnumerator();
}
private sealed class Iterator<T> : IDisposable
{
private readonly IEnumerator<T> _enumerator;
public Iterator(IEnumerator<T> enumerator)
{
_enumerator = enumerator;
}
public bool HasCurrent { get; private set; }
public T Current => _enumerator.Current;
public void MoveNext()
{
HasCurrent = _enumerator.MoveNext();
}
public void Dispose()
{
_enumerator.Dispose();
}
}
}
Notice, that it is impossible to achieve such level of laziness with regular GroupBy operation, since it needs to look through the whole collection before yielding the first group.
Particularly, in my case migration from GroupBy to GroupAdjacent in connection with lazy handling of whole pipeline helped to resolve memory consumption issues for large sequences.
In general, GroupAdjacent can be used as lazy and more efficient alternative of GroupBy, provided that input collection satisfies condition, that keys are sorted (or at least not fragmented), and provided that all operations in pipeline are lazy.
This question already has answers here:
linq group by contiguous blocks
(5 answers)
Closed 4 years ago.
Lets say I have an list of strings with the following values:
["a","a","b","a","a","a","c","c"]
I want to execute a linq query that will group into 4 groups:
Group 1: ["a","a"] Group 2: ["b"] Group 3: ["a","a","a"] Group 4:
["c","c"]
Basically I want to create 2 different groups for the value "a" because they are not coming from the same "index sequence".
Anyone has a LINQ solution for this?
You just need key other than items of array
var x = new string[] { "a", "a", "a", "b", "a", "a", "c" };
int groupId = -1;
var result = x.Select((s, i) => new
{
value = s,
groupId = (i > 0 && x[i - 1] == s) ? groupId : ++groupId
}).GroupBy(u => new { groupId });
foreach (var item in result)
{
Console.WriteLine(item.Key);
foreach (var inner in item)
{
Console.WriteLine(" => " + inner.value);
}
}
Here is the result: Link
Calculate the "index sequence" first, then do your group.
private class IndexedData
{
public int Sequence;
public string Text;
}
string[] data = [ "a", "a", "b" ... ]
// Calculate "index sequence" for each data element.
List<IndexedData> indexes = new List<IndexedData>();
foreach (string s in data)
{
IndexedData last = indexes.LastOrDefault() ?? new IndexedData();
indexes.Add(new IndexedData
{
Text = s,
Sequence = (last.Text == s
? last.Sequence
: last.Sequence + 1)
});
}
// Group by "index sequence"
var grouped = indexes.GroupBy(i => i.Sequence)
.Select(g => g.Select(i => i.Text));
This is a naive foreach implementation where whole dataset ends up in memory (probably not an issue for you since you do GroupBy):
public static IEnumerable<List<string>> Split(IEnumerable<string> values)
{
var result = new List<List<string>>();
foreach (var value in values)
{
var currentGroup = result.LastOrDefault();
if (currentGroup?.FirstOrDefault()?.Equals(value) == true)
{
currentGroup.Add(value);
}
else
{
result.Add(new List<string> { value });
}
}
return result;
}
Here comes a slightly complicated implementation with foreach and yield return enumerator state machine which keeps only current group in memory - this is probably how this would be implemented on framework level:
EDIT: This is apparently also the way MoreLINQ does it.
public static IEnumerable<List<string>> Split(IEnumerable<string> values)
{
var currentValue = default(string);
var group = (List<string>)null;
foreach (var value in values)
{
if (group == null)
{
currentValue = value;
group = new List<string> { value };
}
else if (currentValue.Equals(value))
{
group.Add(value);
}
else
{
yield return group;
currentValue = value;
group = new List<string> { value };
}
}
if (group != null)
{
yield return group;
}
}
And this is a joke version using LINQ only, it is basically the same as the first one but is slightly harder to understand (especially since Aggregate is not the most frequently used LINQ method):
public static IEnumerable<List<string>> Split(IEnumerable<string> values)
{
return values.Aggregate(
new List<List<string>>(),
(lists, str) =>
{
var currentGroup = lists.LastOrDefault();
if (currentGroup?.FirstOrDefault()?.Equals(str) == true)
{
currentGroup.Add(str);
}
else
{
lists.Add(new List<string> { str });
}
return lists;
},
lists => lists);
}
Using an extension method based on the APL scan operator, that is like Aggregate but returns intermediate results paired with source values:
public static IEnumerable<KeyValuePair<TKey, T>> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<KeyValuePair<TKey, T>, T, TKey> combine) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prevkv = new KeyValuePair<TKey, T>(seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = new KeyValuePair<TKey, T>(combine(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
}
You can create extension methods for grouping by consistent runs:
public static IEnumerable<IGrouping<int, TResult>> GroupByRuns<TElement, TKey, TResult>(this IEnumerable<TElement> src, Func<TElement, TKey> key, Func<TElement, TResult> result, IEqualityComparer<TKey> cmp = null) {
cmp = cmp ?? EqualityComparer<TKey>.Default;
return src.ScanPair(0,
(kvp, cur) => cmp.Equals(key(kvp.Value), key(cur)) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => result(kvp.Value));
}
public static IEnumerable<IGrouping<int, TElement>> GroupByRuns<TElement, TKey>(this IEnumerable<TElement> src, Func<TElement, TKey> key) => src.GroupByRuns(key, e => e);
public static IEnumerable<IGrouping<int, TElement>> GroupByRuns<TElement>(this IEnumerable<TElement> src) => src.GroupByRuns(e => e, e => e);
public static IEnumerable<IEnumerable<TResult>> Runs<TElement, TKey, TResult>(this IEnumerable<TElement> src, Func<TElement, TKey> key, Func<TElement, TResult> result, IEqualityComparer<TKey> cmp = null) =>
src.GroupByRuns(key, result).Select(g => g.Select(s => s));
public static IEnumerable<IEnumerable<TElement>> Runs<TElement, TKey>(this IEnumerable<TElement> src, Func<TElement, TKey> key) => src.Runs(key, e => e);
public static IEnumerable<IEnumerable<TElement>> Runs<TElement>(this IEnumerable<TElement> src) => src.Runs(e => e, e => e);
And using the simplest version, you can get either an IEnumerable<IGrouping>>:
var ans1 = src.GroupByRuns();
Or a version that dumps the IGrouping (and its Key) for an IEnumerable:
var ans2 = src.Runs();
Context
I have a list of time intervals. Time interval type is HistoMesures.
Each HistoMesure is defined by a Debut (begin) property, a Fin (end) property, and a Commentaires (a little note) property.
My list is made in such a way that :
All HistoMesure are exclusive, I mean that they can't be overlapping each other.
The list is sorted by Debut, so by the beggining of the interval.
Edit : All HistoMesure are contiguous in this configuration.
Question
I want to merge (transform two little intervals in one big interval) all adjacent HistoMesure which have the same Commentaires. Currently I achieve this that way :
//sortedHistos type is List<HistoMesure>
int i = 0;
while (i < sortedHistos.Count - 1)
{
if (sortedHistos[i].Commentaires == sortedHistos[i + 1].Commentaires)
{
sortedHistos[i].Fin = sortedHistos[i + 1].Fin;
sortedHistos.RemoveAt(i + 1);
}
else
{
++i;
}
}
But I feel that it exists a more elegant way to do this, maybe with LINQ. Do you have any suggestion ?
Your solution works fine, I would keep it.
Don't try too hard to use LINQ if it doesn't match your requirements. LINQ is great to write queries (this is the Q of LINQ), not so great to modify existing lists.
This code will produce overlapping merged intervals. I.e. if you have intervals A, B, C where A and C have same commentaries, result will be AC, B:
var result = from h in sortedHistos
group h by h.Commentaires into g
select new HistoMesure {
Debut = g.First().Debut, // thus you have sorted entries
Fin = g.Last().Fin,
Commentaires = g.Key
};
You can use Min and Max if intervals are not sorted.
UPDATE: There is no default LINQ operator which allows you to create adjacent groups. But you always can create one. Here is IEnumerable<T> extension (I skipped arguments check):
public static IEnumerable<IGrouping<TKey, TElement>> GroupAdjacent<TKey, TElement>(
this IEnumerable<TElement> source, Func<TElement, TKey> keySelector)
{
using (var iterator = source.GetEnumerator())
{
if(!iterator.MoveNext())
{
yield break;
}
else
{
var comparer = Comparer<TKey>.Default;
var group = new Grouping<TKey, TElement>(keySelector(iterator.Current));
group.Add(iterator.Current);
while(iterator.MoveNext())
{
TKey key = keySelector(iterator.Current);
if (comparer.Compare(key, group.Key) != 0)
{
yield return group;
group = new Grouping<TKey, TElement>(key);
}
group.Add(iterator.Current);
}
if (group.Any())
yield return group;
}
}
}
This extension creates groups of adjacent elements which have same key value. Unfortunately all implementations of IGrouping in .NET are internal, so you need yours:
public class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
{
private List<TElement> elements = new List<TElement>();
public Grouping(TKey key)
{
Key = key;
}
public TKey Key { get; private set; }
public IEnumerator<TElement> GetEnumerator()
{
return elements.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(TElement element)
{
elements.Add(element);
}
}
And now your code will look like:
var result = sortedHistos.GroupAdjacent(h => h.Commentaries)
.Select(g => new HistoMesure {
Debut = g.Min(h => h.Debut),
Fin = g.Max(h => h.Fin),
Commentaries = g.Key
});
Using Linq and borrowing from this article to group by adjacent values, this should work:
Your query:
var filteredHistos = sortedHistos
.GroupAdjacent(h => h.Commentaires)
.Select(g => new HistoMesure
{
Debut = g.First().Debut,
Fin = g.Last().Fin,
Commentaires = g.Key
});
And copying from the article, the rest of the code to group by:
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
{
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
}
System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
{
foreach (var s in GroupList)
yield return s;
}
public GroupOfAdjacent(List<TSource> source, TKey key)
{
GroupList = source;
Key = key;
}
}
public static class LocalExtensions
{
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
TKey last = default(TKey);
bool haveLast = false;
List<TSource> list = new List<TSource>();
foreach (TSource s in source)
{
TKey k = keySelector(s);
if (haveLast)
{
if (!k.Equals(last))
{
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource>();
list.Add(s);
last = k;
}
else
{
list.Add(s);
last = k;
}
}
else
{
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
}
If I understood you correctly, you need something like this:
var mergedMesures = mesures
.GroupBy(_ => _.Commentaires)
.Select(_ => new HistoMesures
{
Debut = _.Min(item => item.Debut),
Fin = _.Max(item => item.Fin),
Commentaires = _.Key
});
I have a need to move an item in an IEnumerable<> up, that is move one item above another. What is the simplest way to do this?
A similar question was asked here but I don't have a generic list only an IEnumerable<>: Generic List - moving an item within the list
As #Brian commented the question is a little unclear as to what move an item in an IEnumerable<> up means.
If you want to reorder an IEnumerable for a single item then the code below should might be what you are looking for.
public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> enumerable, int itemIndex)
{
int i = 0;
IEnumerator<T> enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext())
{
i++;
if (itemIndex.Equals(i))
{
T previous = enumerator.Current;
if (enumerator.MoveNext())
{
yield return enumerator.Current;
}
yield return previous;
break;
}
yield return enumerator.Current;
}
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
You can't. IEnumerable is only to iterate through some items, not for editing a list of items
You can use the ToList() extension method and use the answer from the question you referenced. e.g.
var list = enumerable.ToList();
//do stuff from other answer, and then convert back to enumerable if you want
var reorderedEnumerable = list.AsEnumerable();
I didn't find anything that would do what you want with IEnumerable<T>. Having developed similar stuff in the past for specific types of collections, list, arrays, etc, I felt it was time to take a better look at it. So I took a couple of minutes to write a generic version that could be applied to any IEnumerable<T>.
I did some basic testing and parameter checking but by no means consider them compreensive.
Given that disclaimer, let's get to the code:
static class Enumerable {
public static IEnumerable<T> MoveDown<T>(this IEnumerable<T> source, int index) {
if (source == null) {
throw new ArgumentNullException("source");
}
T[] array = source.ToArray();
if (index == array.Length - 1) {
return source;
}
return Swap<T>(array, index, index + 1);
}
public static IEnumerable<T> MoveDown<T>(this IEnumerable<T> source, T item) {
if (source == null) {
throw new ArgumentNullException("source");
}
T[] array = source.ToArray();
int index = Array.FindIndex(array, i => i.Equals(item));
if (index == -1) {
throw new InvalidOperationException();
}
if (index == array.Length - 1) {
return source;
}
return Swap<T>(array, index, index + 1);
}
public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> source, int index) {
if (source == null) {
throw new ArgumentNullException("source");
}
T[] array = source.ToArray();
if (index == 0) {
return source;
}
return Swap<T>(array, index - 1, index);
}
public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> source, T item) {
if (source == null) {
throw new ArgumentNullException("source");
}
T[] array = source.ToArray();
int index = Array.FindIndex(array, i => i.Equals(item));
if (index == -1) {
throw new InvalidOperationException();
}
if (index == 0) {
return source;
}
return Swap<T>(array, index - 1, index);
}
public static IEnumerable<T> Swap<T>(this IEnumerable<T> source, int firstIndex, int secondIndex) {
if (source == null) {
throw new ArgumentNullException("source");
}
T[] array = source.ToArray();
return Swap<T>(array, firstIndex, secondIndex);
}
private static IEnumerable<T> Swap<T>(T[] array, int firstIndex, int secondIndex) {
if (firstIndex < 0 || firstIndex >= array.Length) {
throw new ArgumentOutOfRangeException("firstIndex");
}
if (secondIndex < 0 || secondIndex >= array.Length) {
throw new ArgumentOutOfRangeException("secondIndex");
}
T tmp = array[firstIndex];
array[firstIndex] = array[secondIndex];
array[secondIndex] = tmp;
return array;
}
public static IEnumerable<T> Swap<T>(this IEnumerable<T> source, T firstItem, T secondItem) {
if (source == null) {
throw new ArgumentNullException("source");
}
T[] array = source.ToArray();
int firstIndex = Array.FindIndex(array, i => i.Equals(firstItem));
int secondIndex = Array.FindIndex(array, i => i.Equals(secondItem));
return Swap(array, firstIndex, secondIndex);
}
}
As you can see, MoveUp and MoveDown are basically Swap operations. With MoveUp you swap positions with the previous element and with MoveDown you swap positions with the next element.
Of course, that does not apply for moving up the first element or moving down the last element.
Running a quick test with the code below...
class Program {
static void Main(string[] args) {
int[] a = { 0, 2, 1, 3, 4 };
string[] z = { "Zero", "Two", "One", "Three", "Four" };
IEnumerable<int> b = Enumerable.Swap(a, 1, 2);
WriteAll(b);
IEnumerable<int> c = Enumerable.MoveDown(a, 1);
WriteAll(c);
IEnumerable<int> d = Enumerable.MoveUp(a, 2);
WriteAll(d);
IEnumerable<int> f = Enumerable.MoveUp(a, 0);
WriteAll(f);
IEnumerable<int> g = Enumerable.MoveDown(a, 4);
WriteAll(g);
IEnumerable<string> h = Enumerable.Swap(z, "Two", "One");
WriteAll(h);
var i = z.MoveDown("Two");
WriteAll(i);
var j = z.MoveUp("One");
WriteAll(j);
Console.WriteLine("Press any key to continue...");
Console.Read();
}
private static void WriteAll<T>(IEnumerable<T> b) {
foreach (var item in b) {
Console.WriteLine(item);
}
}
... it looks like everything is working well.
I hope it serves at least as a starting point for you.
I like this approach
/// <summary>
/// Extension methods for <see cref="System.Collections.Generic.List{T}"/>
/// </summary>
public static class ListExtensions
{
public static void MoveForward<T>(this List<T> list, Predicate<T> itemSelector, bool isLastToBeginning)
{
Ensure.ArgumentNotNull(list, "list");
Ensure.ArgumentNotNull(itemSelector, "itemSelector");
var currentIndex = list.FindIndex(itemSelector);
// Copy the current item
var item = list[currentIndex];
bool isLast = list.Count - 1 == currentIndex;
if (isLastToBeginning && isLast)
{
// Remove the item
list.RemoveAt(currentIndex);
// add the item to the beginning
list.Insert(0, item);
}
else if (!isLast)
{
// Remove the item
list.RemoveAt(currentIndex);
// add the item at next index
list.Insert(currentIndex + 1, item);
}
}
public static void MoveBack<T>(this List<T> list, Predicate<T> itemSelector, bool isFirstToEnd)
{
Ensure.ArgumentNotNull(list, "list");
Ensure.ArgumentNotNull(itemSelector, "itemSelector");
var currentIndex = list.FindIndex(itemSelector);
// Copy the current item
var item = list[currentIndex];
bool isFirst = 0 == currentIndex;
if (isFirstToEnd && isFirst)
{
// Remove the item
list.RemoveAt(currentIndex);
// add the item to the end
list.Add(item);
}
else if (!isFirstToEnd)
{
// Remove the item
list.RemoveAt(currentIndex);
// add the item to previous index
list.Insert(currentIndex - 1, item);
}
}
}
I need to access the current and previous element in an IQueryable object. If I had an int array, I would do the following:
var array = new int[]{0,1,2,3,4};
for(var i = 1; i<array.Length ; i++)
{
method1(array[i-1], array[i]);
}
I don't know to do the same with IQueryable, since it does not implement IList.
Using extension methods makes this fairly easy.
public static class IEnumerableExtensions
{
public static IEnumerable<ValueWithPrevious<T>> WithPrevious<T>(this IEnumerable<T> #this)
{
using (var e = #this.GetEnumerator())
{
if (!e.MoveNext())
yield break;
var previous = e.Current;
while (e.MoveNext())
{
yield return new ValueWithPrevious<T>(e.Current, previous);
previous = e.Current;
}
}
}
}
public struct ValueWithPrevious<T>
{
public readonly T Value, Previous;
public ValueWithPrevious(T value, T previous)
{
Value = value;
Previous = previous;
}
}
Usage:
var array = new int[] { 1, 2, 3, 4, 5 };
foreach (var value in array.WithPrevious())
{
Console.WriteLine("{0}, {1}", value.Previous, value.Value);
// Results: 1, 2
// 2, 3
// 3, 4
// 4, 5
}
You can turn an IQueryable<> into a List<> using ToList<>().
EDIT
Misread the question a bit. This code will give you consequetive elements
public static IEnumerable<Pair<T,T>> GroupIntoConsequetive(this IEnumerable<T> enumerable) {
using ( var e = enumerable.GetEnumerator() ) {
if ( !e.MoveNext() ) {
yield break;
}
var last = e.Current;
while ( e.MoveNext() ) {
yield return new Pair<T,T>(last, e.Current);
last = e.Current;
}
}
}
I'm not sure there is default way but writing an extension method to do so shouldn't be to difficult. I'm assuming there is a simple Pair implementation
public static IEnumerable<Pair<T,T>> Window(this IEnumerable<T> enumerable) {
using ( var e = enumerable.GetEnumerator() ) {
while ( e.MoveNext() ) {
var first = e.Current;
if ( !e.MoveNext() ) {
throw new InvalidOperationException("Need even number");
}
var second = e.Current;
yield return new Pair<T,T>(first,second);
}
}
}
With the window you could then get the behavior you desire with the following
var col = GetQueryableItem();
col.Window().Select(pair => method1(pair.First, pair.Second));
Quick and dirty Pair implementation
public struct Pair<T1,T2> {
public readonly T1 First;
public readonly T2 Second;
public Pair(T1 first, T2 second) {
First = first;
Second = second;
}
}
But it provides extension methods to create an array or a list from your IQueryable<T> instance, see ToArray() and ToList(). You can then go and do the same as you would with the array in your example.
IQueryable is IEnumerable. So you can do something like:
var a = new [] {1, 2, 3, 4}.AsQueryable();
if (a.Count() < 2) {
return;
}
var prev = a.First();
var isFirst = true;
foreach (var i in a) {
if (isFirst) {
isFirst = false;
continue;
}
method1(prev, i);
prev = i;
}
Or simply convert IQueryable into IList:
var list = a.ToList();