Wybierz opcję Wiele pól z listy w Linq

128

W ASP.NET C # mam strukturę:

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

i mam ich listę. Chcę wybrać category_idi category_name, prowadzenie DISTINCTi wreszcie ORDERBYna category_name.

Oto, co mam teraz:

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

To oczywiście otrzymuje tylko nazwę kategorii. Moje pytanie brzmi: w jaki sposób mogę uzyskać wiele pól i w jakiej strukturze danych będę to przechowywać (nie a string[])?

EDYTOWAĆ

Korzystanie z listy struktur nie jest nieodłączne. Jeśli byłaby wskazana zmiana struktury danych kopii zapasowej, aby ułatwić wybieranie (będę pisać wiele z nich), z przyjemnością skorzystam z zaleceń.

Chet
źródło
10
Chociaż nie jest to związane ze stroną LINQ, zdecydowanie odradzam używanie struktur zmiennych lub pól publicznych. Osobiście rzadko tworzę struktury w pierwszej kolejności, ale struktury zmienne po prostu proszą o kłopoty.
Jon Skeet
@Jon Skeet Thanks. Przekształcę to w zwykłe zajęcia z prywatnymi członkami.
Chet
1
@Midhat: Zmienne struktury powodują różnego rodzaju problemy, ponieważ nie zachowują się tak, jak oczekują od nich ludzie. A pola publiczne dają całkowity brak hermetyzacji.
Jon Skeet
@Jon Skeet. Czy możesz być bardziej szczegółowy w kwestii pułapek związanych ze zmiennymi strukturami lub wskazać mi lekturę.
Midhat
2
@Midhat: Zajrzyj na stackoverflow.com/questions/441309/why-are-mutable-structs-evil, aby uzyskać punkt wyjścia.
Jon Skeet

Odpowiedzi:

228

Typy anonimowe umożliwiają wybranie dowolnych pól w strukturach danych, które są silnie wpisywane później w kodzie:

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

Ponieważ (najwyraźniej) musisz przechowywać go do późniejszego użytku, możesz użyć operatora GroupBy:

Data[] cats = listObject
    .GroupBy(i => new { i.category_id, i.category_name })
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();
Jason
źródło
Chcę używać odrębnych w 1 kolumnie i pobierać wiele kolumn. Więc jak mogę to zrobić?
Kishan Gajjar
1
Nigdy nie myślałem Selecto nowym typie. W moim przypadku wybrałem nowy KeyValuePair.
cjbarth
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);

Edycja : uczyniłem to bardziej LINQ-ey!

IRBMe
źródło
Haha, dzięki, cóż, doceniam pomoc. Najwyraźniej nie było to najtrudniejsze pytanie na świecie.
Chet
@IRBMe Przepraszam, małe pytanie. Czym dokładnie jest „wartość”?
Fernando S. Kroes
23

Możesz użyć anonimowego typu:

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

Kompilator generowania kodu dla klasie namei category_namewłaściwości i zwraca przypadkach tej klasy. Możesz również ręcznie określić nazwy właściwości:

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

Możesz mieć dowolną liczbę właściwości.

Mehrdad Afshari
źródło
6

Możesz wybrać wiele pól za pomocą linq Select, jak pokazano powyżej w różnych przykładach, zwróci to jako typ anonimowy. Jeśli chcesz uniknąć tego anonimowego typu, oto prosta sztuczka.

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

Myślę, że to rozwiązuje twój problem

RAMIĘ
źródło
5

Jest to zadanie, do którego bardzo dobrze nadają się typy anonimowe . Możesz zwrócić obiekty typu, który jest tworzony automatycznie przez kompilator, wywnioskowany na podstawie użycia.

Składnia ma następującą postać:

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

W swoim przypadku spróbuj czegoś takiego:

var listObject = getData();
var catNames = listObject.Select(i =>
    new { CatName = i.category_name, Item1 = i.item1, Item2 = i.item2 })
    .Distinct().OrderByDescending(s => s).ToArray();
Noldorin
źródło
4
var result = listObject.Select( i => new{ i.category_name, i.category_id } )

Używa to typów anonimowych, więc musisz użyć słowa kluczowego var, ponieważ wynikowy typ wyrażenia nie jest z góry znany.

Paul van Brenk
źródło
3
(from i in list
 select new { i.category_id, i.category_name })
 .Distinct()
 .OrderBy(i => i.category_name);
Joe Chung
źródło
3

Możesz ustawić go jako KeyValuePair, więc zwróci plik "IEnumerable<KeyValuePair<string, string>>"

A więc będzie tak:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();
Zwycięzca
źródło
To dodaje niepotrzebnej złożoności bez korzyści
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);
        }
    }
}
Zoyeb Shaikh
źródło