How to find consecutive same values items as a Linq group

前端 未结 4 1109
無奈伤痛
無奈伤痛 2021-01-29 09:23
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,         


        
4条回答
  •  粉色の甜心
    2021-01-29 10:06

    @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;
        }
    }
    

提交回复
热议问题