How to group consecutive similar items of a collection?

后端 未结 5 848
忘了有多久
忘了有多久 2020-12-06 23:06

Consider the following collection.

  • True
  • False
  • False
  • False
  • True
  • True
  • False
  • False

I

相关标签:
5条回答
  • 2020-12-06 23:17

    This does what you're looking for and is generic:

    private static IEnumerable<IGrouping<int, T>> GroupConsecutive<T>(this IEnumerable<T> set, Func<T, T, bool> predicate)
    {
        var i = 0;
        var k = 0;
        var ranges = from e in set
                     let idx = ++i
                     let next = set.ElementAtOrDefault(idx)
                     let key = (predicate(e, next)) ? k : k++
                     group e by key into g
                     select g;
        return ranges;
    }
    

    Usage:

    var set = new List<bool>
                {
                    true,
                    false,
                    false,
                    false,
                    true,
                    true,
                    false,
                    false,
                };
    var groups = set.GroupConsecutive((b1, b2) => (b1 == b2));
    foreach (var g in groups)
    {
        Console.WriteLine(g.Key);
        foreach (var b in g)
            Console.WriteLine("\t{0}", b);
    }
    

    Output:

    0
            True
    1
            False
            False
            False
    2
            True
            True
    3
            False
            False
    
    0 讨论(0)
  • 2020-12-06 23:24
    public class GroupConsecutiveEqualItemsConverter : IValueConverter
    {
        static readonly object UnsetValue = new object();
    
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            IEnumerable source = value as IEnumerable;
            if (source == null) return DependencyProperty.UnsetValue;
            string propertyName = parameter as string;
            var result = new ObservableCollection<List<object>>();
    
            var notify = value as INotifyCollectionChanged;
            if (notify != null) notify.CollectionChanged += delegate { Reload(result, source, propertyName); };
    
            Reload(result, source, propertyName);
            return result;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    
        void Reload(ObservableCollection<List<object>> result, IEnumerable source, string propertyName)
        {
            result.Clear();
            object previous = UnsetValue;
            List<object> group = null;
            foreach (object i in source)
            {
                object current = UnsetValue;
                if (propertyName == null)
                {
                    current = i;
                }
                else
                {
                    try
                    {
                        var property = i.GetType().GetProperty(propertyName);
                        if (property != null) current = property.GetValue(i, null);
                    }
                    catch (AmbiguousMatchException) { }
                }
                if (!object.Equals(previous, current))
                {
                    if (group != null) result.Add(group);
                    group = new List<object>();
                }
                group.Add(i);
                previous = current;
            }
            if (group != null && group.Count > 0) result.Add(group);
        }
    }
    
    0 讨论(0)
  • 2020-12-06 23:25

    While the code in the accepted answer meets the needs of the original question, it will fall over when handling IEnumerables of more complex objects (since the predicate will tend to throw an exception when comparing the last item in the enumerable with the "next" item [which, by definition, will always be null]).

    This version handles more complex objects:

       public static IEnumerable<IGrouping<int, T>> GroupConsecutive<T>(this IEnumerable<T> set, Func<T, T, bool> predicate)
        {
            var i = 0;
            var k = 0;
            var ranges = from e in set
                         let idx = ++i
                         let next = set.ElementAtOrDefault(idx)
                         let key = next == null ? k : (predicate(e, next)) ? k : k++
                         group e by key into g
                         select g;
            return ranges;
        } 
    
    0 讨论(0)
  • 2020-12-06 23:28

    You may wanna check out the pseudo TreeGrid on Delay's blog at http://blogs.msdn.com/delay/archive/2009/09/23/if-it-walks-like-a-duck-and-talks-like-a-duck-it-must-be-a-treegrid-a-simple-xaml-only-treegrid-ui-for-wpf.aspx


    (source: msdn.com)

    0 讨论(0)
  • 2020-12-06 23:31
    last = null;
    foreach (var option in list)
    {
       if (last != option)
          newlist.Add(new Group(option, new[]));
       newlist.Last().Add(option);
       last = option;
    }
    
    0 讨论(0)
提交回复
热议问题