Sélectionnez plusieurs champs dans la liste dans Linq


128

Dans ASP.NET C #, j'ai une structure:

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

et j'en ai une liste. Je veux choisir category_idet category_name, en cours d' exécution d' un DISTINCTet enfin un ORDERBYsur category_name.

Voici ce que j'ai maintenant:

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

Cela n'a évidemment que le nom de la catégorie. Ma question est la suivante: comment puis-je obtenir plusieurs champs et dans quelle structure de données vais-je les stocker (et non dans une string[])?

ÉDITER

L'utilisation d'une liste de structures n'est pas gravée dans la pierre. S'il serait souhaitable de modifier la structure de mes données de sauvegarde pour faciliter les sélections (j'en écrirai beaucoup), je serais ravi de prendre des recommandations.


10
Bien que cela ne soit pas lié au côté LINQ, je vous déconseille fortement d'utiliser des structures mutables ou des champs publics. Personnellement, je crée rarement des structures en premier lieu, mais les structures mutables ne demandent que des problèmes.
Jon Skeet le

@Jon Skeet Merci. Je vais le convertir en un cours régulier avec des membres privés.
Chet

1
@Midhat: Les structures mutables causent toutes sortes de problèmes, car elles ne se comportent pas comme les gens l'attendent. Et les champs publics donnent un manque total d'encapsulation.
Jon Skeet

@Jon Skeet. Pouvez-vous être plus précis avec les pièges des structures mutables, ou indiquez-moi une lecture.
Midhat

2
@Midhat: jetez un œil à stackoverflow.com/questions/441309/why-are-mutable-structs-evil pour un point de départ.
Jon Skeet

Réponses:


228

Les types anonymes vous permettent de sélectionner des champs arbitraires dans des structures de données qui sont fortement typées plus tard dans votre code:

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

Puisque vous devez (apparemment) le stocker pour une utilisation ultérieure, vous pouvez utiliser l'opérateur GroupBy:

Data[] cats = listObject
    .GroupBy(i => new { i.category_id, i.category_name })
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();

Je veux utiliser distinct sur 1 colonne et récupérer plusieurs colonnes, alors comment puis-je le faire?
Kishan Gajjar

1
Je n'ai jamais pensé à Selectun nouveau type. Dans mon cas, j'ai sélectionné dans un nouveau 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 : fait plus LINQ-ey!


Haha, merci, eh bien, j'apprécie l'aide. Apparemment, ce n'était pas la question la plus difficile au monde.
Chet

@IRBMe Excusez-moi, petite question. Quelle est exactement la «valeur»?
Fernando S. Kroes

23

Vous pouvez utiliser un type anonyme:

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

Le compilateur va générer le code pour une classe nameet category_nameinstances de propriétés et rendements de cette catégorie. Vous pouvez également spécifier manuellement les noms de propriétés:

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

Vous pouvez avoir un nombre arbitraire de propriétés.


6

Vous pouvez sélectionner plusieurs champs à l'aide de linq Select comme indiqué ci-dessus dans divers exemples, cela retournera comme un type anonyme. Si vous voulez éviter ce type anonyme, voici une astuce simple.

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

Je pense que cela résout ton problème


5

C'est une tâche pour laquelle les types anonymes sont très bien adaptés. Vous pouvez renvoyer des objets d'un type créé automatiquement par le compilateur, déduit de l'utilisation.

La syntaxe est de cette forme:

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

Pour votre cas, essayez quelque chose comme ce qui suit:

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 } )

Cela utilise des types anonymes, vous devez donc utiliser le mot-clé var, car le type résultant de l'expression n'est pas connu à l'avance.


3
(from i in list
 select new { i.category_id, i.category_name })
 .Distinct()
 .OrderBy(i => i.category_name);

3

Vous pouvez en faire un KeyValuePair, il renverra donc un "IEnumerable<KeyValuePair<string, string>>"

Donc, ce sera comme ça:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();

Cela ajoute une complexité inutile sans avantage
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);
        }
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.