Linq: разделить список по условию и максимальному размеру n - PullRequest
0 голосов
/ 27 февраля 2019

Я хочу преобразовать массив:

["a", "b", "b", "a", "b", "a", "b"]

в

["a", "a", "b", "b", "a", "b", "b"] или ["b", "b", "a", "a", "b", "b", "a"]

Я хочу сгруппировать массив таким образом, чтобыОсобые условия соответствуют.В моем случае, когда item == 'a' или item == 'b'.И эти группы я хочу разделить на группы по 2. Я в настоящее время немного запутался, как сделать это элегантным способом.

Может кто-нибудь помочь?

Может быть, следующее делает это более понятным:

Мне нравится сначала группировать массив в элементы 'a' и 'b', например:

a-group:

["a","a","a"]

и

b-group:

["b","b","b","b"]

тогда я хочу разделить это на группы по 2:

a-group:

["a","a"]
["a"]

b-group:

["b","b"]
["b","b"]

А теперь я хочу объединить их вместе, чтобы получить результат:

["a","a","b","b","a","b","b"]

(всегда 2 из каждой группы объединяются)

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Сначала вам нужно GroupBy ваши данные.Предположим, что объект string, но в любом случае он не имеет значения, это условие вашей группировки, которое изменится, если у вас будет что-то другое.

Для этого вам понадобится MoreLinq или просто добавьте расширение Batch, которое выполняет«сгруппировать по 2 и 1 для оставшихся за кадром».Подробности можно найти здесь

обратите внимание, что Batch(2) можно изменить на все, что вам нужно.Если вы поставите Batch(5) и у вас будет 7 элементов, то получится 2 группы, одна с 5 элементами и одна из 2 элементов

 // be my data, 4 x a, 3 x b, 1 x c, 2 x d, As a list for easier linQ
 var data = new[] { "a", "a", "c", "b", "a", "b", "b", "d", "d", "a" }.ToList();

 // group by our condition. Here it's the value so very simple
 var groupedData = data.GroupBy(o => o).ToList();

 // transform all groups into a custom List of List of List so they are grouped by 2 internally
 // each list level represent Grouping -> Groups of 2 (or 1) -> element
 var groupedDataBy2 = groupedData.Select(grp => grp.ToList().AsEnumerable().Batch(2).ToList()).ToList();

 // find the group with the maximum amount of groups or 2 (and 1)
 var maxCount = groupedDataBy2.Max(grp => grp.Count());

 // will contain our final objects listed
 var finalObjects = new List<string>();

 // loop on the count we figured out and try add each group one by one
 for (int i = 0; i < maxCount; i++)
 {
     // try each group
     foreach (var group in groupedDataBy2)
     {
         // add the correct index to our final list only if the current group has enough to fill
         if (i < group.Count)
         {
             // add the data to our final list
             finalObjects.AddRange(group[i]);
         }
     }
 }

 // result here is : a,a,c,b,b,d,d,a,a,b
 var results = string.Join(",", finalObjects);
0 голосов
/ 27 февраля 2019

Я думаю, для простоты преобразование ваших массивов в списки с последующим использованием findall() было бы самым простым способом сделать это.

List<char> list = new List<char>(['a', 'a', 'b'...]);

если вы используете предикаты, тогда выможет автоматически фильтровать список, чтобы вытащить каждый из двух элементов, как показано ниже:

a_elements = list.findall((char c) -> { return c == item; });

Тогда для b вы можете сделать

b_elements = list.findall((char c) -> { return c != item; });

где (char c) -> { return c... } является предикатом, метод возвращает true, когда элемент в списке соответствует условию, и false, в противном случае вы также можете использовать класс Predicate от Microsoft, чтобы сформировать их, но я предпочитаю лямбда-функции.

Теперь a_elements и b_elements - это массивы, содержащие совпадения для каждого соответствующего findall() call

. Оттуда все, что вам нужно сделать, это сгруппировать массивы в группы по два

.
...