Introduction to LINQ: Part 5 - Iteration

Date Published: 11/21/2020

Check out my video courses at...

Pluralsight and at Udemy

Before reading this blog post, you should read Introduction to LINQ: Part 1 - Selecting Data. That post introduces you to the data and the general approach to how LINQ works.

In this blog post you learn how to apply some iteration methods to a collection of data. The ForEach() method allows you to iterate over the entire collection and apply an expression to each object. The Skip() methods allows you to bypass a certain amount of objects at the beginning of a collection. The Take() methods allows you to only take a certain amount of objects from the beginning of a collection.

Using the ForEach() Method

The ForEach() method allows you to iterate over any collection and execute an expression on each item in the collection. In the first two samples you are going to loop through the collection and get the length of the value in the Name property for each Product object and assign that value to a property named NameLength in the Product class. The second example passes each Product object to a method that sums up the sales for that product and assigns the total sales to a TotalSales property. Open the Product.cs file and add two new properties as shown in the following code snippet.

// Calculated Properties
public int NameLength { get; set; }
public decimal TotalSales { get; set; }

When using the LINQ query syntax, use the 'let' keyword and a temporary (throw-away) variable to perform the assignment of the prod.Name.Length to the prod.NameLength property. When you use the LINQ method syntax, it is much clearer that you are performing an assignment of the length of the Name property to the NameLength property.

public void ForEach()
{
  // Load all Product Data
  LoadProductsCollection();

  if (UseQuerySyntax) {
    // Query Syntax
    Products = (from prod in Products
                let tmp = prod.NameLength = prod.Name.Length
                select prod).ToList();
  }
  else {
    // Method Syntax
    Products.ForEach(prod => prod.NameLength = prod.Name.Length);
  }

  ResultText = $"Total Products: {Products.Count}";
}

Calling a Method from ForEach()

If you wish to fill in the TotalSales property into each Product object in the Products collection, create a method to which you pass a Product object. In this method you get all of the Sales data, then perform a Where() method to filter the sales by the ProductID property. You then get the sum of all LineTotal values. NOTE: This is an inefficient design as you would not want to retrieve all the values from the Sales collection each time. This sample is just to illustrate calling a method from within a ForEach().

private decimal SalesForProduct(Product prod)
{
  List<SalesOrderDetail> sales;

  // Get All Sales Data
  sales = new SalesOrderDetailRepository().GetAll();

  return sales.Where(s => s.ProductID == prod.ProductID)
              .Sum(s => s.LineTotal);
}

Create a new method as shown below that calls the SalesForProduct() method passing in each Product object as the LINQ query runs. When using the query syntax you must use the 'let' keyword in order to perform the assignment to the TotalSales property from the return result of the call to SalesForProduct().

public void ForEachCallingMethod()
{
  // Load all Product Data
  LoadProductsCollection();

  if (UseQuerySyntax) {
    // Query Syntax
    Products = (from prod in Products
                let tmp = prod.TotalSales = SalesForProduct(prod)
                select prod).ToList();
  }
  else {
    // Method Syntax
    Products.ForEach(prod => prod.TotalSales = SalesForProduct(prod));
  }

  ResultText = $"Total Products: {Products.Count}";
}

Skip()

If you wish to skip a certain amount of items at the beginning of a collection, apply the Skip() method, passing in the total number of items to skip over as shown in the following code.

public void Skip()
{
  // Load all Product Data
  LoadProductsCollection();

  if (UseQuerySyntax) {
    // Query Syntax
    Products = (from prod in Products
                select prod).Skip(20).ToList();
  }
  else {
    // Method Syntax
    Products = Products.Skip(20).ToList();
  }

  ResultText = $"Total Products: {Products.Count}";
}

SkipWhile()

If you wish to apply a condition to skip those items at the beginning of the collection, use the SkipWhile() method. In the following example, use the OrderBy() method to sort the collection by the Name property. Apply the SkipWhile() method to the results of the ordering and skip those items where the Name property starts with the letter "A".

public void SkipWhile()
{
  // Load all Product Data
  LoadProductsCollection();

  if (UseQuerySyntax) {
    // Query Syntax
    Products = 
      (from prod in Products
       orderby prod.Name
       select prod)
       .SkipWhile(prod => prod.Name.StartsWith("A")).ToList();
  }
  else {
    // Method Syntax
    Products = Products.OrderBy(prod => prod.Name)
      .SkipWhile(prod => prod.Name.StartsWith("A")).ToList();
  }

  ResultText = $"Total Products: {Products.Count}";
}

Take()

Instead of skipping a certain amount of items at the beginning of a collection, you might want to only return a certain amount of items the beginning of the collection. Use the Take() method to specify how many items you want to return.

public void Take()
{
  // Load all Product Data
  LoadProductsCollection();

  if (UseQuerySyntax) {
    // Query Syntax
    Products = (from prod in Products
                select prod).Take(5).ToList();
  }
  else {
    // Method Syntax
    Products = Products.Take(5).ToList();
  }

  ResultText = $"Total Products: {Products.Count}";
}

TakeWhile()

TakeWhile() allows you to take a certain amount of items from the beginning of a collection based on a condition. Pass in a predicate expression to the TakeWhile() to perform some test on the objects. If the expression returns a true, the item will be taken and move to the next item in the collection. This continues until the expression returns false. At that point, no more items in the collection will be taken.

public void TakeWhile()
{
  // Load all Product Data
  LoadProductsCollection();

  if (UseQuerySyntax) {
    // Query Syntax
    Products = 
     (from prod in Products
      orderby prod.Name
      select prod)
      .TakeWhile(prod => prod.Name.StartsWith("A")).ToList();
  }
  else {
    // Method Syntax
    Products = Products.OrderBy(prod => prod.Name)
      .TakeWhile(prod => prod.Name.StartsWith("A")).ToList();
  }

  ResultText = $"Total Products: {Products.Count}";
}

Summary

In this blog post you learned how to iterate over items in a collection. Using the ForEach() method you can assign values into new properties or even call a method to perform complex operations. The Skip() method will skip a specific number of items, while the SkipWhile() skips objects based on a condition. The Take() method returns a specific number of items from the start of a collection, while the TakeWhile() method returns items from the start of a collection based on a condition.


#linq #csharp #pauldsheriff #development #programming

Check out my video courses at...

Pluralsight and at Udemy