Cómo seleccionar ciertas propiedades en una lista

Actualmente, estoy trabajando en un requisito que utiliza una Lista y recupera los datos. Pero hay cambios que necesito para incorporar lo que no estoy recibiendo.

Ahora estoy teniendo una lista de elementos y en la selección de usuarios, recibiría los nombres de columna (aleadamente) que son parte de esa lista.
Cómo implemento porque en tiempo de ejecución no sé cuántos nombres de columna recibiría.

A continuación se muestra el código de la muestra:

class Program
    {
        static void Main(string[] args)
        {
            var students = new List()
            {
                new Student { Name = "Vijay", Age = 21 , Gender = "M" , Address = "Address_1"},
                new Student { Name = "Ajay", Age = 26 , Gender = "M" , Address = "Address_2"},
                new Student { Name = "John", Age = 21 , Gender = "M" , Address = "Address_3"},
                new Student { Name = "Rob", Age = 42 , Gender = "M" , Address = "Address_4"},
                new Student { Name = "Kohli", Age = 32 , Gender = "M" , Address = "Address_5"}
            };

            var result = students.Select(x => x.Name);  
            // I get result with Name column. That's fine.
            
        }
    }


    public class Student
    {
        public string Name;
        public int Age;
        public string Gender;
        public string Address;
    }

El código anterior es sólo una muestra de lo que estoy tratando de explicar. Desde students.Select(x => x.Name); me daría el resultado que tiene una lista Names. Pero algunas veces podría recibir nombres de columna separados como Name, Gender o Age, Gender o Name, Address o Address o todos los nombres de columna.

Expected results:

// When user sends Name & Gender as selected columns
Name, Gender

vijay 26
Ajay  21
Rob   42
...

// When user sends only Address as selected columns
Address

Address_1
Address_2
....

¿Puede alguien ayudarme por favor? select usando la lista disponible.

Gracias por adelantado

Pregunta hecha hace 3 años, 4 meses, 29 días - Por codealchemyf00f


