Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

LINQ Basics

0.00/5 (No votes)
11 Jan 2021 1  
Basics of the embedded LINQ language
I will tell you about the basics of embedded LINQ language. It will make it easier for you to work with the C# language. Instead of having to deal with an entirely new set of tools in the form of classes, you can use all the same familiar collections and arrays with existing classes. This means that you can take full advantage of LINQ queries with minimal or no modifications to existing code. LINQ to Objects functionality is provided by the IEnumerable interface, sequences, and standard query operations.

Introduction

Let's take a look at what LINQ is. LINQ is Language-Integrated Query. The data source can be an object (implements the IEnuberable interface, which are standard collections, arrays), an XML document, and a DBSet data set. However, regardless of the data source, LINQ implements the same approach for fetching from that data. In addition, there are many varieties of LINQ:

  • LINQ to Objects: Used to work with arrays and collections
  • LINQ to Entities: Used when accessing databases through Entity Framework technology
  • LINQ to SQL: Data Access Technology in MS SQL Server
  • LINQ to XML: Used when working with XML files
  • LINQ to DataSet: Used when working with a DataSet object
  • Parallel LINQ (PLINQ): Used to execute parallel queries

In this article, I want to talk first of all about the first LINQ language varieties.

Background

First of all, for understanding what it is, I would show you the first instance:

string[] Contries = { "USA", "Canada", "United Kingdom", "Mexico", "China", "Uruguay" };
            var SelectedContr = new List<string>();
            foreach(string c in Contries)
            {
                if (c.StartsWith("U"))
                    SelectedContr.Add(c);
            }
            foreach (string s in SelectedContr)
                Console.WriteLine(s);

In it, we create some data array, and then we loop through it and check the match at each iteration, if this is true, then we write to the collection. What if I say that this code can be shortened several times and the loop in which the condition is checked can be removed? Consider the code written using the LINQ language. To use the LINQ functionality, make sure the System.LINQ namespace is included in the file.

string[] Contries = { "USA", "Canada", "United Kingdom", "Mexico", "China", "Uruguay" };
            var SelectedContr = from C in Contries
                                where c.StartsWith("U")
                                select C;
            foreach (string s in SelectedContr)
                Console.WriteLine(s);

We have the same result, but it is achieved in an easier way. The definition in the language is:

from variable in object_set
select variable;

So what did we do in our example, let's go step by step. The expression from C in Countries defines C as each element of the array and as a result, we can perform various operations with it. MS Visual Studio automatically recognizes that the set c consists of string objects, so the C variable will be treated as a string. This does not mean that LINQ is not strongly typed. Using where, objects are filtered by a certain criterion, but in the case found it starts with the letter “U”. We use the select statement to pass the selected values ​​into the result set that is returned by the LINQ expression.

The advantage of such queries is that they are intuitively similar to SQL queries, although they have some differences. In addition, we have a huge list of extension methods besides from… in… select. Here is the entire list:

  • Select: defines the projection of the selected values
  • Where: defines a selection filter
  • OrderBy: orders items in ascending order
  • OrderByDescending: orders items in descending order
  • ThenBy: sets additional criteria for ordering items in ascending order
  • ThenByDescending: specifies additional criteria for ordering items in descending order
  • Join: joins two collections on a specific basis
  • GroupBy: groups items by key
  • ToLookup: groups items by key, with all items added to the dictionary
  • GroupJoin: performs both collection joining and grouping of items by key
  • Reverse: reverse the order
  • All: determines if all items in the collection meet a specific condition
  • Any: determines if at least one element of the collection meets a certain condition
  • Contains: determines if the collection contains a specific element
  • Distinct: removes duplicate items from a collection
  • Except: returns the difference of two collections, that is, those items that are created in only one collection
  • Union: combines two homogeneous collections
  • Intersect: returns the intersection of two collections, that is, those items that occur in both collections
  • Count: counts the number of elements in the collection that meet a specific condition
  • Sum: calculates the sum of numeric values ​​in a collection
  • Average: calculates the average of the numeric values in the collection
  • Min: finds the minimum value
  • Max: finds the maximum value
  • Take: selects a certain number of items
  • Skip: skips a certain number of items
  • TakeWhile: returns a chain of sequence elements as long as the condition is true
  • SkipWhile: skips elements in a sequence as long as they satisfy a given condition, and then returns the remaining elements
  • Concat: combines two collections
  • Zip: combines two collections according to a specific condition
  • First: selects the first item in the collection
  • FirstOrDefault: selects the first item in the collection or returns the default
  • Single: selects a single element of the collection, if the collection contains more or less than one element, an exception is thrown
  • SingleOrDefault: selects the first item in the collection or returns the default
  • ElementAt: selects an element of a sequence at a specific index
  • ElementAtOrDefault: selects a collection element at a specific index or returns a default value if the index is out of range
  • Last: selects the last item in the collection
  • LastOrDefault: selects the last item in the collection or returns the default

Let's take a closer look at some of the operations in LINQ.

Sorting

The linq language for sorting a dataset in ascending order uses the oderby operator. Let's look at the simplest example for sorting an array.

int[] num = { 5, 7, 9, 3, 19, 25, 34 };
            var numord = from j in num
                           orderby j
                           select j;
            foreach (int c in numord)
                Console.WriteLine(c);

By default, the orderby operator sorts in ascending order. However, using the keywords ascending (sorting in ascending order) and descending (sorting in descending order), you can explicitly specify the direction of sorting: As in the example, we sorted the array by numbers, but the language allows you to sort more complex objects. Consider an example:

class People
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
List<People> people = new List<People>
            {
                new People {Name = "Aron",Age= 19},
                new People {Name = "Bill",Age = 33},
                new People {Name = "Tom", Age = 7}
            };
            var sortedpeople = people.OrderBy(n => n.Name);

As you can see from the example, we have used the OrderBy extension method instead of the orderby operator. Moreover, you can use multiple sorting criteria. Consider the following example:

var result = from person in people
                         orderby person.Name, person.Age
                         select person;

Here, we used two sorting criteria - name and age.

Filtering a Selection

Where method is used to select elements from a set by condition. Let's consider an example:

class People
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
List<People> People = new List<People>
            {
                new People {Name = "Aron",Age= 5},
                new People {Name = "Pamela",Age = 5},
                new People {Name = "George",Age = 10},
                new People {Name = "Mark",Age = 30},
                new People {Name = "David",Age = 25},
                new People {Name = "Tim",Age = 25},
                new People {Name = "Jerry",Age = 25},
                new People {Name = "Tom", Age = 10}
            };
            IEnumerable<People> peoples = from p in People
                                          where p.Age == 25
                                          select p;
            foreach (var i in peoples)
                Console.WriteLine(i.Name);

Here, we have sorted people with an age of 25. The same request using an extension method:

IEnumerable<People> peoples = People.Where(i => i.Age == 25) ;

Union, Intersection and Difference of Collections

In addition to sampling methods, LINQ has several methods that allow you to generate a set of unique elements from two sequences of objects: difference, union, and intersection. Using the Except method, you can get the difference of two sets.

string[] Female = { "Marta", "Dora", "Jane" , "Tim", "Tom", "Ben" };
            string[] Man = { "Tim", "Tom", "Ben" };

            var result = Female.Except(Man);
            foreach (string n in result)
                Console.WriteLine(n);

Here, all elements that are in the Man array are removed from the Female array.

To obtain the intersection of sequences, that is, common to both sets of elements, the Intersect method is used. Consider an example:

string[] Female = { "Marta", "Dora", "Jane", "Tim" };
            string[] Man = { "Tim", "Tom", "Ben" };

            var result = Female.Intersect(Man);
            foreach (string n in result)
                Console.WriteLine(n);

In this case, we will have the result "Tim" since only this element is common in these two arrays.

The Union method is used to combine two sequences. Let's consider an example:

string[] Female = { "Marta", "Dora", "Jane", "Tim" };
            string[] Man = { "Tim", "Tom", "Ben" };
            var result = Female.Union(Man);
            foreach (string n in result)
                Console.WriteLine(n);

As a result, we will have a new set, which contains elements from both the first and the second array, and without repeating elements. You can use the Distinct method to remove items.

Skip and Take Methods

LINQ has two methods, Skip() and Take() which allow you to create paginated output. Let's consider an example:

int[] num = { 1,2,3,4,5,6,7,8,9 };
          var first = num.Take(5);
          foreach (int i in first)
              Console.WriteLine(i);

          var rest = num.Skip(5);
          foreach (int i in rest)
              Console.WriteLine(i);

As a result, in the first case, we will take the first five elements, and in the second case, we will skip the first five elements. However, there are more interesting methods: TakeWhile() and SkipWhile(). Consider an example:

int[] num = {1,1,2,3,4,5 };
            foreach (var c in num.TakeWhile(n=>n.Equals(1)))
                Console.WriteLine(c);

In our example, the TakeWhile() method selects a chain of elements starting from the first until they satisfy the condition. The SkipWhile() method works in a similar way. It loops through the chain of elements, starting at the first element, until they meet a certain condition.

Grouping

To group data by specific parameters, use the groupby operator or the GroupBy() method. Let's consider an example:

class People
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }

List<People> People= new List<People>
            {
                new People {Name = "Aron",Age= 5},
                new People {Name = "Pamela",Age = 5},
                new People {Name = "George",Age = 10},
                new People {Name = "Mark",Age = 30},
                new People {Name = "David",Age = 25},
                new People {Name = "Tim",Age = 25},
                new People {Name = "Jerry",Age = 25},
                new People {Name = "Tom", Age = 10}
            };

            var PeopleGroups = from person in People
                              group person by person.Age;

            foreach (IGrouping<int, People> person in PeopleGroups)
            {
                Console.WriteLine(person.Key);
                foreach (var gr in person)
                    Console.WriteLine(gr.Name);
                Console.WriteLine();
            }

In this example, we have grouped people by age. There is a peculiarity: If the last operator performing operations on a selection in a LINQ expression is group, then the select statement is not applied. The group operator accepts the criterion by which the grouping is carried out: group person by person.Age - in this case, grouping by the Age property.

The group statement results in a selection that consists of groups. Each group represents an IGrouping <string, People> object: the string parameter indicates the type of the key, and the People parameter indicates the type of the grouped objects.

Each group has a key, which we can get through the Key property: person.Key

All elements of the group can be obtained using additional iteration. Group items are of the same type as the type of objects that were passed to the group statement, that is, in this case, objects of type People. The exact same query can be built using the GroupBy extension method. Consider an example:

var PeopleGroups = People.GroupBy(p => p.Age);

            foreach (IGrouping<int, People> person in PeopleGroups)
            {
                Console.WriteLine(person.Key);
                foreach (var gr in person)
                    Console.WriteLine(gr.Name);
                Console.WriteLine();
            }

Here, we applied the GroupBy extension method and ended up with the same result.

Connecting Collections. Join Method

If you need to combine several different types of sets into one, you can use join operator. The join operator or the Join() method is used to join. Typically, this operation is applied to two sets that have one common criterion. Let's consider an example:

List<Country> countries = new List<Country>
            {
                new Country{Name ="USA",Population =330},
                new Country{Name ="Canada",Population = 37}
            };

            List<Capital> capitals = new List<Capital>
            {
                new Capital {Name= "Ottawa",CountryName="Canada"},
                new Capital {Name="Washington",CountryName="USA"}
            };

            var result = from c in countries
                         join cap in capitals on c.Name equals cap.CountryName
                         select new { Name = c.Name,CapitalName= cap.Name, 
                                      Population= c.Population };

            foreach (var item in result)
                Console.WriteLine($"{item.Name} , {item.CapitalName}, {item.Population}");

The expression join cap in capitals on c.Name equals cap.CountryName joins the cap in capitals list to the c in the countries list if the value of the c.Name property matches the value of the cap.CountryName property. The result of the connection will be an object of an anonymous type that will contain three properties.

The same action could be accomplished with the Join() method:

var result = countries.Join(capitals,
                p => p.Name,
                t => t.CountryName,
                (p, t) => new { Name = p.Name, CapitalName = t.Name, 
                                Population = p.Population });

The Join() method takes four parameters:

  • the second list, which we connect with the current
  • property of an object from the current list, through which the connection is made
  • property of an object from the second list, along which the connection goes
  • a new object that is obtained as a result of the connection

All and Any Methods

If we need to determine whether a collection meets a certain condition, then we can use the Any, All, Contains methods. These methods are boolean and declare true or false.

The All method checks if all items meet the condition. Let's consider an example:

List<People> People= new List<People>
            {
                new People {Name = "Aron",Age= 5},
                new People {Name = "Pamela",Age = 5},
                new People {Name = "George",Age = 10},
                new People {Name = "Mark",Age = 30},
                new People {Name = "David",Age = 25},
                new People {Name = "Tim",Age = 25},
                new People {Name = "Jerry",Age = 25},
                new People {Name = "Tom", Age = 10}
            };

bool result = People.All(p => p.Age > 25); // true
            if (result)
                Console.WriteLine("All people have age more then 25");
            else
                Console.WriteLine("We have people with age less 25");
            Console.ReadKey();

In this example, we checked the age of the people. Of course, we have an age less than 25 and the result is false.
The Any method works in a similar way, only allows you to find out whether at least one element of the collection meets a certain condition, we use the same class and set of objects as in the previous example, but change the condition a little.

bool result = People.Any(u => u.Age > 45);
if (result)
                Console.WriteLine("We have somebody more then 45 years");
            else
                Console.WriteLine("Nobody");

It returns false, because we don’t have any more then 45 years.

Conclusion

In conclusion, part of what makes LINQ so powerful and easy to use is its tight integration with the C # language. Instead of having to deal with an entirely new set of tools in the form of classes, you can use all the same familiar collections and arrays with existing classes. This means that you can take full advantage of LINQ queries with minimal or no modifications to existing code. LINQ to Objects functionality is provided by the IEnumerable <T> interface, sequences, and standard query operations.

History

  • 11th January, 2021: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here