CodeGym /Courses /C# SELF /Projecting Data with Selec...

Projecting Data with Select

C# SELF
Level 31, Lesson 3
Available

1. Introduction

When you watch a TV show, you don't always get the full info about every character—just what you need for the story. In programming, it's kinda the same: you don't always need the whole object, just some fields, values, or maybe some calculations based on them.

Projection in LINQ is all about transforming elements from the original sequence into a new shape. Usually, you do this with the Select method. You can think of Select as a magic machine that takes each element, applies a function to it, and spits out the result. That's it—no tricks.

When do you need projection?

  • You wanna show just user names, not all their data.
  • Prepping an email blast: from the client object, you only need the address and name.
  • Doing some math: add tax to a product's price and get a new collection.
  • For the UI: only show part of the data, so you don't overload the interface.

2. The Select Method: Syntax and Basics

Basic Syntax

LINQ supports two styles: Method Syntax and Query Syntax. For Select, both styles give you the same result.

Method Syntax

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

Here, x is your variable for each element in the collection, and on the right is the expression that turns the element into whatever you want.

Query Syntax

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

This one looks a bit more like SQL.

Simple Example: List of Numbers

var numbers = new List<int> { 1, 2, 3, 4, 5 };
// I want their squares
var squares = numbers.Select(n => n * n);

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

See? We just turned a collection of numbers into their squares—instantly, no manual loop needed!

Example 2

In earlier examples, we already had a Product class:

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

Let's say we have a list:

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

If you just want all the product names, you don't need the whole object, right? You just want a list of names:

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

3. What Kind of Collection Does It Return?

Heads up: Select always returns a collection (technically, IEnumerable<TOutput>), where TOutput is whatever type your function returns. You decide what that is: string, number, anonymous type, even a new object.

Projecting to a New Type

For example, you want not the raw object, but its "projection":

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

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

Here we made a new collection of anonymous objects—with the name and price including some imaginary VAT!

4. Returning a New Class or Anonymous Type

You can choose: make full-blown classes for the result, or just use anonymous types if you only need the structure "right here, right now."

Anonymous Types

var result = products.Select(p => new { Title = p.Name, OldPrice = p.Price, NewPrice = p.Price + 40 });
foreach (var p in result)
{
    Console.WriteLine($"{p.Title}: Was {p.OldPrice}, now {p.NewPrice}");
}

Anonymous types are super handy for quickly shaping results, especially when there's no point in making a class for just one operation.

Using Your Own Class for Projection

Let's make a new class:

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

Now inside the LINQ query:

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. Accessing Inner Collections and Properties

What if there's a collection inside your object? Like, each user has a list of purchases.

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

You wanna get a list of all the purchase lists? No problem!

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

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

Important! In this case, you get not a "flat" list of products, but a collection of collections. How do you get ONE list with all products from all users? That's for the next lecture—where we'll meet SelectMany.

6. Handy Tips

Converting to a Collection Type: ToList() and Friends

Select returns an IEnumerable<T>. If you want a regular list (List<T>), just add .ToList():

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

Now you've got a full-on list you can use however you like.

Using the Element Index

Sometimes it's useful to know which element in the collection you're processing. The Select method has an overload for that:

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

This is great for prepping data for numbered lists, for example.

Projection in Programming

Being good with Select is a common interview question. It's hard to imagine modern apps without it:

  • Picking just the data you need from a database or external service.
  • Prepping data to send between app layers.
  • Transforming data into a format that's good for showing on screen or sending over the network.
  • Saving memory (you're not dragging the whole object collection, just the fields you need).

Companies working with big data often build their core business logic around this.

7. Common Mistakes and Gotchas

Sometimes newbies run into annoying bugs—usually because they don't get how deferred execution (deferred execution) works in LINQ. For example, after calling Select, the query doesn't always run right away—only when you actually iterate over the collection (foreach, ToList(), etc.). This lets you build a "processing pipeline," but sometimes it means your result changes if the source data was changed before you used the query result.

Also, don't forget that Select doesn't change the original collection—it builds a new one. So if you're still expecting to find new fields in products after products.Select(...), that's not gonna happen.

Handling null: if your projection might hit potentially null fields, don't forget to handle that. In C# 8+, you'll get a compiler warning if you might get a NullReferenceException.

2
Task
C# SELF, level 31, lesson 3
Locked
Projecting objects with calculated values
Projecting objects with calculated values
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION