Showing posts with label Linq. Show all posts
Showing posts with label Linq. Show all posts

October 2, 2026

Linq Aggregate Functionality

Here is a link to a explanation of the linq aggregate method on Det Net Tutorials: LINQ Aggregate Method in C# The most powerful form allows you to set a seed value to start the Aggregate computation and a result selector to perform a final calculation at the end calculation. I like the way the examples are written in the examples, it makes it so much easier to see what is being done.


List ints = [4, 6, 9, 1, 3, 5];
List decimals = [4.2m, 6.3m, 9.7m, 1.8m, 3.4m, 5.1m];

// Aggregate to (total revenue, count), then project to average
decimal averageDecimals = decimals.Aggregate(
    seed: (Total: 0m, Count: 0),
    func: (acc, s) => (acc.Total + s, acc.Count + 1),
    resultSelector: acc => acc.Count > 0 ? acc.Total / acc.Count : 0m);

decimal averageInts = ints.Aggregate(
        seed: (Total: 0m, Count: 0),
        func: (acc, s) => (acc.Total + (decimal)s, acc.Count + 1),
        resultSelector: acc => acc.Count > 0m ? acc.Total / (decimal)acc.Count : 0m);

Console.WriteLine($"Average Decimals: ${averageDecimals:F2}");
Console.WriteLine($"Average Ints: ${averageInts:F2}");

I know their are already methods for calculating an average, it is just a good example to see how the Aggregate method is used

October 14, 2023

Simple Line Editor 2

Here is the new LineEditor, it can work on a file or a string. This timing using the power of LINQ.

// Here is the definition of a line edit
public interface ILineEdit
{
    string ApplyEdit(string input);
}

/// <summary>
/// Perform a bunch of edits on a text file or string on a line by line basis. 
/// Only search and replace type edits are available.
/// </summary>
/// <summary>
/// Perform a bunch of edits on a IEnumerable<string>. 
/// </summary>
public class TextLineEditor
{
    private List<ILineEdit> _pendingEdits = new List<ILineEdit>();

    public TextLineEditor()
    {
    }

    public IEnumerable<string> ApplyEdits(IEnumerable<string> inputs)
    {
        foreach (var line in inputs)
        {
            string tmp = line;
            foreach (ILineEdit edit in _pendingEdits)
            {
                tmp = edit.ApplyEdit(tmp);
            }
            yield return tmp;
        }
    }

    public void AddEdit(ILineEdit edit)
    {
        Trace.Assert(edit != null);
        _pendingEdits.Add(edit);
    }
}

We can use this as the basis of a line by line text file ediitor:

/// <summary>
/// Perform a bunch of edits on a text file 
/// Only search and replace type edits are available.
/// </summary>
public class FileTextLineEditor : TextLineEditor
{
    public void ApplyEditsToFile(string inputPath, bool keepOriginal = true)
    {
        Debug.Assert(!string.IsNullOrWhiteSpace(inputPath));
        var inputFileInfo = new FileInfo(inputPath);
        Debug.Assert(inputFileInfo.Exists);

        var outputPath = inputPath + ".new";
        var outputFileInfo = new FileInfo(outputPath);
        Debug.Assert(!outputFileInfo.Exists);

        File.WriteAllLines(outputFileInfo.FullName, ApplyEdits(File.ReadLines(inputFileInfo.FullName)));
        
        inputFileInfo.MoveTo(inputPath + ".bak");
        outputFileInfo.Refresh();
        outputFileInfo.MoveTo(inputPath);
        if (!keepOriginal)
        {
            File.Delete(inputPath + ".bak");
        }
    }
}
Here is a LineEdit example. Note that it can do deletes by replacing with a blank string.

public class SearchAndReplace : ILineEdit
{
    public enum RegExUsage
    {
        None = 0,
        Use
    }

    public SearchAndReplace()
    {
    }

    public SearchAndReplace(string searchFor, string replaceWith, bool extendedChars = false, RegExUsage regExUse = RegExUsage.None)
    {
        if (string.IsNullOrEmpty(searchFor))
            throw new ArgumentException("Cannot be null or an empty string", "searchFor");
        if (replaceWith == null)
            throw new ArgumentException("Cannot be null", "replaceWith");
        this.SearchFor = searchFor;
        this.ReplaceWith = replaceWith;
        this.ExtendedChars = extendedChars;
    }

    public bool IsRegEx { get; set; } = false;
    public string SearchFor { get; set; } = "";
    public string ReplaceWith { get; set; } = "";
    public bool ExtendedChars { get; set; } = false;

    public string ApplyEdit(string input)
    {
        if (ExtendedChars)
        {
            SearchFor = SearchFor.Replace("\\t","\t").Replace("\\n","\n").Replace("\\r","\r");
        }
        return (IsRegEx) ? SearchReplaceInString(input) : SearchReplaceInStringRegEx(input);
    }

    private string SearchReplaceInString(string input)
    {
        string output = input.Replace(SearchFor, ReplaceWith);
        return output;
    }

    private string SearchReplaceInStringRegEx(string input)
    {
        string output = Regex.Replace(input, SearchFor, ReplaceWith);
        return output;
    }
}

Here is an example usage that attempts to replace usage of Moq in a CS file to usage of NSubstitute:
void Main()
{
    string inputFile = @"a:/path/SomeFileName.cs";

    FileTextLineEditor resEditor = new();
    resEditor.AddEdits(this.Edits());
    resEditor.ApplyEditsToFile(inputFile, keepOriginal: false);
}

internal IEnumerable<ILineEdit> Edits()
{
    yield return new SearchAndReplace("using Moq", "using NSubstitute");
    yield return new SearchAndReplace("MockBehavior.Strict", "");
    yield return new SearchAndReplace("MockBehavior.Loose", "");
    yield return new SearchAndReplace(".Object", "");
    yield return new SearchAndReplace("It.IsAny", "Arg.Any");
    yield return new SearchAndReplace("It.Is", "Arg.Is");
    yield return new SearchAndReplace(@"(new\sMock\<([A-Za-z\<\>\-_]+)\>)", @"Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(Mock\<([A-Za-z\<\>\-_]+)\>\s([A-Za-z\<\>\-_]+)\s\=\snew)", @"var $3 = Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use);
    // yield return new SearchAndReplace(@" = new()", @" = Substitute.For(??)");
    //yield return new SearchAndReplace(@"(^\sMock\<([A-Za-z\<\>\-_]+)", "var", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(Setup\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(SetupGet\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(Verify\([a-z]+\s\=\>\s[a-z]+)", "Received(?)", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace("Times.Once", "1");
    yield return new SearchAndReplace("Times.Never", "0");
    yield return new SearchAndReplace(@"Returns\(\(\)\s\=\>\s", "Returns(");
} 

June 22, 2021

Implementing a Batch in Linq

When enumeration database records in Linq, it is important to batch the work otherwise LINQ can generate SQL statements that are too big (regardless of whether Entity Frameworks are being used or LINQ to SQL). Another problem is that it can take a long time to process all the records, not only can that require a lot of memory, after a while you will start to wonder if the code has crashed or not. It is better to see a batch finishing every now and then and thus know that it is actually progressing.
// Source here : https://stackoverflow.com/questions/13731796/create-batches-in-linq
// Example here : https://dotnetfiddle.net/HpRgd5
public static class BatchLinq
{
    /// <summary>
    /// Full lazy implementation of a batch
    /// Known issue with this approach is that each batch must be 
    /// enumerated and enumerated fully before moving to the next batch.
    /// </summary>
    /// <typeparam name="T">The type being enumerated</typeparam>
    /// <param name="source">The source enumerable</param>
    /// <param name="size">Size of the batch</param>
    /// <returns>An enumeration of batches with size elements 
    /// in each batch</returns>
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        if (size <= 0)
        {
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
        }
        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                var innerBatch = new InnerBatch();
                var enumerable = innerBatch.Batch(enumerator, size);
                yield return enumerable;
                if (!innerBatch.IsFinished)
                    enumerable.Count();
            }
        }
    }

    private class InnerBatch
    {
        public bool IsFinished { get; private set; } = false;

        public IEnumerable<T> Batch<T>(IEnumerator<T> source, int size)
        {
            int i = 0;
            do 
                yield return source.Current;
            while (++i < size && source.MoveNext());
            IsFinished = true;
        }
    }

    // This implementation splits the enumeration up into batches of lists
    // This is safe to use if the batches are small but it is not a fully lazy 
    // evaluation of the batch like the above method. However if you do not 
    // enumerate each batch fully then this version must be used
    public static IEnumerable<IList<T>> BatchByList<T>(this IEnumerable<T> source, int size)
    {
        List<T> batch = new List<T>(size);

        foreach (var item in source)
        {
            batch.Add(item);

            if (batch.Count >= size)
            {
                yield return batch;
                batch = new List<T>(size);
            }
        }

        if (batch.Count > 0)
        {
            yield return batch;
        }
    }
}

September 29, 2020

Linq GroupBy/ToLookUp

These are Linq methods to group elements in a sequence. ToLookup() is the same as GroupBy(); the only differences are that GroupBy execution is deferred, whereas ToLookup() execution is immediate and also ToLookup() is not available in linq query form only the method form.

Note that the resultant group object exposes itself in 2 ways:

1. A 'Key' property exposes a value which all the items in the group have in common (this could be a calculated value)

2. The group object itself is an IEnumerable of the items in the group.

This operation will iterate over all the items in the enumeration. Works well with the Linq method "ToDictionary()" to convert a sequence into a dictionary.

Here is an example:

// Read StationLocationRaw(s) (int StationId and string Location)
// from the Station Locations file
public class StationLocationReader 
{
    FileInfo fi;
    public StationLocationReader(string path)
    {
        fi = new FileInfo(path);
    }

    public IDictionary<int, string> StationLocationsByStationId()
    {
        var stationLocationsDictionary =
            ReadStationLocations(). // returns StationLocationRaw(s)
                                    // (int StationId and string Location)
            GroupBy(s => s.StationId).
            ToDictionary(g => g.Key, g => g.Single().Location ?? "");
        return stationLocationsDictionary;
    }

    public IEnumerable<StationLocationRaw> ReadStationLocations()
    {
        Debug.Assert(fi.Exists);
        var stationLocations = fi.
            ReadLines().
            Skip(1). // Skip the header line
            Select(line => ExtractStationLocation(line));
        return stationLocations;
    }

    private StationLocationRaw ExtractStationLocation(string line)
    {
        var stationLocation = new StationLocationRaw();
        var parts = line.Split('\t');
        if (parts.Length >= 2)
        {
            // ParseInt is an extension method to convert
            // to a string to an integer, returning 0 
            // when the string is invalid.
            stationLocation.StationId = parts[0].Trim().ParseInt(0); 
            stationLocation.Location = parts[1].Trim();
        }
        return stationLocation;
    }
}

September 13, 2017

CSV Files with Linq

An example of using Linq to parse a CSV file and write data to a TSV file. The beauty of the approach shown here is the file is read one line at a time when parsing, you do not have to load the whole file to process the data, and written one line at a time when writing. Here is a sample piece of the CSV file (note the first line is a header line not a data line):
id,edsm_id,name,x,y,z,population,is_populated,government_id,government,allegiance_id,allegiance,state_id,state,security_id,security,primary_economy_id,primary_economy,power,power_state,power_state_id,needs_permit,updated_at,simbad_ref,controlling_minor_faction_id,controlling_minor_faction,reserve_type_id,reserve_type
17,60,"10 Ursae Majoris",0.03125,34.90625,-39.09375,0,0,176,None,5,None,80,None,16,Low,10,None,,,,0,1497906646,"10 Ursae Majoris",,,,
24,12009,"11 Bootis",-49.40625,285.25,65.21875,0,0,176,None,5,None,80,None,16,Low,10,None,,,,0,1474116394,"11 Bootis",,,,
26,13308,"11 Mu Aurigae",-30,0.75,-150.03125,0,0,176,None,5,None,80,None,16,Low,10,None,,,,0,1497806946,,,,,


