Использование Linq для группировки списка объектов, содержащего данные примитивов, в новый сгруппированный список объектов - PullRequest
4 голосов
/ 18 апреля 2020

У меня есть список объектов, который содержит различные примитивные типы данных:

List<object> list = new List<object>() { 
  3, "cat", 4.1, 'a', 5, "dog", "horse", 9.1, 'd', 1 
};

Я хочу иметь возможность выполнить запрос Linq в приведенном выше списке, который группирует все элементы по типу данных и порядку как (строка, целое, двойное, символ) (если это имеет смысл?). Примерно так:

list = { "cat","dog", "horse", 3, 5, 1, 4.1, 9.1, 'a', 'd' }

Я пытался из

Использование Linq для группировки списка объектов в новый сгруппированный список списка объектов

код

lst
  .GroupBy(x => x.GetType())
  .Select(grp => grp.ToList())
  .ToList()
  .ForEach(group => group.ForEach(item => Console.WriteLine(item)));

работает, но не дает желаемого выхода.

вывод

 { 3, 5, 1, "cat","dog", "horse",4.1, 9.1, 'a', 'd' }

Ответы [ 4 ]

2 голосов
/ 18 апреля 2020

Вы можете просто заказать имя типа по убыванию после Select, это даст вам желаемый результат - (строковое, целое, двойное, символьное) элементы

list.GroupBy(x => x.GetType())
    .OrderByDescending(g => g.Key.Name)
    .Select(grp => grp.ToList())
    .ToList()
    .ForEach(group => group.ForEach(Console.WriteLine));

Другой вариант - упорядочить по IsPrimitive свойство OrderBy(g => g.Key.IsPrimitive), для ваших конкретных данных оно также дает желаемый результат

2 голосов
/ 18 апреля 2020

Подобный подход к ответу @ Pac0 , вы можете создать метод для определения порядка сортировки по типам:

private static int OrderByOnType(object o)
{
    var type = o.GetType();

    if (type == typeof(string))
        return 0;

    else if (type == typeof(int)) 
        return 1;

    else if (type == typeof(double)) 
        return 2;

    else if (type == typeof(char)) 
        return 3;

    return 4;
}

Затем передать этот метод в Enumerable.OrderBy из LINQ для создания нового отсортированного списка:

var result = list.OrderBy(OrderByOnType);

Console.WriteLine("{ " + string.Join(", ", result) + " }");

Или отсортируйте исходный список на месте с помощью List<T>.Sort, передав свой собственный Comparison<T> Delegate:

list.Sort((x, y) => OrderByOnType(x).CompareTo(OrderByOnType(y)));

Console.WriteLine("{ " + string.Join(", ", list) + " }");

Который также можно обернуть в отдельный метод:

private static int CompareTwoTypes(object x, object y)
{
    return OrderByOnType(x).CompareTo(OrderByOnType(y));
}

list.Sort(CompareTwoTypes);

Console.WriteLine("{ " + string.Join(", ", list) + " }");

Все из которых дают:

{ cat, dog, horse, 3, 5, 1, 4.1, 9.1, a, d }
2 голосов
/ 18 апреля 2020

Если проблема заключается в порядке типов в выводе, вы можете использовать пользовательский класс IComparer и список Sort, который вы перечисляете.

Основной принцип c состоит в том, что вы можете сопоставлять типы с целое число баллов, а затем вернуть сравнение между баллами.

См. документацию .

( dotnetFiddle )

public class StringThenIntThenDoubleThenChar  : Comparer<object> 
{
    public override int Compare(object x, object y)
    {
        return GetTypeScore(x).CompareTo(GetTypeScore(y));
    }

    private int GetTypeScore(object o) 
    {
        var type = o.GetType();
        if (type == typeof(string)) return 0;
        else if (type == typeof(int)) return 1;
        else if (type == typeof(double)) return 2;
        else if (type == typeof(char)) return 3;
        else return 4;

        /* Or, if you are using C# 8 :
        return o.GetType() switch
        {
            Type t when t == typeof(string) => 0,
            Type t when t == typeof(int) => 1,
            Type t when t == typeof(double) => 2,
            Type t when t == typeof(char) => 3,
            _ => 4
        };
        */
    }
}

Используйте это следующим образом:

        List<object> list = new List<object>() { 3, "cat", 4.1, 'a', 5, "dog", "horse", 9.1, 'd', 1 };

        list.Sort(new StringThenIntThenDoubleThenChar());
        list.ForEach(x => Console.WriteLine(x));

Преимущество перед уже предоставленным более простым решением (сортировка по имени типа) состоит в том, что вы можете настроить его по своему усмотрению. sh.

Вы также можете уточнить компаратор, например, если оценки равны, затем вы можете сравнить их, используя их порядок сравнения по умолчанию (так что string, int et c. соответственно отсортированы среди них).

0 голосов
/ 21 апреля 2020

Я думаю, что более короткая версия @ Pac0's и @ RoadRunner ответ заключается в использовании выражения Expression в запросе Linq.

        list.OrderBy(x => x  switch
        {
             string xString=> 0,
             int xInt=> 1,
             double xDouble=> 2,
             char xChar=> 3,
            _ => -1,
        }).ToList().ForEach(item => Console.WriteLine(item));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...