How to group consecutive similar elements of a collection?

Consider the following set.

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

I want to show it in a structured way, say, in a TreeView . I want to be able to draw borders around entire groups, etc.

  • True group
    • True
  • False group
    • False
    • False
    • False
  • True group
    • True
    • True
  • False group
    • False
    • False

How to do this with minimal procedural code?

+2
wpf datatemplate hierarchicaldatatemplate
source share
5 answers

This does what you are looking for, and 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; } 

Application:

 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 
+5
source share

As long as the code in the accepted answer meets the needs of the original question, it will crash when handling IEnumerables of more complex objects (since the predicate will tend to throw an exception when comparing the last element in an enum using the "next" element [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
source share
 last = null; foreach (var option in list) { if (last != option) newlist.Add(new Group(option, new[])); newlist.Last().Add(option); last = option; } 
-one
source share
 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); } } 
-one
source share

All Articles