2009-09-25 13 views
2

Considérons la collection suivante.Comment grouper des éléments similaires consécutifs d'une collection?

  • Vrai Faux
  • Faux
  • Faux
  • vrai
  • Vrai Faux
  • Faux

Je veux pour l'afficher de manière structurée, par exemple, dans un TreeView. Je veux être capable de dessiner des frontières autour de groupes entiers et autres.

  • vrai groupe
    • vrai
  • groupe Faux
    • Faux
    • Faux
    • Faux
  • vrai groupe
    • vrai
    • vrai
  • groupe Faux
    • Faux
    • Faux

Comment est-ce que j'accomplis cela avec le moins de code de procédure possible?

Répondre

5

Cela fait ce que vous êtes à la recherche et est générique:

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

Utilisation:

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

Sortie:

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

Félicitations pour l'utilisation des fonctionnalités de langage de fantaisie. – CannibalSmith

+0

Y a-t-il des performances O (n^2) en raison de l'accès aux éléments IEnumerable par l'index de la ligne 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

C'est la partie facile. – 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

Alors que le code dans la réponse acceptée répond aux besoins de la question initiale, il tombera sur lors de la manipulation IEnumerables des objets plus complexes (puisque le prédicat aura tendance à lancer une exception lors de la comparaison du dernier élément de l'énumérable avec l'élément "suivant" [qui, par définition, sera toujours nul]).

Cette version gère des objets plus complexes:

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