Список разделения в подсписки с LINQ

Для тех, кто не знает, как использовать PDO (исходя из функций mysql_), я сделал очень, очень простую PDO-обертку , которая представляет собой один файл. Он существует, чтобы показать, насколько легко выполнять все обычные приложения, которые необходимо выполнить. Работает с PostgreSQL, MySQL и SQLite.

В основном читайте

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

Мне нужен один столбец

$count = DB::column('SELECT COUNT(*) FROM `user`);

Мне нужны результаты массива (key => value) (т. е. для создания selectbox)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

Мне нужен результат одной строки

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

Мне нужен массив результатов

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

360
задан Draken 17 March 2017 в 07:38
поделиться

7 ответов

Попробуйте следующий код.

public static IList<IList<T>> Split<T>(IList<T> source)
{
    return  source
        .Select((x, i) => new { Index = i, Value = x })
        .GroupBy(x => x.Index / 3)
        .Select(x => x.Select(v => v.Value).ToList())
        .ToList();
}

идея состоит в том, чтобы сначала сгруппировать элементы индексами. Деление на три имеет эффект группировки их в группы 3. Тогда преобразуйте каждую группу в список и IEnumerable из List к List из List s

355
ответ дан Mykola 23 November 2019 в 00:15
поделиться

Если список имеет тип system.collections.generic, можно использовать метод "CopyTo", доступный для копирования элементов массива к другим массивам sub. Вы определяете элемент запуска и число элементов для копирования.

Вы могли также сделать 3 клона своего исходного списка и использовать "RemoveRange" в каждом списке для уменьшения списка к размеру, который Вы хотите.

Или просто создают вспомогательный метод сделать это для Вас.

3
ответ дан Jobo 23 November 2019 в 00:15
поделиться

Вот список, разделяющий стандартную программу, которую я записал паре несколько месяцев назад:

public static List<List<T>> Chunk<T>(
    List<T> theList,
    int chunkSize
)
{
    List<List<T>> result = theList
        .Select((x, i) => new {
            data = x,
            indexgroup = i / chunkSize
        })
        .GroupBy(x => x.indexgroup, x => x.data)
        .Select(g => new List<T>(g))
        .ToList();

    return result;
}
6
ответ дан Amy B 23 November 2019 в 00:15
поделиться

Вы могли использование много запросов, которые используют Take и Skip , но это добавило бы слишком много повторений в исходном списке, я верю.

Скорее я думаю, что необходимо создать собственный итератор, как так:

public static IEnumerable<IEnumerable<T>> GetEnumerableOfEnumerables<T>(
  IEnumerable<T> enumerable, int groupSize)
{
   // The list to return.
   List<T> list = new List<T>(groupSize);

   // Cycle through all of the items.
   foreach (T item in enumerable)
   {
     // Add the item.
     list.Add(item);

     // If the list has the number of elements, return that.
     if (list.Count == groupSize)
     {
       // Return the list.
       yield return list;

       // Set the list to a new list.
       list = new List<T>(groupSize);
     }
   }

   // Return the remainder if there is any,
   if (list.Count != 0)
   {
     // Return the list.
     yield return list;
   }
}

можно тогда назвать это, и это - LINQ, включенный так, можно выполнить другие операции на получающихся последовательностях.

<час>

световой сигнал In [1 123] ответ Sam , который я чувствовал, был более легкий способ сделать это без:

  • Итерация через список снова (который я не сделал первоначально)
  • Осуществление объектов в группах прежде, чем выпустить блок (для больших блоков объектов, будут проблемы памяти)
  • Весь код, который Sam отправил

Однако здесь является другой передачей, которую я шифровал в дополнительном методе к [1 124] IEnumerable<T> названный Chunk:

public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, 
    int chunkSize)
{
    // Validate parameters.
    if (source == null) throw new ArgumentNullException("source");
    if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize",
        "The chunkSize parameter must be a positive value.");

    // Call the internal implementation.
    return source.ChunkInternal(chunkSize);
}

Ничто удивляющее там, просто основная проверка ошибок.

Хождение дальше к [1 110]:

private static IEnumerable<IEnumerable<T>> ChunkInternal<T>(
    this IEnumerable<T> source, int chunkSize)
{
    // Validate parameters.
    Debug.Assert(source != null);
    Debug.Assert(chunkSize > 0);

    // Get the enumerator.  Dispose of when done.
    using (IEnumerator<T> enumerator = source.GetEnumerator())
    do
    {
        // Move to the next element.  If there's nothing left
        // then get out.
        if (!enumerator.MoveNext()) yield break;

        // Return the chunked sequence.
        yield return ChunkSequence(enumerator, chunkSize);
    } while (true);
}

В основном, это получает IEnumerator<T> и вручную выполняет итерации через каждый объект. Это проверяет, чтобы видеть если там любые объекты в настоящее время, чтобы быть перечисленным. После того, как каждый блок перечисляется через, при отсутствии оставленных объектов, он вспыхивает.

, Как только это обнаруживает, существуют объекты в последовательности, это делегирует ответственность за внутреннее IEnumerable<T> реализация к [1 113]:

