Seleccione múltiples campos de la lista en Linq

128

En ASP.NET C # tengo una estructura:

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

y tengo una lista de esos. Quiero seleccionar category_idy category_name, ejecutando un DISTINCTy finalmente un ORDERBYencendido category_name.

Esto es lo que tengo ahora:

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

Obviamente, esto solo obtiene el nombre de la categoría. Mi pregunta es, ¿cómo obtengo múltiples campos y en qué estructura de datos almacenaré esto (no a string[])?

EDITAR

Usar una lista de estructuras no está establecido en piedra. Si fuera aconsejable cambiar mi estructura de datos de respaldo para facilitar las selecciones (escribiré muchos de estos), con mucho gusto tomaré recomendaciones.

Chet
fuente
10
Si bien no está relacionado con el lado de LINQ, le recomiendo encarecidamente que no use estructuras mutables o campos públicos. Personalmente, rara vez creo estructuras en primer lugar, pero las estructuras mutables solo piden problemas.
Jon Skeet el
@ Jon Skeet Gracias. Lo convertiré en una clase regular con miembros privados.
Chet
1
@Midhat: Las estructuras mutables causan todo tipo de problemas, ya que no se comportan como la gente espera que lo hagan. Y los campos públicos dan una falta total de encapsulación.
Jon Skeet
@ Jon Skeet. ¿Puede ser más específico con las trampas de las estructuras mutables, o señalarme una lectura?
Midhat
2
@Midhat: Eche un vistazo a stackoverflow.com/questions/441309/why-are-mutable-structs-evil para obtener un punto de partida.
Jon Skeet

Respuestas:

228

Los tipos anónimos le permiten seleccionar campos arbitrarios en estructuras de datos que se tipean más adelante en su código:

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

Dado que (aparentemente) necesita almacenarlo para su uso posterior, puede usar el operador 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
fuente
Quiero usar distinto en 1 columna y recuperar varias columnas. Entonces, ¿cómo puedo hacerlo?
Kishan Gajjar
1
Nunca pensé Selecten un nuevo tipo. En mi caso seleccioné uno nuevo 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);

Editar : lo hizo más LINQ-ey!

IRBMe
fuente
Jaja, gracias, bueno, agradezco la ayuda. Aparentemente, esta no era la pregunta más difícil del mundo.
Chet
@IRBMe Disculpe, pequeña pregunta. ¿Qué es exactamente el "valor"?
Fernando S. Kroes
23

Podrías usar un tipo anónimo:

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

El compilador generará el código para una clase con namey category_namepropiedades y devolverá instancias de esa clase. También puede especificar manualmente los nombres de propiedad:

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

Puede tener un número arbitrario de propiedades.

Mehrdad Afshari
fuente
6

Puede seleccionar múltiples campos usando linq Select como se muestra arriba en varios ejemplos, esto devolverá como un tipo anónimo. Si desea evitar este tipo anónimo, aquí está el simple truco.

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

Creo que esto resuelve tu problema

BRAZO
fuente
5

Esta es una tarea para la que los tipos anónimos son muy adecuados. Puede devolver objetos de un tipo creado automáticamente por el compilador, inferido del uso.

La sintaxis es de esta forma:

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

Para su caso, intente algo como lo siguiente:

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

Utiliza tipos anónimos, por lo que debe usar la palabra clave var, ya que el tipo resultante de la expresión no se conoce de antemano.

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

Puede convertirlo en un KeyValuePair, por lo que devolverá un "IEnumerable<KeyValuePair<string, string>>"

Entonces, será así:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();
Víctor
fuente
Esto agrega complejidad innecesaria sin un beneficio
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
fuente