Want to parse some of these lines into this structure:
public class BaseSystemRaw
{
    public int id { get; set; }
    public string name { get; set; }
    public float x { get; set; }
    public float y { get; set; }
    public float z { get; set; }

    public override string ToString()
    {
        return "id=" + id + ", name=" + name + 
               ", location=( " + x + ", " + y + ", " + z + " )";
    }
}
Here is a test harness:
[ TestFixture ]
class ParseCsvFilesWithLinqTests
{
    [ Test ]
    public void ParseCsvFileTest()
    {
        // Skip(1) will skip the header line
        // This commented out version will only use the first 10 lines,
        // great for debugging
        // var baseSystemRaws = File.ReadLines(TestFilePaths.SystemsFilePath).
        //     Take(10).Skip(1).Select(line => ExtractBaseSystemRaw(line));
        var baseSystemRaws = File.ReadLines( TestFilePaths.SystemsFilePath ).
                Skip( 1 ).Select( line => ExtractBaseSystemRaw( line ) );
        DoSomethingWith( baseSystemRaws );
    }

    BaseSystemRaw ExtractBaseSystemRaw(
        string line )
    {
        var parts = line.Split( ',' );
        var sysRaw = new BaseSystemRaw();
        int tmp;
        if ( int.TryParse( parts[ 0 ], out tmp ) )
        {
            sysRaw.id = tmp;
        }
        // Ignore parts[1] edsm_id
        sysRaw.name = parts[ 2 ].Trim().Replace( "\"", "" );
        float ftmp;
        if ( float.TryParse( parts[ 3 ], out ftmp ) )
        {
            sysRaw.x = ftmp;
        }
        if ( float.TryParse( parts[ 4 ], out ftmp ) )
        {
            sysRaw.y = ftmp;
        }
        if ( float.TryParse( parts[ 5 ], out ftmp ) )
        {
            sysRaw.z = ftmp;
        }
        // Ignore other parts
        return sysRaw;
    }

    private void DoSomethingWith(
        IEnumerable<BaseSystemRaw> baseSystemRaws )
    {
        foreach ( var entry in baseSystemRaws )
        {
            Trace.WriteLine( entry );
        }
    }
}
Now write the data to a TSV file.
[ TestFixture ]
class WriteTsvFilesWithLinqTests
{
    [ Test ]
    public void WriteTsvFileTest()
    {
        const string separator = "\t";
        var headers = new[] { "Id", "System", "x", "y", "z" };
        // Skip(1) will skip the header line
        // This version will only use the first 4 lines, great for debugging, 
        // remove the Take(4) to process the whole file
        var baseSystemRaws = File.ReadLines( TestFilePaths.SystemsFilePath ).
            Take(4).Skip(1).Select( line => ExtractBaseSystemRaw( line ) );
        
        // Use Enumerable.Concat to add the header string
        File.WriteAllLines( TestFilePaths.BaseSystemsTsvFilePath,
            Enumerable.Concat( new[] { string.Join( separator, headers ) },
                baseSystemRaws.Select(sys => sys != null ? 
                    CoreSystemRawToTsv( separator, sys ) : "")));
    }

    private string CoreSystemRawToTsv(
        BaseSystemRaw sys )
    {
        string line = string.Join( "\t", sys.id, sys.name, sys.x.ToString( "0.0000" ),
            sys.y.ToString( "0.0000" ), sys.z.ToString( "0.0000" ) );
        return line;
    }
}

October 30, 2014

Using Linq To Sql

To use LinqToSql in a project:
First add "System.Data.Linq" reference to the project
Map the Entity Classes to Tables. Need a "[Table]XxxTable" class per table with appropriate properties for each column. Note that the columns names have to match the property names. Although this table class maps to the Db table an instance of it represents a row in the table. For example:
// Table mapping entity for the MyTableRow table row
[Table(Name = "MyTable")]
internal class MyTableRow
{
    // Default constructor is Required for Linq to Sql
    public MyTableRow()
    {
    }

    /////////////////////////////////
    // Database columns defined here

    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    public int Id { get; set; }

    [Column(CanBeNull = false)]
    public string Name { get; set; }

    [Column(DbType = "Bit NOT NULL")]
    public bool IsMandatory { get; set; }
}
This maps to a row in the SQL table "MyTable". Need a "DataContext" derived class to access these tables, call it xxxDataContext. Mark it with the "[Database]" attribute. For example:
// Linq to SQL data context for accessing the DB 
[Database]
internal class MyDataContext : DataContext
{
    // Constructor
    public MyDataContext(string connectionString)
        : base(connectionString)
    {
    }
}
Found that making the correct key definitions (including foreign keys) on the SQL tables was critical to getting the Linq to Sql working. With those definitions we can start to query the DB using Linq.
Querying SQL with LINQ
LINQ to SQL: .NET Language-Integrated Query for Relational Data
private void CreatePattern(
    MyDataContext dbAccess, 
    MyTableRow[] toCreate)
{
    var table = dbAccess.GetTable<MyTableRow>();
    table.InsertAllOnSubmit(toCreate);
    dbAccess.SubmitChanges();
}

private MyTableRow[] ReadPattern()
{
    var allRows = new MyTableRow[0];
    using (var dbAccess = new MyDataContext(GetDatabaseConnectionString()))
    {
        var table = dbAccess.GetTable<MyTableRow>();
        allRows = table.ToArray();
    }
    return allRows.ToArray();
}

private void UpdatePattern(
    MyAdminContext dbAccess, 
    MyTableRow[] changed)
{
    var table = dbAccess.GetTable<MyTableRow>();
    foreach (var target in changed)
    {
        // Find row to update in the table
        var id = target.Id;
        var row = table.FirstOrDefault(rowx => rowx.Id == id);
        if (row != null) // IF it was found
        {
            // Copy the changes from target into the row
            UpdateRow(row, target); 
        }
    }
    dbAccess.SubmitChanges(); 
}

private void DeletePattern(
    MyDataContext dbAccess, 
    MyTableRow[] deleted)
{
    var table = dbAccess.GetTable<MyTableRow>();

    foreach (var target in deleted)
    {
        // Find row to delete in the table
        var id = target.Id;
        var row = table.FirstOrDefault(ipdx => ipdx.Id == id);
        if (row != null) // Was it found?
        {
            table.DeleteOnSubmit(row);
        }
    }
    dbAccess.SubmitChanges();
}
There are 2 ways to create transactions; using the TransactionScope class and using the standard DbTransaction class.
Using new TransactionScope() Considered Harmful
All About TransactionScope
Using Transaction Scope (need to add the System.Transactions assembly in the references)
using System.Transactions;
...
using (var scope = TransactionScopeFactory.CreateTransactionScope()) // Asscociate all the changes with 1 transaction
{
    // Use 1 data context for all operations, in this case MS DTC will not be used
    // See http://weblog.west-wind.com/posts/2009/Jul/14/LINQ-to-SQL-and-Transactions 
    // paragraph 'TransactionScope DTC Requirements'
    using (var dbAccess = new MyDataContext (GetDatabaseConnectionString()))
    {
        MakeDbChangesUsingLinqToSql(dbAccess);

        scope.Complete();
        log.WriteInfo("Transaction completed, the database changes are committed.");
    }
}
Using a DbTransaction
using (var dbAccess = new MyDataContext(GetDatabaseConnectionString()))
{
    dbAccess.Connection.Open();
    // To absolutely guarantee that the MS DTC will not be used (which can occur when using TransactionScope) 
    // we will use a standard DB transaction here
    dbAccess.Transaction = dbAccess.Connection.BeginTransaction();
    
    try
    {
        MakeDbChangesUsingLinqToSql(dbAccess);
    
        dbAccess.Transaction.Commit(); // No exceptions so commit the changes
        log.WriteInfo("Transaction completed, the database changes are committed.");
    }
    catch (Exception) // Rollback if any exception is encountered
    {
        dbAccess.Transaction.Rollback();
        throw;
    }
}

May 13, 2014

Searching for text within files using Linq

A good example of the power of Linq:
Directory.EnumerateFiles(@"S\Source\", "*.*proj", SearchOption.AllDirectories)
    .SelectMany(file => File.ReadAllLines(file).Select((text,n)=> new {text,lineNumber=n+1,file}))
    .Where(line => Regex.IsMatch(line.text,@"SccProjectName"))
    .Where(line => !Regex.IsMatch(line.text,@"SAK"))
    .Dump("Should be set to SAK")

November 19, 2009

Sample Linq

In order to use Linq in the following ways a reference to the 'System.Core' assembly is required. The namespace is 'System.Linq'.
Useful Links
This code shows the same method 3 times using different forms of Linq. It also demonstrates some lambda expressions.
private Smeg FindSmeg(Project project, string smegName)
{
    SpellRoot spellRoot = SpellRoot.Get(project);
    SmegCollection smegs = spellRoot.SmegCollection;

    // Note that the 'smegVar' is an IEnumerable 
    var smegVar = from bh in smegs
                      where bh.Name.Equals(smegName,   
                                StringComparison.OrdinalIgnoreCase)
                      select bh; 
    Smeg smeg = smegVar.FirstOrDefault();

    return smeg;
}

private Smeg FindSmeg2(Project project, string smegName)
{
    SpellRoot spellRoot = SpellRoot.Get(project);
    SmegCollection smegs = spellRoot.SmegCollection;

    // Declaring a Func type lambda expression
    // A good way to learn how to form correct lambda expressions
    Func smegSelector = 
        bh => bh.Name.Equals(smegName, StringComparison.OrdinalIgnoreCase);
    Smeg smeg = smegs.FirstOrDefault(smegSelector);

    return smeg;
}

private Smeg FindSmeg3(Project project, string smegName)
{
    SpellRoot spellRoot = SpellRoot.Get(project);
    SmegCollection smegs = spellRoot.SmegCollection;

    // Inline lambda expression
    Smeg smeg = smegs.FirstOrDefault(
        bh => bh.Name.Equals(smegName, StringComparison.OrdinalIgnoreCase));

    return smeg;
}

Simple examples

Count() - Get count of items in an enumeration
IEnumerable smegs = ...
int count = smegs.Count();
ToArray() - enumeration to an array
IEnumerable smegs = ...
Smeg smegArr = smegs.ToArray();
ToList() - enumeration to a list
IEnumerable smegs = ...
IList smegList = smegs.ToList();
Where() - This is a general purpose filter, the function in the Where clause performs the filtering
File.ReadLines( @"D:\Users\ukrb\Docs\SpcPressures.txt" ).
    Where(li => li.Contains(" PRESSURE")). // Filter out any line NOT containing the specified string (" PRESSURE")
    Dump();
Select() - Allows you to Select portions of/Convert each enumerated item to an item of another type (not by casting but by extraction or conversion). Anonymous types can be used for conversion to simple intermediate types
// This parses a file of data where each line contains a number of samples, their average, maximum, minimum, etc.
File.ReadLines( @"D:\Users\ukrb\Docs\PRESSURE 214289.DAT" ).
    Select(li => ParseLine(li)).   // ParseLine is a function returning a specific class type instance
    Select(pe => pe.ToV10Form()).  // Now convert it to another more readable string of only data that is interesting
    Dump(); 
Single() - Returns the single matching element in the enumeration, if there is not exactly one match an 'InvalidOperationException' exception is thrown.
IEnumerable smegs = ...
Smeg first = smegs.Single();
First() - first element in an enumeration, be careful an exception is thrown if the enumeration is empty! If your enumeration maybe empty use FirstOrDefault
IEnumerable smegs = ...
Smeg first = smegs.First();
FirstOrDefault() - first element in an enumeration, and when it is empty, the default value of the enumerated type (which is always null for a reference type)
IEnumerable smegs = ...
Smeg first = smegs.FirstOrDefault();
Last() - last element in an enumeration, be careful an exception is thrown if the enumeration is empty!
IEnumerable smegs = ...
Smeg last = smegs.Last();
Skip() - skip the first n elements in an enumeration and return what is left, be careful an exception is thrown if the enumeration has less than n items
string[] allArgs = Environment.GetCommandLineArgs();
IEnumerable<string> args = allArgs.Skip(1); // First argument is the exe path
OfType() - extract from an enumeration all objects of the given type. Safe way of converting an enumeration of one type to another, it uses the 'is' operator. An excellent way to filter out null objects in a sequence. There is the "Cast<>()" operator but the trouble with this is that it will throw an exception if one of the objects of the enumeration is not of the given type so only use that when you know that all the elements of the enumeration will cast to the new type
IEnumerable smegs = ...
int numSquares = smegs.OfType().Count();
Cast() - Casts all items in the enumeration to the given type. When an item can not be cast to the new type then an exception is thrown. Useful to convert old style enumerables of known types. For example:
StringCollection sc = this.settingsService.GetProperty
      <StringCollection>(SourceDirectoriesSetting) ?? new StringCollection();
if (sc.Count > 0)
{
  // Cast the string collection 'sc' entries to strings
  ObservableCollection<string> oc = new ObservableCollection<string>(sc.Cast<string>());
  ...
}
Using the XXXOrDefault (First, Single, Last, ElementAt) option:
In some cases when an IEnumerable returns no entries at all the Linq operator will throw an exception using the "OrDefault" option will circumvent this problem. In the case that there are no entries returned from an XXXOrDefault operator, Linq will return the default value for the generic parameter. In the case of a reference type this will be null. For a value type it will return the default value for that value type.

SelectMany() - SelectMany flatten nested enumerables, see here for a good example

Linq Set operators:
  • Union - Simply appends one IEnumerable to another
  • Intersect - Finds items common to 2 IEnumerables
  • Except - Subtracts one IEnumerable from another
  • Distinct - Removes duplicate items from a single IEnumerable (Operates on a single IEnumerable!)

July 29, 2009

Extension Methods On IEnumerable

The 'System.Linq' namespace adds a bunch of useful IEnumerable extension methods. These are used in Linq expressions but can alos be used seperately.

A reference to the 'System.Core' assembly is required. For example performing a count with an enumerator:
using System.Linq;
...
//This saves performing a foreach statement on the enumeration and counting the number of elements
IEnumerable enumKeywords = editor.Sections.RunSpec.GetAll("SMOG");
bool metricUnits = enumKeywords.Count() > 0;
// or
bool metricUnits = editor.Sections.RunSpec.GetAll("SMOG").Count() > 0;
// or (even shorter)
bool metricUnits = editor.Sections.RunSpec.GetAll("SMOG").Any();
There are lots of other useful extensions 'ToList', 'Cast', 'Contains', 'OfType', 'First', 'Last', 'Reverse', 'Union', 'Intersect', 'Aggregate', 'Average', ... For more information, try the following Search