Выберите несколько полей из списка в Linq

128

В ASP.NET C # у меня есть структура:

public struct Data
{
    public int item1;
    public int item2;
    public int category_id;
    public string category_name;
}

и у меня есть их список. Я хочу , чтобы выбрать category_idи category_name, работает DISTINCTи , наконец, ORDERBYна category_name.

Вот что у меня есть сейчас:

List<Data> listObject = getData();
string[] catNames = listObject
                    .Select(i=> i.category_name)
                    .Distinct()
                    .OrderByDescending(s => s)
                    .ToArray();

Очевидно, это просто получает название категории. У меня вопрос: как мне получить несколько полей и в какой структуре данных я буду хранить их (а не в string[])?

РЕДАКТИРОВАТЬ

Использование списка структур не является камнем преткновения. Если было бы целесообразно изменить структуру резервных данных, чтобы упростить выбор (я напишу много таких), я с радостью приму рекомендации.

Chet
источник
10
Хотя это не связано со стороной LINQ, я настоятельно рекомендую вам не использовать изменяемые структуры или общедоступные поля. Лично я вообще редко создаю структуры, но изменяемые структуры просто вызывают проблемы.
Джон Скит,
@Jon Skeet Спасибо. Я конвертирую его в обычный класс с закрытыми членами.
Chet
1
@Midhat: Мутабельные структуры вызывают всевозможные проблемы, поскольку они не ведут себя так, как от них ожидают. А публичные поля дают полное отсутствие инкапсуляции.
Джон Скит
@ Джон Скит. Не могли бы вы подробнее рассказать о подводных камнях изменяемых структур или указать мне на чтение.
Midhat
2
@Midhat: Взгляните на stackoverflow.com/questions/441309/why-are-mutable-structs-evil в качестве отправной точки.
Джон Скит

Ответы:

229

Анонимные типы позволяют вам выбирать произвольные поля в структурах данных, которые строго типизированы позже в вашем коде:

var cats = listObject
    .Select(i => new { i.category_id, i.category_name })
    .Distinct()
    .OrderByDescending(i => i.category_name)
    .ToArray();

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

Data[] cats = listObject
    .GroupBy(i => new { i.category_id, i.category_name })
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();
Джейсон
источник
Я хочу использовать отдельный столбец для одного и получить несколько столбцов. Как мне это сделать?
Кишан Гаджар
1
Никогда не думал о том, чтобы Selectстать новым типом. В моем случае я выбрал новый KeyValuePair.
cjbarth 06
26
var selectedCategories =
    from value in
        (from data in listObject
        orderby data.category_name descending
        select new { ID = data.category_id, Name = data.category_name })
    group value by value.Name into g
    select g.First();

foreach (var category in selectedCategories) Console.WriteLine(category);

Изменить : Сделано больше LINQ-ey!

IRBMe
источник
Ха-ха, спасибо, я ценю помощь. Видимо, это был не самый сложный вопрос в мире.
Chet,
@IRBMe Простите, небольшой вопрос. Что такое «ценность»?
Фернандо С. Крус
23

Вы можете использовать анонимный тип:

.Select(i => new { i.name, i.category_name })

Компилятор генерирует код для класса с nameи category_nameсвойством и возвращают экземпляры этого класса. Вы также можете вручную указать имена свойств:

i => new { Id = i.category_id, Name = i.category_name }

Вы можете иметь произвольное количество свойств.

Мехрдад Афшари
источник
6

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

var items = listObject.Select(f => new List<int>() { f.Item1, f.Item2 }).SelectMany(item => item).Distinct();

Я думаю это решит твою проблему

РУКА
источник
5

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

Синтаксис имеет следующий вид:

new { Property1 = value1, Property2 = value2, ... }

В вашем случае попробуйте следующее:

var listObject = getData();
var catNames = listObject.Select(i =>
    new { CatName = i.category_name, Item1 = i.item1, Item2 = i.item2 })
    .Distinct().OrderByDescending(s => s).ToArray();
нолдорин
источник
4
var result = listObject.Select( i => new{ i.category_name, i.category_id } )

При этом используются анонимные типы, поэтому вы должны использовать ключевое слово var, поскольку результирующий тип выражения заранее не известен.

Пол ван Бренк
источник
3
(from i in list
 select new { i.category_id, i.category_name })
 .Distinct()
 .OrderBy(i => i.category_name);
Джо Чанг
источник
3

Вы можете сделать его KeyValuePair, чтобы он возвращал "IEnumerable<KeyValuePair<string, string>>"

Итак, это будет так:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();
Виктор
источник
Это добавляет излишнюю сложность без пользы
Matze
2
public class Student
{
    public string Name { set; get; }
    public int ID { set; get; }
}

class Program
{
  static void Main(string[] args)
    {
        Student[] students =
        {
        new Student { Name="zoyeb" , ID=1},
        new Student { Name="Siddiq" , ID=2},
        new Student { Name="sam" , ID=3},
        new Student { Name="james" , ID=4},
        new Student { Name="sonia" , ID=5}
        };

        var studentCollection = from s in students select new { s.ID , s.Name};

        foreach (var student in studentCollection)
        {
            Console.WriteLine(student.Name);
            Console.WriteLine(student.ID);
        }
    }
}
Зоеб Шейх
источник