var schedules = new List- {
new Item { Id=1, Name = \"S\" },
new Item { Id=2, Name = \"P\" },
new Item { Id=3, Name = \"X\" },
new Item { Id=4,
@dasblinkenlight has provided an answer that just uses LINQ. Any answer using purely existing LINQ methods may be ugly, may perform poorly, and may not be highly reusable. (This is not a criticism of that answer. It's a criticism of LINQ.)
@eoin-campbell has provided an answer that uses a custom LINQ method. However, I think it can be improved upon to more closely match the capabilities of the existing LINQ GroupBy
function, such as custom comparers (for when you need to do things like case-insensitive comparison of the keys). This Partition
method below looks and feels like the GroupBy
function but meets the requirement for consecutive items.
You can use this method to meet your goal by doing the following. Notice that it looks exactly like how you would write this if you didn't have the consecutivity requirement, but it's using Partition
instead of GroupBy
.
var partitionsWithMoreThan1 = schedules.Partition(o => o.Name)
.Where(p => p.Count() > 1)
.Select(p => p.ToList())
.ToList();
Here's the method:
static class EnumerableExtensions
{
///
/// Partitions the elements of a sequence into smaller collections according to a specified
/// key selector function, optionally comparing the keys by using a specified comparer.
/// Unlike GroupBy, this method does not produce a single collection for each key value.
/// Instead, this method produces a collection for each consecutive set of matching keys.
///
/// The type of the elements of .
/// The type of the key returned by .
/// An whose elements to partition.
/// A function to extract the key for each element.
/// An to compare keys.
///
/// An IEnumerable{IGrouping{TKey, TSource}} in C#
/// or IEnumerable(Of IGrouping(Of TKey, TSource)) in Visual Basic
/// where each object contains a collection of objects and a key.
///
public static IEnumerable> Partition(this IEnumerable source, Func keySelector, IEqualityComparer comparer = null)
{
if (comparer == null)
comparer = EqualityComparer.Default;
using (var enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
var item = enumerator.Current;
var partitionKey = keySelector(item);
var itemsInPartition = new List {item};
var lastPartitionKey = partitionKey;
while (enumerator.MoveNext())
{
item = enumerator.Current;
partitionKey = keySelector(item);
if (comparer.Equals(partitionKey, lastPartitionKey))
{
itemsInPartition.Add(item);
}
else
{
yield return new Grouping(lastPartitionKey, itemsInPartition);
itemsInPartition = new List {item};
lastPartitionKey = partitionKey;
}
}
yield return new Grouping(lastPartitionKey, itemsInPartition);
}
}
}
// it's a shame there's no ready-made public implementation that will do this
private class Grouping : IGrouping
{
public Grouping(TKey key, List items)
{
_items = items;
Key = key;
}
public TKey Key { get; }
public IEnumerator GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
private readonly List _items;
}
}