2014-12-11 4 views
5

У меня есть List<string>, и я хочу взять из него группы из 5 предметов. Нет ключей или чего-то простого для группировки ... но он всегда будет кратным 5.Возьмите группы из 5 строк из списка

например.

{"A","16","49","FRED","AD","17","17","17","FRED","8","B","22","22","107","64"} 

Принимать группы:

"A","16","49","FRED","AD" 
"17","17","17","FRED","8" 
"B","22","22","107","64" 

, но я не могу работать, простой способ сделать это!

Довольно уверен, что это может быть сделано с перечислением и Take (5) ...

+1

Вы c на самом деле использовать Take (5) и на следующей итерации Skip (5). Взять (5) и так далее. –

+2

Это работает для небольших списков, но в целом это плохая идея. Он выполняет итерацию по коллекции один раз в группе, что приводит к квадратичному времени работы [O (n^2)] (http://stackoverflow.com/a/487300/141397). Это относится ко всем остальным ответам здесь, используя '.Skip(). Take()'. – 3dGrabber

+2

http://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq – Andrew

ответ

7

Вы можете использовать трюк целочисленного деления:

List<List<string>> groupsOf5 = list 
    .Select((str, index) => new { str, index }) 
    .GroupBy(x => x.index/5) 
    .Select(g => g.Select(x => x.str).ToList()) 
    .ToList(); 
+1

это будет работать, но 1) это создаст ненужный анонс. объекты, 2) он будет перебирать по коллекции не менее трех раз. –

+1

http://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq yeah, right – Andrew

1

Использование Take() и Пропустить() для достижения этой цели:

 List<string> list = new List<string>() { "A", "16", "49", "FRED", "AD", "17", "17", "17", "FRED", "8", "B", "22", "22", "107", "64" }; 

     List<List<string>> result = new List<List<string>>(); 
     for (int i = 0; i < list.Count/5; i++) 
     { 
      result.Add(list.Skip(i * 5).Take(5).ToList()); 
     } 
+0

Я не знаю, сколько всего наборов из 5 будет, только что они будут полными наборами – BlueChippy

+0

отредактировано для работы на yoy –

7
List<List<string>> result = new List<List<string>>(); 
for(int i = 0; i < source.Count; i += 5) 
     result.Add(source.Skip(i).Take(5).ToList()); 

как это?

+6

У этого есть ужасная производительность исполнения, см. Мой комментарий в вопросе. – 3dGrabber

+0

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

1

Вы можете использовать эту функцию:

public IEnumerable<string[]> GetChunk(string[] input, int size) 
{ 
    int i = 0; 
    while (input.Length > size * i) 
    { 
     yield return input.Skip(size * i).Take(size).ToArray(); 
     i++; 
    } 
} 

возвращает вам куски из списка

вы можете проверить это, как

var list = new[] 
{ 
    "A", "16", "49", "FRED", "AD", "17", "17", "17", "FRED", "8", "B", "22", "22", "107", "64" 
}; 

foreach (var strings in GetChunk(list, 5)) 
{ 
    Console.WriteLine(strings.Length); 
} 
+1

Как этот ответ, потому что я могу расширить размер «chunk» ... – BlueChippy

+0

Я отредактировал его для поддержки разных размеров блоков – dotctor

3

В общем синтаксисе программирования:

 public List<List<string>> Split(List<string> items, int chunkSize = 5) 
    { 
     int chunkCount = items.Count/chunkSize; 
     List<List<string>> result = new List<List<string>>(chunkCount); 

     for (int i = 0; i < chunkCount; i++) 
     { 
      result.Add(new List<string>(chunkSize)); 
      for (int j = i * chunkSize; j < (i + 1) * chunkSize; j++) 
      { 
       result[i].Add(items[j]); 
      } 
     } 

     return result; 
    } 

Это O((N/ChunkSize) x ChunkSize) = O(N), то есть линейный.

+0

throws index вне установленной дистанции исключение – WhileTrueSleep

+1

правый, сделано .... – Andrew

+1

лучший ответ до сих пор IMO. Поскольку размер куска постоянный внутри цикла, вы можете использовать вместо него массив, который быстрее и использует меньше памяти. Или, по крайней мере, используйте конструктор списка с «начальной емкостью», поэтому список не должен изменять размер. – 3dGrabber

2

Я рекомендую Batch метод из MoreLINQ библиотеки:

var result = list.Batch(5).ToList(); 
1

Если вам нужна производительность или не можете использовать Linq причины вашей .net версии здесь простое решение с O(n)

private List<List<string>> SplitList(List<string> input, int size = 5) 
    { 
     var result = new List<List<string>>(); 
     for (int i = 0; i < input.Count; i++) 
     { 
      var partResult = new List<string>(); 
      while (true) 
      { 
       // save n items 
       partResult.Add(input[i]); 
       if ((i+1) % size == 0) 
       { 
        break; 
       } 
       i++; 
      } 
      result.Add(partResult); 
     } 

     return result; 
    } 
+2

Ваше имя говорит само за себя, если я смотрю на внутренний цикл;) –

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