How to Perform Multiple Actions on Items in a List Using LINQ Select

Sometimes we'll have a list of data which needs to have multiple actions performed on each item in the list. An example might be to write the item's value to a log, call a function using the item as a param to the function, and then convert the item to a new type. In the tradional approach, we would build a foreach loop, and perform these actions in the loop.

I am going to show you how to use LINQ to perform multiple actions on each item in an IENumerable list using LINQ. Depending on your desired outcome, you could use different LINQ extensions. In my example below, we will be using LINQ Select.

Another use of this technique is for using LINQ to SQL or during deferred execution. You could output each item's value to the console or a log as a demonstration of when deferrede excecution actually processes the item.

In the example below, we will take a list of integers in string format, output their original value, call a function which outputs the item's length, and finally convert the item to an int to populate a new List<int>.

Example of Performing Multiple Actions on Each Item in a List using LINQ Select

static void OutputItemLength(string item)
{
    Console.WriteLine(string.Format("\tItem Length: {0}", item.Length));
}

static void Main(string[] args)
{
    // holds the final list
    List<string> SampleList = new List<string>() { "10", "21", "32", "43", "564", "68", "79", "811", "92468", "10" };

    // convert the list using Cast
    List<int> SampleIntList = SampleList.Select<string, int>(x =>
        {
            // log the item
            Console.WriteLine(x);

            // run a func on the item
            OutputItemLength(x);

            // convert the item for the final result
            return Convert.ToInt32(x);
        }
        ).ToList();

    /*
     * Output:
     *  10
     *      Item Length: 2
     *  22
     *      Item Length: 2
     *  32
     *      Item Length: 2
     *  43
     *      Item Length: 2
     *  564
     *      Item Length: 3
     *  68
     *      Item Length: 2
     *  79
     *      Item Length: 2
     *  811
     *      Item Length: 3
     *  92468
     *      Item Length: 5
     *  10
     *      Item Length: 2
    */

    Console.ReadLine();
}

 There are a few things you need to keep in mind, using this technique:

  1. You must use the curly brackets { } to wrap the code inside your Lambda function. This allows you to reuse the variable (x in our example) multiple times.
  2. Because LINQ Select() expects a return result from the Lambda expression, your last statement must be a return statement.
    1.  This return is only used to exit the Lambda function and return a value to the LINQ Select() statement

As you can see, this can be a very powerful tool for list handling.

If you have questions, comments, or suggestions, please feel to post them in the LINQ Exchange Forum

share save 171 16 How to Perform Multiple Actions on Items in a List Using LINQ Select

How to Use LINQ to Filter a List of Files by Date

Sometimes we have to write applications which need to retrieve a list of files filtered by the date the file was created or last written to. A common example is for applications which need to delete a list of files older than a certain date. For example, I have an application which creates a new log file every time the application is launched. The customer does not want to have log files older than seven days, so each time the application launches, it must first delete log files which are older than seven days.

For this example, we will be using the System.IO namespace. I have included the full namespace in the examples, but obviously you will want to add it to your using statements, instead.

We will create a function which takes two parameters: the full path of the directory where the log files are stored, and the date at which files should NOT be selected. Files older than this date will be selected. 

Below are two examples of how to accomplish this task. The first example demonstrates the C# 2.0 method. The second example demonstrates using LINQ to filter the file list.

Filter a File List by Date using C# 2.0

/// <summary>
/// Outputs the list of log files in a directory
/// </summary>
/// <param name="FileDir">the directory to search</param>
/// <param name="NewFileDate">
///    files older than this date will be outputted.
///    newer files will be skipped.
/// </param>
public static void OutputLogFileList(string FileDir, DateTime NewFileDate)
{
    // get the list of all log files in FileDir
    List<string> LogFiles = System.IO.Directory.GetFiles(FileDir, "*.log").ToList();

    // filter the list to only include files older than NewFileDate
    List<string> OutputList = new List<string>();
    foreach (string file in LogFiles)
    {
        // add this file if it's creation date is less than NewFileDate
        if (System.IO.File.GetCreationTime(file) < NewFileDate)
            OutputList.Add(file);
    }

    // output the list
    foreach (string logFile in OutputList)
        Console.WriteLine(logFile);
}

 

 Using LINQ to Filter a File List by Date

/// <summary>
/// Outputs the list of log files in a directory
/// </summary>
/// <param name="FileDir">the directory to search</param>
/// <param name="NewFileDate">
///    files older than this date will be outputted.
///    newer files will be skipped.
/// </param>
public static void OutputLogFileList(string FileDir, DateTime NewFileDate)
{
    // get the list of all log files in FileDir
    List<string> LogFiles = System.IO.Directory.GetFiles(FileDir, "*.log").ToList();

    // filter the list to only include files older than NewFileDate
    List<string> OutputList = LogFiles.Where(x => System.IO.File.GetCreationTime(x) < NewFileDate).ToList();

    // output the list
    foreach (string logFile in OutputList)
        Console.WriteLine(logFile);
}

 

As you can see, using LINQ removed 5 lines of code. We could further remove another line by using the following instead (notice I removed the System.IO namespace):

// get the list of all log files in FileDir
List<string> OutputList = GetFiles(FileDir, "*.log").Where(x => File.GetCreationTime(x) < NewFileDate).ToList();

If you have questions, comments, or suggestions, please feel to post them in the LINQ Exchange Forum

share save 171 16 How to Use LINQ to Filter a List of Files by Date

How to Convert a Collection From One Type to Another Type Using LINQ Cast

Typically we have the problem of having a collection of objects which we would like to convert into another type of collection (for example, from an ArrayList to a List<>). If the collection inherits from IENumerable or IQueryable (List<>  for example), we can use LINQ's ConvertAll. However, if the collection instead inherits from System.Collections (ArrayList and Dictionary for example), we need to use LINQ's Cast extension.

Microsoft Explanation:

Converts the elements of an IEnumerable to the
specified type.

NOTE: LINQ Cast uses deferred execution. I will be using LINQ ToList to force immediate execution.

This method is implemented by using deferred execution. The immediate return
value is an object that stores all the information that is required to perform
the action. The query represented by this method is not executed until the
object is enumerated either by calling its GetEnumerator method directly
or by using foreach in Visual C# or For Each in Visual Basic.

 

Let's take the example of converting an ArrayList to a List.

Converting an ArrayList to a List<> Using C# 2.0

// holds the final list
List<int> SampleIntList = new List<int>();

// a list of integers, in string format
ArrayList SampleStringList = new ArrayList() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };

// convert the list to an int list
foreach (string item in SampleStringList)
    SampleIntList.Add(Convert.ToInt32(item));

// output the result
foreach (int item in SampleIntList)
    Console.WriteLine(item.ToString());

/*
 * Output:
 *  1
 *  2
 *  3
 *  4
 *  5
 *  6
 *  7
 *  8
 *  9
 *  10
*/

This is pretty straight-forward. If the types had been more complex, though, you could have seen more work being done in the first foreach loop. Using LINQ, we can call Cast, and using a Lambda expression, convert the entire collection in one line of code:

Convert an ArrayList to a List<> Using LINQ Cast

// holds the final list
List<int> SampleIntList = new List<int>();

// a list of integers, in string format
ArrayList SampleStringList = new ArrayList() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };

// convert the list using Cast

SampleIntList = SampleStringList.Cast<int>().Select(x => Convert.ToInt32(x)).ToList();

// output the result
foreach (int item in SampleIntList)
    Console.WriteLine(item.ToString());

/*
 * Output:
 *  1
 *  2
 *  3
 *  4
 *  5
 *  6
 *  7
 *  8
 *  9
 *  10
*/

 Notice that we cannot use a Lambda expression directly in the Cast<>() fuction, we have to call Select() (or another LINQ selector). Also notice the use of ToList as the final function call, to force the immediate conversion of the list.

For more complex data types, using LINQ Cast can be a very efficient way to convert a collection into a List or other IENumerable type.

If have questions, comments, or suggestion, please feel to post them in the LINQ Exchange Forum

share save 171 16 How to Convert a Collection From One Type to Another Type Using LINQ Cast

How to Find the Average Value of a List Using LINQ Average

Often times when dealing with large lists of numbers (or strings), we need to find the average value of the list. Maybe we are calculating a student's GPA, or trying find the average age of visitors to a website. In the 2.0 world, we had to create a loop (typically foreach), count all the values in the list, then divide by the number of items in the list. Now, thanks to LINQ, we can call a new extension – Average.

Microsoft Explanation:

Computes the average of a sequence of nullable Int32
values that is obtained by invoking a projection function on each element of the
input sequence.

In layman's terms, it calculates the average of a list of Int32 values, and returns the result as a double.

Using our scenario above, let's calculate the average age of visitors to a website, using the 2.0 method, and the new LINQ Average method.

Calculating Average Age of Visitors Using C# 2.0

// build a list and get the average value of the list
List<int> AgeList = new List<int>() { 25, 25, 26, 32, 27, 22, 42, 50, 23, 18, 51 };

// holds the sum of ages from the list
int AgeCounter = 0;

// loop through the list and get the sum of all ages
foreach (int age in AgeList)
    AgeCounter += age;

// get the average of all ages (Sum / Number of items in list)
double avg = AgeCounter / AgeList.Count;

// output the result
Console.WriteLine(string.Format("Average value of list: {0}", avg));

// Outputs:
// Average value of list: 31

 

Calculating Average Age of Visitors Using LINQ Average

// build a list and get the average value of the list
List<int> AgeList = new List<int>() { 25, 25, 26, 32, 27, 22, 42, 50, 23, 18, 51 };

// get the average age
double avg = AgeList.Average();

// output the results
Console.WriteLine(string.Format("Average value of list: {0}", avg));

// Outputs:
// Average value of list: 31

Another example of using LINQ Average is to find the average length of strings in a List. This example is taken from the Microsoft Documentation.

Calculating Average String Length of a List Using LINQ Average

string[] fruits = { "apple", "banana", "mango", "orange", "passionfruit", "grape" };
double average = fruits.Average(s => s.Length);
Console.WriteLine("The average string length is {0}.", average);
/*
 This code produces the following output:

 The average string length is 6.5.
*/ 

As we can see, LINQ Average allows use to use a Lambda expression also, for a more refined Average calculation.

If have questions, comments, or suggestion, please feel to post them in the LINQ Exchange Forum

share save 171 16 How to Find the Average Value of a List Using LINQ Average

Testing if Any Element in a C# List Meets a Condition

A common problem we developers have to deal with is determining if one or more items in a list meets a condition. We don't care if the whole list meets the condition, so long as at least one item meets the condition. For example, let's say you have a list of animals and their vaccination history; you need to find if any animals in the list are unvaccinated. We can use the LINQ extension Any to find any item in the list meets the criteria. LINQ Any returns true if any item matches the criteria, or false if all items do not match the criteria.

Microsoft Explanation

Determines whether any element of a sequence satisfies a condition.

Using the 2.0 method, we might use something like this:

C# 2.0 Example

class Pet
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Vaccinated { get; set; }
}

public static void AnyEx()
{
    // Create an array of Pets.
    List<Pet> PetList =
    {
        new Pet { Name="Barley", Age=8, Vaccinated=true },
        new Pet { Name="Boots", Age=4, Vaccinated=false },
        new Pet { Name="Whiskers", Age=1, Vaccinated=false }
    };

    // set our flag
    bool unvaccinated = false;

    // Determine whether any pets over age 1 are also unvaccinated.
    foreach (Pet pet in PetList)
    {
        // check for age over 1 and unvaccinated
        if ((pet.Age > 1) && (! pet.Vaccinated))
        {
            // found a match, set flag and exit loop
            unvaccinated = true;
            break;
        }
    }

    Console.WriteLine(
        "There {0} unvaccinated animals over age one.",
        unvaccinated ? "are" : "are not any");
}

// This code produces the following output:
//
//  There are unvaccinated animals over age one.
 

 

With LINQ,  we can write this, instead:

C# LINQ Any Example

class Pet
{
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Vaccinated { get; set; }
}

public static void AnyEx2()
{
    // Create an array of Pets.
    List<Pet> PetList =
    {
        new Pet { Name="Barley", Age=8, Vaccinated=true },
        new Pet { Name="Boots", Age=4, Vaccinated=false },
        new Pet { Name="Whiskers", Age=1, Vaccinated=false }
    };

    // Determine whether any pets over age 1 are also unvaccinated.
    bool unvaccinated = PetList.Any(p => p.Age > 1 && p.Vaccinated == false);

    Console.WriteLine(
        "There {0} unvaccinated animals over age one.",
        unvaccinated ? "are" : "are not any");
}

// This code produces the following output:
//
//  There are unvaccinated animals over age one.

 

As you can see, using LINQ Any is a powerful way to quickly check for the existence of item or criteria in a list.

If have questions, comments, or suggestion, please feel to post them in the LINQ Exchange Forum

share save 171 16 Testing if Any Element in a C# List Meets a Condition

Testing an Expression in a C# List using LINQ ALL

Let's say you have  list of items, and you need to quickly find out if all the items in the list match (or don't match) a given criteria. Maybe you want to know if a list of ints are all even, or if all items in the list start with the letter P. I write a lot of game code, so I'm frequently checking if a list of units (soldiers, tanks, trees, whatever) are within a certain distance of a target.

Important Note: All<> does not return a list of the items
in the collection. It returns a bool indicating if all the items in the
list match an expression.

Microsoft Explanation

Determines whether all elements of a sequence satisfy a condition.

					public static bool All<TSource>
	
					(
	IEnumerable<TSource> source,
	Func<TSource, bool> predicate
	)
	

In the 2.0 days, we would have to do something like this:

C# 2.0 Example

// build a list and see if all items in it are even
List<int> SampleList = new List<int>() { 1, 1, 2, 3, 5, 8, 13 };
bool IsEven = true;
foreach (int item in SampleList)
{
    // if the item is not even, then set the flag and exit the loop
    if ((item % 2) != 0)
    {
        IsEven = false;
        break;
    }
}
Console.WriteLine(string.Format("Are all numbers even? {0}", IsEven));

// Outputs:
// Are all numbers even? False

 

Now with LINQ, we can write more concise code, like this:

C# 3.0 All<> Example

// build a list and see if all items in it are even
List<int> SampleList = new List<int>() { 1, 1, 2, 3, 5, 8, 13 };
bool AllAreEven = SampleList.All(x => x % 2 == 0);
Console.WriteLine(string.Format("Are all numbers even? {0}", AllAreEven));

// Outputs:
// Are all numbers even? False

 

As you can see, LINQ's All<> returns a bool, true if all items in the list match the expression.

If have questions, comments, or suggestion, please feel to post them in the LINQ Exchange Forum

 

share save 171 16 Testing an Expression in a C# List using LINQ ALL

Using LINQ to Convert an IENumerable to a List

I see a lot of examples for LINQ where a foreach loop is used after the select to populate the result list. There are situations where you would want to use the foreach loop, mainly to take advantage of LINQ's deferred execution. However, most developers will not need, nor desire, to have deferred execute, especially for SQL based selects.

If you're not familiar with deferred execution, this is the method by which LINQ statements are executed. In essence, the returned IENumerable/IQueryable list from a LINQ statement has not executed, yet. After you use the LINQ statement (typically Select), you are returned an IENumerable/IQueryable list. However, this list has not been populated. When you call the foreach loop on the returned list, each item in the list is individually executed. This can have huge impacts if you use LINQ to query a database, or to query a collection which can be modified outside your immediate loop. Many people have posted problems on LINQ forums about the expected results of the foreach being different from the actual results.

I'm going to show you a simple way to force the entire list to execute and populate, with one simple command. 

Microsoft Explanation

Creates a List<T> from an IENumerable<T>

Example 1 demonstrates the typical LINQ select examples found online. This example demonstrates a list of names (possibly user names), and selecting only the names which start with "P".

Example 1

List<string> SampleList = new List<string>() { "Mary", "Nancy", "Pete", "Ned", "Paul", "Bob", "Jim", "Maria" };
List<string> FilteredList = new List<string>();

// get the list of names that start with "P"
var query = from x in SampleList
            where x.StartsWith("P")
            select x;

// pop the filtered list
foreach (var result in query)
    FilteredList.Add(result);

 

If the SampleList were modifiable outside the immediate scope of this function (for example, as a property that other threads could access), it is very possible that the results of the select statement could change during the execution of the foreach loop.

The solution? Wrap the select with a call to the ToList extension:

Example 2 – The Solution

List<string> SampleList = new List<string>() { "Mary", "Nancy", "Pete", "Ned", "Paul", "Bob", "Jim", "Maria" };
List<string> FilteredList = new List<string>();

// get the list of names that start with "P"
FilteredList = (from x in SampleList
            where x.StartsWith("P")
            select x).ToList();

 

An even more consise way would be:

Example 3 – The Shorter Solution

List<string> SampleList = new List<string>() { "Mary", "Nancy", "Pete", "Ned", "Paul", "Bob", "Jim", "Maria" };
List<string> FilteredList = new List<string>();

// get the list of names that start with "P"
FilteredList = SampleList.Where(x => x.StartsWith("P")).ToList();

 

There are other ToX extensions, such as ToArray, ToDictionary, and ToLookup. These all perform the same immediate execution of the LINQ statement, returning the respective collection type.

If have questions, comments, or suggestion, please feel to post them in the LINQ Exchange Forum

share save 171 16 Using LINQ to Convert an IENumerable to a List

How to Use LINQ Aggregate to on a C# List

Hello, and welcome to the first blog post on LINQ Exchange. My goal for this blog is to cover each LINQ extension, describe it, and show examples of how and why you might use it. I will probably be going in alphabetical order, at least in the beginning. If you have a request for a specific extension, or if something warrants additional commentary, then I will happily post another blog entry for it.

Today, we will be discussing Aggregate.

Syntax:

 Aggregate<(Of TSource>)(IEnumerable<(Of TSource>), Func<(Of TSource, TSource, TSource>))

 

First, the Microsoft Explanation:

The Aggregate method makes it simple to perform a calculation over a
sequence of values. This method works by calling func one time for each element in source. Each time func is
called, Aggregate passes both the element from the sequence and an
aggregated value (as the first argument to func).
The first element of source is used as the initial
aggregate value. The result of func replaces the
previous aggregated value. Aggregate returns the final result of func.

So, basically Aggregate allows you to loop through an IENumerable list and add the values in the list to produce a final result. The final result is of the same type as the IENumerable items. 

The example below (Example 1) demonstrates a simple Aggregate use on a List<int>. 

Example 1:

List<int> SampleList = new List<int>() { 1, 1, 2, 3, 5, 8, 13 };
int AggCount = SampleList.Aggregate((counter, listItem) => counter += listItem);
Console.WriteLine(string.Format("Aggregate count is: {0}", AggCount));

// Outputs:
// Aggregate count is: 33

As you can see in the example, each time through the loop, counter is incremented by the value of listItem. listItem is the item in the list being iterated, while counter holds the final return value.

Why can't I just use Sum? Well, you can. But, the power of Aggregate comes when you want to do more than just add the integer values of a list. I'm going to borrow a Microsoft example so you can see what I mean:

Example 2:

string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words.

string[] words = sentence.Split(' ');
// Join each word to the beginning of the new sentence to reverse the word oder.

string reversed = words.Aggregate(( workingSentence, next) => next + " " + workingSentence);
Console.WriteLine(reversed);
/*
This code produces the following output:

dog lazy the over jumps fox brown quick the
*/

As you can see, with a little ingenuity, Aggregate can be used for a variety of purposes.

If have questions, comments, or suggestion, please feel to post them in the LINQ Exchange Forum

share save 171 16 How to Use LINQ Aggregate to on a C# List
 Page 10 of 10  « First  ... « 6  7  8  9  10