2009-09-25 4 views
2

Рассмотрите следующую коллекцию.Как группировать последовательные похожие элементы коллекции?

  • Правда
  • Ложные
  • Ложные
  • Ложные
  • Правда
  • Правда
  • Ложные
  • Ложные

Я хочу для отображения его структурированным способом, скажем, в TreeView. Я хочу иметь возможность рисовать границы вокруг целых групп и т. Д.

  • Правда Группа
    • Правда
  • Ложные Группа
    • Ложные
    • Ложные
    • Ложные
  • Правда Группа
    • Правда
    • Правда
  • Ложные Группа
    • Ложные
    • Ложные

Как выполним ли это с минимальным процедурным кодом?

ответ

5

Это делает то, что вы ищете и родовое:

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

Использование:

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

Выход:

0 
     True 
1 
     False 
     False 
     False 
2 
     True 
     True 
3 
     False 
     False 
+0

Престижность использования фэнтезийных особенностей. – CannibalSmith

+0

Не существует ли производительность O (n^2) из-за доступа к элементам IEnumerable по индексу в строке 7 («let next = set.ElementAtOrDefault (idx)»)? –

-1
last = null; 
foreach (var option in list) 
{ 
    if (last != option) 
     newlist.Add(new Group(option, new[])); 
    newlist.Last().Add(option); 
    last = option; 
} 
+0

Это легкая часть. – CannibalSmith

-1
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

Хотя код в принятом ответе отвечает потребностям первоначального вопроса, он будет падать при обработке IEnumerables более сложных объектов (так как предикат будет стремиться генерировать исключение при сравнении последнего элемента в перечисляемом с «следующим» элементом [который по определению всегда будет null]).

Эта версия поддерживает более сложные объекты:

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; 
    } 
Смежные вопросы