2014-01-16 2 views
6

Предполагая, что у меня есть списокSplit массив с LINQ

var listOfInt = new List<int> {1, 2, 3, 4, 7, 8, 12, 13, 14} 

Как я могу использовать LINQ, чтобы получить список списков следующим образом:

{{1, 2, 3, 4}, {7, 8}, {12, 13, 14}} 

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

+5

, что является crieteria на основании которого вы split –

+0

@ Co.Aden: Как они писали, им нужны смежные диапазоны чисел в отдельных подпоследовательностях. – Joey

+0

@ Тогда это должно быть {1,2,3,4}, {7,8}, {12,13,14} –

ответ

3

Вы можете создать метод расширения (я пропустил проверку источника здесь), который будет перебирать источник и создавать группы последовательные элементы. Если следующий элемент в источнике не подряд, то текущая группа дала:

public static IEnumerable<List<int>> ToConsecutiveGroups(
    this IEnumerable<int> source) 
{ 
    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      yield break; 
     } 
     else 
     {      
      int current = iterator.Current; 
      List<int> group = new List<int> { current }; 

      while (iterator.MoveNext()) 
      { 
       int next = iterator.Current; 
       if (next < current || current + 1 < next) 
       { 
        yield return group; 
        group = new List<int>();        
       } 

       current = next; 
       group.Add(current); 
      } 

      if (group.Any()) 
       yield return group; 
     }     
    } 
} 

Способ применения прост:

var listOfInt = new List<int> { 1, 2, 3, 4, 7, 8, 12, 13, 14 }; 
var groups = listOfInt.ToConsecutiveGroups(); 

Результат:

[ 
    [ 1, 2, 3, 4 ], 
    [ 7, 8 ], 
    [ 12, 13, 14 ] 
] 

UPDATE: Вот общий версия этого метода расширения, который принимает предикат для проверки того, следует ли считать два значения последовательно:

public static IEnumerable<List<T>> ToConsecutiveGroups<T>(
    this IEnumerable<T> source, Func<T,T, bool> isConsequtive) 
{ 
    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      yield break; 
     } 
     else 
     {      
      T current = iterator.Current; 
      List<T> group = new List<T> { current }; 

      while (iterator.MoveNext()) 
      { 
       T next = iterator.Current; 
       if (!isConsequtive(current, next)) 
       { 
        yield return group; 
        group = new List<T>();        
       } 

       current = next; 
       group.Add(current); 
      } 

      if (group.Any()) 
       yield return group; 
     }     
    } 
} 

Способ применения прост:

var result = listOfInt.ToConsecutiveGroups((x,y) => (x == y) || (x == y - 1)); 
2

Предположим, что ваш вклад в в порядке, следующие будут работать:

var grouped = input.Select((n, i) => new { n, d = n - i }).GroupBy(p => p.d, p => p.n); 

Это не будет работать, если ваш вклад, например { 1, 2, 3, 999, 5, 6, 7 }.

Вы получите { { 1, 2, 3, 5, 6, 7 }, { 999 } }.

+0

+1 для самых сжатых/элегантных с учетом ограничения. Однако легко удалите ограничение с помощью 'OrderBy' так:' input.OrderBy (n => n). Выберите ((n, i) => new {n, d = ni}). GroupBy (p => pd , p => p.n); ' –

+0

@msorens. Однако' OrderBy' разрушает последовательный тест. '1 2 3 8 1 2 3' должен быть' {1 2 3} {8} {1 2 3} ', но становится беспорядочным, если вы его сортируете. – Rawling

+0

Хорошая точка; Я предполагал, что эти ценности были уникальными, но не было причин, по которым они должны были быть в общем случае. –

3

Это работает как для отсортированных и неупорядоченных списков:

var listOfInt = new List<int> { 1, 2, 3, 4, 7, 8, 12, 13 }; 
int index = 0; 
var result = listOfInt.Zip(listOfInt 
          .Concat(listOfInt.Reverse<int>().Take(1)) 
          .Skip(1), 
          (v1, v2) => 
          new 
          { 
           V = v1, 
           G = (v2 - v1) != 1 ? index++ : index 
          }) 
         .GroupBy(x => x.G, x => x.V, (k, l) => l.ToList()) 
         .ToList(); 

Внешний index строит индекс последовательных групп, которые имеют значение разности 1. Тогда вы можете просто GroupBy относительно этого индекса.

Для уточнения решения, вот как эта коллекция выглядит без группировки (GroupBy комментировал):

enter image description here

+0

Он пропускает последнее значение ввода. – okrumnow

+0

@okrumnow, правда, я обновил ответ соответственно –

0

Это работает:

var results = 
    listOfInt 
     .Skip(1) 
     .Aggregate(
      new List<List<int>>(new [] { listOfInt.Take(1).ToList() }), 
      (a, x) => 
      { 
       if (a.Last().Last() + 1 == x) 
       { 
        a.Last().Add(x); 
       } 
       else 
       { 
        a.Add(new List<int>(new [] { x })); 
       } 
       return a; 
      }); 

Я получаю этот результат:

results

Смежные вопросы