4 Respuestas:

  • Dynamic Linq puede ayudarte.

    using System.Linq.Dynamic.Core;
    
    var values = new List { "Age", "Gender" };
    
    var columns = "new {" + string.Join(",", values) + "}";
    
    var result = students.AsQueryable().Select(columns);
    
    foreach (var x in result)
        Console.WriteLine(x);
    

    Producto:

    { Age = 21, Gender = M }
    { Age = 26, Gender = M }
    { Age = 21, Gender = M }
    { Age = 42, Gender = M }
    { Age = 32, Gender = M }
    

    Tenga en cuenta que está recibiendo un tipo anónimo. Y cómo trabajarás con ella aún es una pregunta.

    Respondida el Dec 18, 2020 a las 03:04 - por devdynamox

    Votos positivos: 0 | Votos negativos: 0

  • En realidad hice esto un tiempo atrás para una API web. Básicamente, puedes usar Newtonsoft. Json para serializar su tipo mientras mantiene sólo los campos \properties que desea.

    Primero, necesitarás una costumbre ContractResolver:

        public class SelectiveContractResolver : DefaultContractResolver
        {
            private readonly HashSet properties;
    
            public SelectiveContractResolver(HashSet selectedProperties)
            {
                properties = selectedProperties;
            }
    
            protected override JsonProperty CreateProperty
                (
                    MemberInfo member,
                    MemberSerialization memberSerialization
                )
            {
                var property = base.CreateProperty(member, memberSerialization);
    
                property.ShouldSerialize = _ =>
                    properties
                        .Contains(
                            $"{member.ReflectedType.FullName.ToString()}.{member.Name}"
                            .ToLower());
    
                return property;
            }
        }
    
    

    Aquí formo específicamente mis cuerdas como "[el camino completo va aquí].Student.Name" o "[same aquí]. Estudiante.Gender" y comprobar si la propiedad fue seleccionada. Esto podría ser útil si vas a serializar tipos complejos y necesitarás alimentar selecciones para diferentes tipos a tu Resolver.

    Lo segundo que hay que hacer, y esto es puramente para la optimización, crear una fábrica que anule sus resolvers, por lo que no tienes que reconstruirlos cada vez:

        public class SelectiveContractResolverFactory
        {
            private ConcurrentDictionary, IContractResolver>
                cachedResolvers;
    
            public SelectiveContractResolverFactory()
            {
                cachedResolvers =
                    new ConcurrentDictionary, IContractResolver>
                        (HashSet.CreateSetComparer());
            }
    
            public IContractResolver GetResolver
                (
                    IEnumerable selectedProperties
                )
            {
                var selectedSet = selectedProperties.ToHashSet();
    
                if (cachedResolvers.ContainsKey(selectedSet))
                    return cachedResolvers[selectedSet];
    
                var newResolver = new SelectiveContractResolver(selectedSet);
                cachedResolvers[selectedSet] = newResolver;
    
                return newResolver;
            }
        }
    

    Finalmente, vamos a aplicar esa cosa a un objeto:

    var student = new Student { Name = "Vijay", Age = 21 , Gender = "M" , Address = "Address_1"};
    
    var selectedProps = new List {"Student.Name","Student.Age"};
    
    // Obviously, this should be injected as a singleton:
    var resolverFactory = new SelectiveContractResolverFactory();
    
    var resolver = resolverFactory.GetResolver(selectedProps);
    
    var selectedResult = JsonConvert.SerializeObject
                         (
                             student,
                             Formatting.None,
                             new JsonSerializerSettings
                             {
                                 ContractResolver = resolver
                             }
                         );
    

    Y así, tienes un objeto json limpio con las propiedades que querías.

    Pero, si tienes que conseguir una realidad C# objeto fuera de esto, entonces usted puede deserializar en un ExpandoObject utilizando ExpandoObjectConverter:

    dynamic filteredStudent = JsonConvert
        .DeserializeObject
        (
            selectedResult,
            new ExpandoObjectConverter()
        );
    

    Aunque, la forma en que se expresa la pregunta, siento que vas a devolver estos datos de una API de tipo, así que json ya sería perfecto para esto.

    Respondida el Dec 18, 2020 a las 03:10 - por coderchampion

    Votos positivos: 0 | Votos negativos: 0

  • En lugar de utilizar Select directamente con el nombre de la propiedad, puedes utilizar una lista dinámica para especificar las propiedades que deseas seleccionar en tiempo de ejecución. A continuación, te muestro un ejemplo de cómo puedes lograr esto:

    csharp
    class Program
    {
        static void Main(string[] args)
        {
            var students = new List()
            {
                new Student { Name = "Vijay", Age = 21, Gender = "M", Address = "Address_1"},
                new Student { Name = "Ajay", Age = 26, Gender = "M", Address = "Address_2"},
                new Student { Name = "John", Age = 21, Gender = "M", Address = "Address_3"},
                new Student { Name = "Rob", Age = 42, Gender = "M", Address = "Address_4"},
                new Student { Name = "Kohli", Age = 32, Gender = "M", Address = "Address_5"}
            };
    
            List selectedColumns = new List { "Name", "Gender" }; // Puedes modificar esta lista según las columnas que necesites
    
            foreach (var student in students)
            {
                string result = string.Join(" ", selectedColumns.Select(col => typeof(Student).GetProperty(col).GetValue(student)));
                Console.WriteLine(result);
            }
    
        }
    }
    
    public class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Gender { get; set; }
        public string Address { get; set; }
    }
    

    En este ejemplo, he agregado una lista selectedColumns que contiene los nombres de las propiedades que deseas seleccionar dinámicamente. Luego, en el bucle foreach, obtengo los valores de las propiedades correspondientes utilizando reflexión, y finalmente imprimo la fila resultante.

    Es importante tener en cuenta que debes validar que los nombres de las columnas proporcionados sean válidos y que las propiedades correspondientes existan en la clase Student.

    Respondida el Dec 19, 2020 a las 02:59 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Claro, puedes utilizar la función dynamic para seleccionar dinámicamente propiedades de una lista, incluso si no conoces los nombres de las propiedades en tiempo de compilación. Aquí tienes un ejemplo modificado de tu código:

    csharp
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var students = new List()
            {
                new Student { Name = "Vijay", Age = 21 , Gender = "M" , Address = "Address_1"},
                new Student { Name = "Ajay", Age = 26 , Gender = "M" , Address = "Address_2"},
                new Student { Name = "John", Age = 21 , Gender = "M" , Address = "Address_3"},
                new Student { Name = "Rob", Age = 42 , Gender = "M" , Address = "Address_4"},
                new Student { Name = "Kohli", Age = 32 , Gender = "M" , Address = "Address_5"}
            };
    
            // Obtén los nombres de columna seleccionados del usuario
            var selectedColumns = new List { "Name", "Gender" };
    
            // Crea una lista de propiedades dinámicas
            var dynamicProperties = new List();
            foreach (var student in students)
            {
                dynamic dynamicStudent = new ExpandoObject();
                foreach (var columnName in selectedColumns)
                {
                    // Obtiene el valor de la propiedad mediante reflexión
                    dynamicStudent.AddProperty(columnName, student.GetType().GetProperty(columnName).GetValue(student, null));
                }
                dynamicProperties.Add(dynamicStudent);
            }
    
            // Imprime los resultados
            foreach (var dynamicStudent in dynamicProperties)
            {
                foreach (var property in dynamicStudent)
                {
                    Console.Write($"{property.Key}: {property.Value} ");
                }
                Console.WriteLine();
            }
        }
    
        public class Student
        {
            public string Name;
            public int Age;
            public string Gender;
            public string Address;
        }
    }
    

    Este código utiliza el tipo dynamic para crear objetos dinámicos que pueden contener propiedades con nombres arbitrarios. Al iterar sobre los elementos de la lista, el código obtiene dinámicamente los valores de las propiedades especificadas en la lista selectedColumns mediante reflexión.

    Ten en cuenta que este enfoque solo funciona si los nombres de las propiedades coinciden con los nombres de las columnas. Si los nombres de las columnas son diferentes, tendrás que utilizar un mapeo entre los nombres de las columnas y los nombres de las propiedades.

    Respondida el Dec 19, 2020 a las 03:05 - por Gemini

    Votos positivos: 0 | Votos negativos: 0