private static IEnumerable<T> ChunkSequence<T>(IEnumerator<T> enumerator, 
    int chunkSize)
{
    // Validate parameters.
    Debug.Assert(enumerator != null);
    Debug.Assert(chunkSize > 0);

    // The count.
    int count = 0;

    // There is at least one item.  Yield and then continue.
    do
    {
        // Yield the item.
        yield return enumerator.Current;
    } while (++count < chunkSize && enumerator.MoveNext());
}

С тех пор MoveNext был уже назван на эти IEnumerator<T>, передал [1 116], это приводит к объекту, возвращенному [1 127] Current , и затем увеличивает количество, удостоверяясь никогда не возвращать [больше чем 1 118] объекты и перемещаясь в следующий объект в последовательности после каждого повторения (но закороченный, если количество объектов, к которым приводят, превышает размер блока).

, Если не будет никаких оставленных объектов, то InternalChunk метод сделает другую передачу во внешнем цикле, но когда MoveNext будет назван во второй раз, это все еще возвратит false, согласно документации (шахта акцента):

, Если MoveNext передает конец набора, перечислитель расположен после того, как последний элемент в наборе и MoveNext возвращает false. , Когда перечислитель в этом положении, последующие вызовы к MoveNext также возвращают false, пока Сброс не называют.

На данном этапе цикл повредится, и последовательность последовательностей завершится.

Это - простой тест:

static void Main()
{
    string s = "agewpsqfxyimc";

    int count = 0;

    // Group by three.
    foreach (IEnumerable<char> g in s.Chunk(3))
    {
        // Print out the group.
        Console.Write("Group: {0} - ", ++count);

        // Print the items.
        foreach (char c in g)
        {
            // Print the item.
            Console.Write(c + ", ");
        }

        // Finish the line.
        Console.WriteLine();
    }
}

Вывод:

Group: 1 - a, g, e,
Group: 2 - w, p, s,
Group: 3 - q, f, x,
Group: 4 - y, i, m,
Group: 5 - c,

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

Кроме того, это сделает странные вещи, если Вы будете играть с порядком, так же, как [1 129], Sam сделал однажды .

65
ответ дан Community 23 November 2019 в 00:15
поделиться

Проверьте это! У меня есть список элементов со счетчиком последовательности и датой. В течение каждого раза перезапуски последовательности я хочу создать новый список.

Напр. список сообщений.

 List<dynamic> messages = new List<dynamic>
        {
            new { FcntUp = 101, CommTimestamp = "2019-01-01 00:00:01" },
            new { FcntUp = 102, CommTimestamp = "2019-01-01 00:00:02" },
            new { FcntUp = 103, CommTimestamp = "2019-01-01 00:00:03" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:04" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:05" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:06" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:07" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:08" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:09" }
        };

я хочу разделить список на отдельные списки как встречные перезапуски. Вот код:

var arraylist = new List<List<dynamic>>();

        List<dynamic> messages = new List<dynamic>
        {
            new { FcntUp = 101, CommTimestamp = "2019-01-01 00:00:01" },
            new { FcntUp = 102, CommTimestamp = "2019-01-01 00:00:02" },
            new { FcntUp = 103, CommTimestamp = "2019-01-01 00:00:03" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:04" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:05" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:06" },

            //restart of sequence
            new { FcntUp = 1, CommTimestamp = "2019-01-01 00:00:07" },
            new { FcntUp = 2, CommTimestamp = "2019-01-01 00:00:08" },
            new { FcntUp = 3, CommTimestamp = "2019-01-01 00:00:09" }
        };

        //group by FcntUp and CommTimestamp
        var query = messages.GroupBy(x => new { x.FcntUp, x.CommTimestamp });

        //declare the current item
        dynamic currentItem = null;

        //declare the list of ranges
        List<dynamic> range = null;

        //loop through the sorted list
        foreach (var item in query)
        {
            //check if start of new range
            if (currentItem == null || item.Key.FcntUp < currentItem.Key.FcntUp)
            {
                //create a new list if the FcntUp starts on a new range
                range = new List<dynamic>();

                //add the list to the parent list
                arraylist.Add(range);
            }

            //add the item to the sublist
            range.Add(item);

            //set the current item
            currentItem = item;
        }
0
ответ дан 23 November 2019 в 00:15
поделиться
public static List<List<T>> GetSplitItemsList<T>(List<T> originalItemsList, short number)
    {
        var listGroup = new List<List<T>>();
        int j = number;
        for (int i = 0; i < originalItemsList.Count; i += number)
        {
            var cList = originalItemsList.Take(j).Skip(i).ToList();
            j += number;
            listGroup.Add(cList);
        }
        return listGroup;
    }
0
ответ дан 23 November 2019 в 00:15
поделиться

Мы обнаружили, что решение Дэвида Б работает лучше всего. Но мы адаптировали его к более общему решению:

list.GroupBy(item => item.SomeProperty) 
   .Select(group => new List<T>(group)) 
   .ToArray();
4
ответ дан 23 November 2019 в 00:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: