CodeGym /Cursos /C# SELF /Proyección de datos usando

Proyección de datos usando Select

C# SELF
Nivel 31 , Lección 3
Disponible

1. Introducción

Cuando ves una serie, no siempre sabes toda la info sobre todos los personajes, solo lo que hace falta para la trama. En programación pasa algo parecido: no siempre necesitas el objeto entero, sino solo algunos de sus campos, valores o incluso algunos cálculos basados en ellos.

Proyección en LINQ es transformar los elementos de una secuencia original en una nueva forma. Normalmente usando el método Select. Puedes pensar en Select como una máquina mágica que toma cada elemento, le aplica una función y te devuelve el resultado. Y ya está, sin trucos raros.

¿Cuándo necesitas una proyección?

  • Quieres mostrar solo los nombres de los usuarios, no todos sus datos.
  • Preparas un email-masivo: del objeto cliente solo coges la dirección y el nombre.
  • Haces un cálculo: al precio del producto le sumas el impuesto y obtienes una nueva colección.
  • Para el UI: solo necesitas mostrar parte de los datos, sin sobrecargar la interfaz.

2. Método Select: sintaxis y uso básico

Sintaxis principal

LINQ soporta dos estilos: sintaxis de método (Method Syntax) y sintaxis de consulta (Query Syntax). Para Select el resultado es igual en ambos estilos.

Sintaxis de método

var query = myCollection.Select(x => x.QueDevolver);

Aquí x es la variable para cada elemento de la colección, y a la derecha tienes la expresión que transforma el elemento en lo que necesitas.

Sintaxis de consulta

var query = from x in myCollection
            select x.QueDevolver;

Aquí se parece un poco más a SQL.

Ejemplo simple: lista de números

var numbers = new List<int> { 1, 2, 3, 4, 5 };
// Quiero obtener sus cuadrados
var squares = numbers.Select(n => n * n);

foreach (var s in squares)
{
    Console.WriteLine(s); // 1, 4, 9, 16, 25
}

Acabamos de transformar una colección de números en una colección de sus cuadrados — al instante, sin usar un bucle manual.

Ejemplo 2

En ejemplos anteriores ya teníamos una clase Product:

public class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
}

Supón que tienes una lista:

var products = new List<Product>
{
    new Product { Name = "Chocolate", Price = 120 },
    new Product { Name = "Queso", Price = 250 },
    new Product { Name = "Pan", Price = 55 }
};

Si solo quieres obtener los nombres de todos los productos, no necesitas el objeto entero, ¿verdad? Solo necesitas la lista de nombres:

var names = products.Select(p => p.Name);
foreach (var name in names)
{
    Console.WriteLine(name); // Chocolate, Queso, Pan
}

3. ¿Qué tipo de colección devuelve?

Ojo, Select siempre devuelve una colección (más bien — IEnumerable<TOutput>), donde TOutput es el tipo de resultado de la función. Tú decides qué será: string, número, tipo anónimo, incluso un objeto nuevo.

Proyección a un nuevo tipo

Por ejemplo, quieres obtener no el objeto original, sino su "proyección":

var projections = products.Select(p => new { p.Name, PrecioConIVA = p.Price * 1.2 });

foreach (var item in projections)
{
    Console.WriteLine($"{item.Name}: {item.PrecioConIVA}");
}

Aquí hemos creado una nueva colección de objetos anónimos — con el nombre y el precio con un IVA imaginario.

4. Devolver una nueva clase o un tipo anónimo

Puedes elegir: crear clases completas para el resultado o usar tipos anónimos si la estructura solo la necesitas "aquí y ahora".

Tipos anónimos

var result = products.Select(p => new { Nombre = p.Name, PrecioAntiguo = p.Price, PrecioNuevo = p.Price + 40 });
foreach (var p in result)
{
    Console.WriteLine($"{p.Nombre}: Antes {p.PrecioAntiguo}, ahora {p.PrecioNuevo}");
}

Los tipos anónimos son súper útiles para formar resultados rápidos, sobre todo cuando no tiene sentido crear clases solo para una operación.

Usando tu propia clase para la proyección

Creamos una nueva clase:

public class ProductDto
{
    public string Name { get; set; }
    public double PriceWithTax { get; set; }
}

Ahora dentro de la consulta LINQ:

var productsWithTax = products.Select(p => new ProductDto
{
    Name = p.Name,
    PriceWithTax = p.Price * 1.2
});

foreach (var p in productsWithTax)
{
    Console.WriteLine($"{p.Name}: {p.PriceWithTax}");
}

5. Acceso a colecciones y propiedades internas

¿Y si dentro del objeto hay una colección? Por ejemplo, cada usuario tiene una lista de compras.

public class User
{
    public string Name { get; set; }
    public List<Product> Purchases { get; set; }
}

¿Quieres obtener la lista de todas las listas de compras? ¡Sin problema!

var allPurchases = users.Select(u => u.Purchases);

foreach (var purchaseList in allPurchases)
{
    foreach (var product in purchaseList)
    {
        Console.WriteLine(product.Name);
    }
}

¡Ojo! En este caso no tienes una lista "plana" de productos, sino una colección de colecciones. ¿Cómo obtener UNA sola lista con todos los productos de todos los usuarios? Eso será tema de la próxima lección — ahí conoceremos SelectMany.

6. Trucos útiles

Convertir a tipo de colección: ToList() y compañía

Select devuelve un IEnumerable<T>. Si quieres una lista normal (List<T>), solo añade .ToList():

var nameList = products.Select(p => p.Name).ToList();

Ahora tienes una lista de verdad, con la que puedes trabajar como siempre.

Usar el índice del elemento

A veces mola saber qué elemento de la colección estás procesando ahora. El método Select tiene una sobrecarga:

var indexed = products.Select((product, index) => new { Index = index, Name = product.Name });
foreach (var item in indexed)
{
    Console.WriteLine($"{item.Index}: {item.Name}");
}

Así puedes, por ejemplo, preparar datos para listas numeradas.

Proyección en programación

Saber usar Select es una pregunta típica en entrevistas. Sin esta construcción es difícil imaginar apps modernas:

  • Seleccionar solo los datos necesarios de la base de datos o servicios externos.
  • Preparar datos para pasar entre capas de la app.
  • Transformar datos en un formato apto para mostrar en pantalla o enviar por red.
  • Ahorro de memoria (no arrastras todo el objeto de la colección, solo los campos que necesitas).

Las empresas que trabajan con grandes volúmenes de datos suelen basar en esto su lógica de negocio principal.

7. Errores típicos y particularidades

A veces los que empiezan se topan con bugs molestos — normalmente por no entender cómo funciona la ejecución diferida (deferred execution) en LINQ. Por ejemplo, después de llamar a Select no siempre se ejecuta la consulta al instante — solo cuando realmente iteras la colección (foreach, ToList(), etc). Esto te permite montar una "tubería de procesamiento", pero a veces hace que el resultado cambie si los datos originales se modifican antes de usar el resultado de la consulta.

Tampoco olvides que Select no modifica la colección original — crea una nueva. Así que si esperas encontrar en products nuevos campos después de products.Select(...), eso no va a pasar.

Manejo de null: si la proyección implica acceder a campos que pueden ser null, no te olvides de tenerlo en cuenta. En C# 8+ el compilador te avisa si puedes tener un NullReferenceException.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION