C# and .NET Basics

LINQ: Selecting, Ordering, & Filtering

Spencer Schneidenbach

Spencer Schneidenbach

Aviron Software, Microsoft MVP
C# and .NET Basics

Check out a free preview of the full C# and .NET Basics course

The "LINQ: Selecting, Ordering, & Filtering" Lesson is part of the full, C# and .NET Basics course featured in this preview video. Here's what you'd learn in this lesson:

Spencer demonstrates creating a collection of employees and then shows how to use LINQ methods such as Select, Where, OrderBy, and Aggregate to manipulate and query the collection. He also explains the difference between methods like First, Single, and SingleOrDefault, and provides examples of how to use them. Additionally, he mentions other LINQ methods like Sum, Max, Min, and Average for performing calculations on collections of numbers.

Preview
Close

Transcript from the "LINQ: Selecting, Ordering, & Filtering" Lesson

[00:00:00]
>> Spencer Schneidenbach: All right, I'm gonna go ahead and demonstrate some of the link features. So I'm gonna say prop string Name, and I am going to say prop int Salary, okay? And we'll do FirstName and LastName, okay? And then I'm gonna create a collection of employees, var employees, so I'm gonna say, new list.

[00:00:26]
We'll just go with the list of employee. And I'm gonna go ahead and fill this list with some new employees. So I'm gonna say, FirstName = ''Spencer'', LastName = "George", Salary = 100,000. Kinda go with the example that we had before. We'll copy through these. We'll make some more salaries, some more different salaries, and 1444, and we'll go, Albert,
>> Spencer Schneidenbach: And Amy, and Grant, and Snape.

[00:01:07]
[LAUGH] All right, and Micah, all right. So now we wanna operate on this collection. So, first things first, we're gonna look at select, select is our way of kinda taking a collection and transforming it down into its component pieces. So I'm gonna just start by collecting, maybe I just want a distinct list of last names in this list.

[00:01:30]
So these are the things you can do. So you can go var distinctLastNames. You can say, employees.Select. You give it the e, this is where that lambda function comes in. E is the argument to that function, and it will be of our employee object. We do our equals sign, it's the equals and greater than sign that the pronounce goes into.

[00:01:54]
So we do Select(e) goes into e.LastName, okay? So now we've transformed this list of employees, if we look here, we now have a list of IEnumerable, of string. It says, T is string. So I can see that by saying IEnumerable string> distinctLastNames, e of string. So, we wanna say, well, what do we want?

[00:02:21]
We want the distinct elements of this collection. At this point, if we spin through this,
>> Spencer Schneidenbach: We're just gonna get, Console.WriteLine, we're gonna get each of those names, we haven't really done anything to make them distinct yet. We are gonna get, George, George, George, Albert, George. There's other methods on top of link, you can chain lots of link calls together.

[00:02:47]
So what we would do is we can say Select, and then we can call the extension method called Distinct, which does not take any argument. It basically says, I'm just gonna return to you the distinct elements of this thing that you selected, such that at the end of that, you will just get George and Albert.

[00:03:03]
And we can continue to chain these things together. We can say, Where(e =>), now, where is a special case where, if you look at the function signature, it takes in a funk of employee and returns a bool. So we'll say e, which is an employee parameter, this is a little function, goes into e.Salary, and we wanna return a bool from this.

[00:03:27]
So we need to return some kind of condition. We could return true, it doesn't really do us much good. Or we could say, e.Salary is greater than 50,000, right? So what we're doing is using link to very, very easily express how we want this collection to look. Contrast that with the syntax that we had shown previously at the top of this example.

[00:03:54]
That's a crazy amount of work and a crazy amount of code to get exactly the same thing out. So we can do that by using this, by chaining together our link methods. So we've looked at Select, we've looked at Where, we've looked at Distinct, I wanna show you a little bit of a property.

[00:04:11]
Think of it as a kinda the origin of a record class. Let's say that you just needed last name and first name from this, and you wanted to get the distinct list of names. Let's say there were two Snape Georges, and you just wanted to get their distinct list.

[00:04:28]
So, there's a couple of different ways you can do that. The main way that you do that is declare what's called an anonymous type. So you can see that I am gonna go ahead and change this back to var, because it's going to be interpreted as a string.LastName.

[00:04:41]
So what I've done is I've created an entirely new type without actually declaring a class or a record or a struct. I've declared something completely new, and I can use link to operate on that. So we would say, e.FirstName, and then we could still call distinct on this.

[00:04:58]
One of the cool properties of anonymous types is like records, they're structurally equal. So anything where it's Snape George, Snape George, as long as those are the only properties I selected, it's considered a unique record. And we can see that, if we run this, we can see that we should print, we print three records.

[00:05:22]
So you could see that, yes, and we've filtered out people that have a salary less than 50,000. So we've only gotten these. And if we take this out and run it again, you can see now that we get four names instead of three. So, those are some of the features of link.

[00:05:40]
So we've got Select, where we can select an individual property. We can select a bag of properties using using anonymous type. You can even select a whole new class. You can instantiate a whole class in here, if you wanted to. You could create a person class and instantiate that instead, we can do that, class Person.

[00:06:02]
We'll go ahead and use the primary constructor to save keystrokes.
>> Spencer Schneidenbach: And we can call that right here, new Person, and then we would give it the string FirstName, e.FirstName, e.Last name. So that's an example of some of the functions. Let me talk to you about the ones that I think are most important.

[00:06:30]
So, select, we've talked about that. We've talked about Where, e e.Salary is greater than 50,000. So we've talked about Where, used a filter. You can also chain Wheres, so you can say another wear. You can say e is e.LastName.StartsWith ("S"), in this case, G. So you chain this pretty well infinitely down, as far down as you can go, like I said, in a really nice and expressive way.

[00:07:08]
You can also order by, you can say, OrderBy. And you can just say, I just wanna order by the object itself, which will work because of the nature of anonymous types. You could also say, just order by last name.
>> Spencer Schneidenbach: Let me see, yeah, because I haven't defined property here, create and assign property.

[00:07:29]
Create and assign property. Just let my IDE do all the hard work. So, that in a nutshell is link. Those are probably the most important methods, but there's a few more I do wanna touch on. So up to this point we've kind of operated on just operating on just other collections, but what if you wanna select individual values from this list?

[00:07:59]
There's a couple of different methods that you can use to do that. So if I wanted to select Micah, assuming he was there, if I knew the position that his record was gonna be inside of this, and I often don't, I would use ElementAt or ElementAtOrDefault. And you can think of this like index or syntax inside of an array or a list.

[00:08:22]
But you can basically say, I just want to get the element that exists in this collection. A collection is gonna be up to, in this case, probably 3, 4, 5 records. Most of the time, you're probably not gonna know ahead of time, because you're just operating on a collection with which you don't necessarily know where that thing is going to be.

[00:08:41]
But what you can do is there's four methods that you need to know. We've kinda gone through the projection. We've talked about Select, we've talked about Where, OrderBy, but the four methods that you really need to know are First or FirstOrDefault, and Single or SingleOrDefault. So First, essentially, we'll say, give me the first number in this list.

[00:09:07]
So given this list of values, I want you to give me the first one, and it's going to return the top element of that collection. An empty array, which we've declared, or an empty list here that we call first on is going to throw an exception. Because it says, hey, I expect you to return something to me.

[00:09:28]
I don't care how many elements are in the list, but if you don't return anything to me, a call to First will throw an exception. FirstOrDefault, correspondingly, will return the first element of an array, or it It will return the default value if there is no elements in the array to return.

[00:09:46]
So in this case, it will return the default value of an integer, which is 0. If you had a list of reference types, it would return a null. Say that to Single, which is not going to return the first, it's going to expect there to be exactly one element in this array.

[00:10:04]
That's what it expects in this collection. So, given a single number list, if we call .Single, it's gonna return 42 very happily, whereas in an empty list, it will return an exception. Or in a list, in a collection with more than one element, it's also going to throw an exception.

[00:10:25]
A method called a Single says I want exactly one record, period. And SingleOrDefault is either looking for a collection that has one element or zero. Anything else, it will throw an exception. So, more than one element will throw an exception every time. They can also take an optional predicate.

[00:10:46]
So, let's say for instance, that we just wanted the single value of e.LastName ==, let's see. Or we'll say FirstName, cuz we're looking at Micah.
>> Spencer Schneidenbach: Console.WriteLine(micah), boom.
>> Spencer Schneidenbach: And, open up our terminal, and dotnet run. And we'll see what happens. Okay, so because inside of this collection we filtered out Micah, who has a salary of less than 50,000, we filtered them out, so Single returned nothing.

[00:11:28]
Sorry, it didn't return nothing, it threw an exception. It said, I didn't find any element. Correspondingly, as I mentioned before, SingleOrDefault will return a null value. Which is, yep, it printed it right here, prints as an empty string. So you can do that one of two ways. So, this is an optional argument, you can call SingleOrDefault.

[00:11:50]
And then, if you're calling SingleOrDefault without a predicate, you better be sure that this thing has exactly zero or one elements in this collection, or it's gonna throw. The most of the time when I'm calling single or SingleOrDefault, I'm passing in a predicate. And as I mentioned, if I'm sure that that collection has no other elements in it, I'm gonna be calling SingleOrDefault without the predicate.

[00:12:18]
This comes down to a word of warning, bugs often hide in First or FirstOrDefault. I see this countless times, where a developer says, hey, I just want the first element in this array. This literally happened to me a couple of weeks ago. I had a developer under me who, he fixed a bug by calling First.

[00:12:37]
He said, well, we just need the first element in this array. Not realizing that there were duplicates, and what he was doing was technically invalid. It did get the program to run, but then correspondingly, set the system in an invalid state. Because it said, well, you just wanted the first record, but if you were expecting a single record and there were multiple ones, that can cause problems.

[00:12:57]
So I'm here to come to you with a word of warning, that I do not use First or FirstOrDefault almost ever, if I can use Single or SingleOrDefault. And because they're methods on the same collection, you can see that Single or SingleOrDefault are perfectly valid. And most of the time, when you're looking at a collection and you're trying to get a single value out, those are gonna be the methods that you wanna reach for.

[00:13:22]
>> Spencer Schneidenbach: Questions.
>> Student 1: So, are these kind of meant to be used once you've already filtered through a list to find what you want? Because others, I guess I don't really understand the point of saying Single, you have one element, yes.
>> Spencer Schneidenbach: Yeah, that's a good question. So let's take this example, real quick.

[00:13:47]
Kinda just look at this, let's say that we didn't use this filtered list. Let's say we were actually doing an operation that was just on the list of employees. We can go back and have our call to Single be on this. So t could be, it just depends on the use case, right?

[00:14:02]
It could be that you're filtering down a list and then calling Single. Most of the time you're probably going to know ahead of time if there is a single element in a list or not. But in this case, you can absolutely just call it pre-filtered. And this would be your kind of, filtering predicate.

[00:14:20]
And that's very common to do, especially when you go into a database and you say, I want employee record 1, 2, 3, 4. There's probably, in a well-designed system, there's only gonna be one employee record with an ID of 1, 2, 3, 4. So most of the time you're usually calling SingleOrDefault or Single on non-filtered collections.

[00:14:39]
Good question.
>> Student 2: Is there a method similar to Reduce, from JavaScript?
>> Spencer Schneidenbach: Yes, that is called Aggregate, and yes, you can reduce it. And essentially, it takes a collection and reduces it down to a single value. So if you could have a list of strings, you could call Aggregate, and then get it down to concatenating all those strings together, for instance.

[00:15:01]
Let's see a quick example of that. I'm gonna keep that up there. But if you had a, we have a collection here, we'll just use it. We'll just say, var names, and we say, employees.Select, e goes into e.FirstName. We'll just get the first name here to array. And then you can aggregate, names.Aggregate.

[00:15:26]
And so, you can see that if you hover over it, it does return a string. It takes in a func that takes in two strings and then returns a string. And what you can do is use that as a way to combine all those strings together, for instance.

[00:15:45]
And it's a good way to sum a bunch of numbers. Although there's a sum method that you can just call to sum numbers. So, you don't have to do that, per se. But let's say xy, because we know we're taking a func of func goes into x + y, and then we'll see what it writes.

[00:16:04]
Console.writeLine.
>> Spencer Schneidenbach: You can think of Aggregate as a way to just take a set of values and convert it down to one value. Often used for, as you can imagine, aggregate functions. So you can see that, because all we've done is concatenate, taken all their names and then concatenated them together, that we've aggregated them down to a single string value here.

[00:16:28]
And that value is all of the names put together, Spencer, Snape, Micah, Grant, and Snape. There's a couple of other link methods that you'll definitely wanna know about. So if we go down to here, Aggregate Methods. If you have a collection of numbers and you wanna sum them together, you can take for instance, this list, and then call .sum.

[00:16:51]
And then say, it will sum all of those integer or doubles or decimals for you. There is a max and a mean, that we can use to return the maximum value within an array, or a collection, I mean, or the minimum value, which, in this case, would be 1.

[00:17:10]
And of course, there's an average, which would return the mean. And there is something that you should know about that sum and those aggregate methods, is that if you call them on empty lists, you will get an invalid operation exception. Because you can't sum a collection of numbers that does not exist.

[00:17:27]
So if this is an empty array and you call sum on it, it will throw an exception. You can use the DefaultIfEmpty to actually populate it to say, hey, if this array is empty, then just give me value 0, just default to 0. And that will protect you from getting an exception, throw it if you just wanted to sum the values in array and you don't know ahead of time if there will actually be values in that array.

[00:17:50]
Maybe you're getting salaries from a database and you just wanna sum them all together, but you've added filtering criteria, you only want salaries greater than 300,000. Well, nobody in the company makes that. So that would be a use case for why we would use that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now