Wählen Sie in Linq mehrere Felder aus Liste aus

128

In ASP.NET C # habe ich eine Struktur:

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

und ich habe eine Liste davon. Ich möchte auswählen category_idund category_name, läuft ein DISTINCTund schließlich eine ORDERBYauf category_name.

Folgendes habe ich jetzt:

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

Dies erhält offensichtlich nur den Kategorienamen. Meine Frage ist, wie bekomme ich mehrere Felder und in welcher Datenstruktur speichere ich diese (nicht a string[])?

BEARBEITEN

Die Verwendung einer Liste von Strukturen ist nicht in Stein gemeißelt. Wenn es ratsam wäre, meine Hintergrunddatenstruktur zu ändern, um die Auswahl zu vereinfachen (ich werde viele davon schreiben), würde ich gerne Empfehlungen annehmen.

Chet
quelle
10
Obwohl es nichts mit der LINQ-Seite zu tun hat , würde ich Ihnen dringend raten, keine veränderlichen Strukturen oder öffentlichen Felder zu verwenden. Persönlich erstelle ich selten Strukturen, aber veränderbare Strukturen verlangen nur nach Ärger.
Jon Skeet
@ Jon Skeet Danke. Ich werde es in eine reguläre Klasse mit privaten Mitgliedern umwandeln.
Chet
1
@Midhat: Veränderbare Strukturen verursachen alle Arten von Problemen, da sie sich nicht so verhalten, wie es die Leute erwarten. Und öffentliche Felder führen zu einem völligen Mangel an Kapselung.
Jon Skeet
@ Jon Skeet. Können Sie die Fallstricke veränderlicher Strukturen genauer beschreiben oder mich auf eine Lesung hinweisen?
Midhat
2
@Midhat: Schauen Sie sich stackoverflow.com/questions/441309/why-are-mutable-structs-evil an, um einen Ausgangspunkt zu finden.
Jon Skeet

Antworten:

227

Mit anonymen Typen können Sie beliebige Felder in Datenstrukturen auswählen, die später in Ihrem Code stark typisiert werden:

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

Da Sie es (anscheinend) zur späteren Verwendung speichern müssen, können Sie den GroupBy-Operator verwenden:

Data[] cats = listObject
    .GroupBy(i => new { i.category_id, i.category_name })
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();
Jason
quelle
Ich möchte für eine Spalte eindeutig verwenden und mehrere Spalten abrufen. Wie kann ich das tun?
Kishan Gajjar
1
Ich hätte nie gedacht, Selectin einen neuen Typ. In meinem Fall habe ich eine neue ausgewählt 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);

Edit : Mehr LINQ-ey!

IRBMe
quelle
Haha, danke, ich weiß die Hilfe zu schätzen. Anscheinend war dies nicht die schwierigste Frage der Welt.
Chet
@IRBMe Entschuldigung, kleine Frage. Was ist genau "Wert"?
Fernando S. Kroes
23

Sie könnten einen anonymen Typ verwenden:

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

Der Compiler generiert den Code für eine Klasse mit nameund category_nameEigenschaften und gibt Instanzen dieser Klasse zurück. Sie können Eigenschaftsnamen auch manuell angeben:

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

Sie können eine beliebige Anzahl von Eigenschaften haben.

Mehrdad Afshari
quelle
6

Sie können mit linq Select mehrere Felder auswählen, wie oben in verschiedenen Beispielen gezeigt. Dies wird als anonymer Typ zurückgegeben. Wenn Sie diesen anonymen Typ vermeiden möchten, ist hier der einfache Trick.

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

Ich denke, das löst dein Problem

AR M.
quelle
5

Dies ist eine Aufgabe, für die anonyme Typen sehr gut geeignet sind. Sie können Objekte eines Typs zurückgeben, der vom Compiler automatisch erstellt wird und aus der Verwendung abgeleitet wird.

Die Syntax hat folgende Form:

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

Versuchen Sie für Ihren Fall Folgendes:

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
quelle
4
var result = listObject.Select( i => new{ i.category_name, i.category_id } )

Dies verwendet anonyme Typen, daher müssen Sie das Schlüsselwort var verwenden, da der resultierende Typ des Ausdrucks nicht im Voraus bekannt ist.

Paul van Brenk
quelle
3
(from i in list
 select new { i.category_id, i.category_name })
 .Distinct()
 .OrderBy(i => i.category_name);
Joe Chung
quelle
3

Sie können es zu einem KeyValuePair machen, sodass a zurückgegeben wird "IEnumerable<KeyValuePair<string, string>>"

Also wird es so sein:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();
Sieger
quelle
Dies fügt unnötige Komplexität ohne Vorteil hinzu
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
quelle