<jupyter_start><jupyter_text>Session 6: LINQ and Extension Methods[LINQ (Language Integrated Query)](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/?WT.mc_id=visualstudio-twitch-jefritz) is a collection of methods and language features that allow you to interact with collections of data.  In our last session, we focused on **LINQ to Objects** which allows us to use method predicates to interact with those collections.Let's setup our `Card` class and `FritzSet` collection object to work with again in this workbook<jupyter_code>class Card {
    public Card(string def) {
        var values = def.Split('-');
        Rank = values[0];
        Suit = values[1];
    }
    public string Rank;
    public int RankValue { 
        get { 
            var faceCards = new Dictionary<string,int> { {"J", 11}, {"Q", 12}, {"K", 13}, {"A", 14} };
            return faceCards.ContainsKey(Rank) ? faceCards[Rank] : int.Parse(Rank); 
        }
    }
    public string Suit;
    public override string ToString() {
        return $"{Rank}-{Suit}";
    }
    
    private static bool IsLegalCardNotation(string notation) {
        var segments = notation.Split('-').Length;
        if (segments != 2) return false;

        var validSuits = new [] {"c","d","h","s"};
        if (!validSuits.Any(s => s == segments[1])) return false;
        
        var validRanks = new [] {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
        if (!validRanks.Any(r => r == segments[0])) return false;
        return true;
        
    }
    
    public static implicit operator Card(string id) {
        if (IsLegalCardNotation(id)) return new Card(id);
        return null;
    }
}

class FritzSet<T> : IEnumerable<T> {

    private List<T> _Inner = new List<T>();

    public IEnumerator<T> GetEnumerator()
    {
        return _Inner.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _Inner.GetEnumerator();
    }

    public FritzSet<T> Add(T newItem) {
        var insertAt = _Inner.Count == 0 ? 0 : new Random().Next(0,_Inner.Count+1);
        _Inner.Insert(insertAt, newItem);
        return this;
    }
    
    public FritzSet<T> Shuffle() {
        _Inner = _Inner.OrderBy(_ => Guid.NewGuid()).ToList();
        return this;
    }
    
}

var TheDeck = new FritzSet<Card>();
TheDeck.Add("A-c").Add("A-d");TheDeck.Add("A-h");TheDeck.Add("A-s");TheDeck.Add("2-c");TheDeck.Add("2-d");TheDeck.Add("2-h");TheDeck.Add("2-s");TheDeck.Add("3-c");TheDeck.Add("3-d");TheDeck.Add("3-h");TheDeck.Add("3-s");TheDeck.Add("4-c");TheDeck.Add("4-d");TheDeck.Add("4-h");TheDeck.Add("4-s");
TheDeck.Add("5-c");TheDeck.Add("5-d");TheDeck.Add("5-h");TheDeck.Add("5-s");TheDeck.Add("6-c");TheDeck.Add("6-d");TheDeck.Add("6-h");TheDeck.Add("6-s");TheDeck.Add("7-c");TheDeck.Add("7-d");TheDeck.Add("7-h");TheDeck.Add("7-s");TheDeck.Add("8-c");TheDeck.Add("8-d");TheDeck.Add("8-h");TheDeck.Add("8-s");
TheDeck.Add("9-c");TheDeck.Add("9-d");TheDeck.Add("9-h");TheDeck.Add("9-s");TheDeck.Add("10-c");TheDeck.Add("10-d");TheDeck.Add("10-h");TheDeck.Add("10-s");TheDeck.Add("J-c");TheDeck.Add("J-d");TheDeck.Add("J-h");TheDeck.Add("J-s");
TheDeck.Add("Q-c");TheDeck.Add("Q-d");TheDeck.Add("Q-h");TheDeck.Add("Q-s");TheDeck.Add("K-c");TheDeck.Add("K-d");TheDeck.Add("K-h");TheDeck.Add("K-s");

// TheDeck
TheDeck.Shuffle().Shuffle().Shuffle().Shuffle().Shuffle();
//TheDeck

Card PriyanksCard = "Joker"; // Fix this
//display(PriyanksCard ?? "No card assigned");<jupyter_output><empty_output><jupyter_text>In review, we can write a little bit of code to work with this collection to deal cards appropriately for a Texas Hold 'em poker game:<jupyter_code>var ourDeck = TheDeck.Shuffle().Shuffle();

var hand1 = new List<Card>();
var hand2 = new List<Card>();
var hand3 = new List<Card>();
hand1.Add(ourDeck.Skip(1).First());
hand2.Add(ourDeck.Skip(2).First());
hand3.Add(ourDeck.Skip(3).First());
hand1.Add(ourDeck.Skip(4).First());
hand2.Add(ourDeck.Skip(5).First());
hand3.Add(ourDeck.Skip(6).First());

display("Hand 1");
display(hand1);
display("Hand 2");
display(hand2);
display("Hand 3");
display(hand3);

// Burn a card and deal the next 3 cards called 'the flop'
display("The Flop");
display(ourDeck.Skip(8).Take(3));
    
// Burn a card and take one card called 'the turn'
display("The Turn");
display(ourDeck.Skip(12).First());

// Burn a card and take the final card called 'the river'
display("The River");
display(ourDeck.Skip(14).First());<jupyter_output><empty_output><jupyter_text>Language Integrated QueryYou can build [expressions](https://docs.microsoft.com/dotnet/csharp/linq/query-expression-basics?WT.mc_id=visualstudio-twitch-jefritzwhat-is-a-query-and-what-does-it-do) in the middle of your C code that _LOOKS_ like SQL turned sideways.  Query Expressions begin with a `from` clause and there's also a mandatory `select` clause to specify the values to return.  By convention, many C developers who use this syntax align the clauses to the right of the `=` symbol.  Let's dig into that syntax a bit more:<jupyter_code>// The simplest query
var outValues = from card in TheDeck // the required collection we are querying
                select card;         // the values to be returned

outValues<jupyter_output><empty_output><jupyter_text>Where and OrderBy clausesThat's a boring and non-productive query.  You can start to make queries more interesting by adding a [where](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/where-clause?WT.mc_id=visualstudio-twitch-jefritz) clause with an appropriate test in a format similar to that you would find in an `if` statement.  You can also optionally add an [orderby](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/orderby-clause?WT.mc_id=visualstudio-twitch-jefritz) clause with an **ALSO** optional [descending](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/descending?WT.mc_id=visualstudio-twitch-jefritz) keyword.  Tinker with the query in the next block to learn more about these clauses<jupyter_code>var results = from card in TheDeck
              where card.Suit == "h"    // Return just the Hearts
              orderby card.RankValue descending
              select card;
              
results<jupyter_output><empty_output><jupyter_text>Additionally, nothing is requiring you to return the object in the collection.  You can return different properties and values by changing up the `select` clause:<jupyter_code>var results = from card in TheDeck
              where card.Suit == "h" && card.RankValue > 10
              select card.Rank;
              
results<jupyter_output><empty_output><jupyter_text>JoinsJust like SQL syntax, you can correlate two collections and work with the combined result. The [Join keyword](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/join-clause?WT.mc_id=visualstudio-twitch-jefritz) allows you to relate two collections based on a matching key value in each collection.  There is a similar [Join method in LINQ to Objects](https://docs.microsoft.com/dotnet/api/system.linq.enumerable.join?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) that delivers the same feature.  Joins are slightly more involved and can be confusing topic, and we've embedded the official sample from the docs here.  This sample relates `Person` records to their `Pets` that they own.  The `Join` method receives each collection and uses two expression bodied members to select the key properties from each collection.  Finally, it provides a projection method to create the resultant object.I have annotated this sample and the `Join` method to make it clearer<jupyter_code>class Person
{
    public string Name { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

    Person magnus = new Person { Name = "<NAME>" };
    Person terry = new Person { Name = "<NAME>" };
    Person charlotte = new Person { Name = "<NAME>" };

    // Declare the set of 4 pets and their owners
    Pet barley = new Pet { Name = "Barley", Owner = terry };
    Pet boots = new Pet { Name = "Boots", Owner = terry };
    Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
    Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

    List<Person> people = new List<Person> { magnus, terry, charlotte };
    List<Pet> pets = new List<Pet> { barley, boots, whiskers, daisy };

    // Create a list of Person-Pet pairs where
    // each element is an anonymous type that contains a
    // Pet's name and the name of the Person that owns the Pet.
    var query =
        people.Join(pets,                 // Join the People and Pets collections
                    person => person,     // We will match the Person object
                    pet => pet.Owner,     // with the Owner property in the Pet record
                    (person, pet) =>      // The combined output of Person and Pet
                                          // is an object with OwnerName and the Pet's Name
                        new { OwnerName = person.Name, Pet = pet.Name });

    foreach (var obj in query)
    {
        display(string.Format("{0} - {1}",
            obj.OwnerName,
            obj.Pet));
    }<jupyter_output><empty_output><jupyter_text>Grouping data with the Group clauseData in your query can be grouped together using the [group clause](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/group-clause?WT.mc_id=visualstudio-twitch-jefritz).  The `group` clause can be used in place of the `select` clause or can be used with the `select` clause to aggregate data in various groupings.  Let's try using the `group` keywords<jupyter_code>var results = from card in TheDeck
              group card by card.Suit;

display(results.GetType());
results<jupyter_output><empty_output><jupyter_text>Interestingly, we are returned a collection with all of the cards grouped by their suits.  If we also wanted to select the suit and create a grouped result we could expand our query like this:<jupyter_code>var results = from card in TheDeck
              group card by card.Suit into suit
              select new {TheSuit=suit.Key, suit};

display(results.GetType());
results<jupyter_output><empty_output><jupyter_text>Now this is **VERY INTERESTING** we have created an [Anonymous Type](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types?WT.mc_id=visualstudio-twitch-jefritz), a type on the fly that contains a string field for `TheSuit` and a collection of `Card` objects in a field called `suit`.  We'll get more into **Anonymous Types** next week, but you need to know that you can use the `new` keyword with curly braces `{ }` to create a type and make it available in your code.  Many C veterans will recommend against exposing the anonymous type outside of the method it is created in and instead suggest creating a concrete type to return in that `select` clause. Our groupings can take some interesting calculations.  Let's write a grouping for all of the face cards (and the Ace too):<jupyter_code>var results = from card in TheDeck
              group card by card.RankValue > 10 into facecards
              select new {TheSuit=facecards.Key, facecards};

results<jupyter_output><empty_output><jupyter_text>That looks strange, but we have two groups:  1 group that are the numeric cards and a second group that are the face cards.  Let's tinker with that method a little more:<jupyter_code>var results = from card in TheDeck
              where card.RankValue > 10
              group card by card.Rank into facecards
              select new {Face=facecards.Key, facecards};

results<jupyter_output><empty_output><jupyter_text>Now this sets up for a simplified **Sam the Bellhop** classic card trick.  Take a few minutes and enjoy magician and former Philadelphia Eagles player [<NAME> performing this trick](https://www.youtube.com/watch?v=fwKPDrtgXRs) where he sorts and finds cards while telling the story of Sam the Bellhop.  Loading data from CSVWe've worked with objects and data that we've specified here in the notebook. Let's use an external library, in .NET we call them **NuGet Packages** from www.nuget.org called [LINQtoCSV](https://www.nuget.org/packages/LinqToCsv/) to load Atlantic Hurricane Season data (courtesy of [Wikipedia](https://en.wikipedia.org/wiki/Atlantic_hurricane_season)).<jupyter_code>#r "nuget:LinqToCsv"
using LINQtoCSV;

class MyDataRow { 
    [CsvColumn(Name = "Year", FieldIndex = 1)]
    public int Year {get; set;}
    [CsvColumn(Name = "Number of tropical storms", FieldIndex = 2)]
    public byte TropicalStormCount { get; set;}
    [CsvColumn(Name = "Number of hurricanes", FieldIndex = 3)]
    public byte HurricaneCount { get; set;}
    [CsvColumn(Name = "Number of major hurricanes", FieldIndex = 4)]
    public byte MajorHurricaneCount { get; set;}
    
    // Accumulated Cyclone Energy
    [CsvColumn(Name = "ACE", FieldIndex = 5)]
    public decimal ACE { get; set; }
    
    [CsvColumn(Name = "Deaths", FieldIndex = 6)]
    public int Deaths { get; set; }

    [CsvColumn(Name="Strongest storm", FieldIndex = 7)]
    public string StrongestStorm { get; set; }
    
    [CsvColumn(Name = "Damage USD", FieldIndex = 8)]
    public string DamageUSD { get; set; }

    [CsvColumn(Name = "Retired names", FieldIndex = 9)]
    public string RetiredNames { get; set; }

    [CsvColumn(Name = "Notes", FieldIndex = 10)]
    public string Notes { get; set; }

    
}
var inputFileDescription = new CsvFileDescription
{
    SeparatorChar = ',', 
    FirstLineHasColumnNames = true
};
var context = new CsvContext();
var hurricanes = context.Read<MyDataRow>("data/atlantic_hurricanes.csv", inputFileDescription);
display(hurricanes.OrderByDescending(h => h.Year).Take(10).Select(h => new {h.Year, h.TropicalStormCount, h.HurricaneCount, h.StrongestStorm}));
var results = from storm in hurricanes
              orderby storm.DamageUSD descending
              where storm.HurricaneCount >= 10
              select new {storm.Year, storm.HurricaneCount, storm.ACE, storm.StrongestStorm, storm.DamageUSD};

results<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12424370339102293, 'fraction_numerical': 0.008653440270156184, 'mean_word_length': 3.6867787668974614, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 78, 'https://': 12, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '38234', 'license': 'MIT', 'n_tokens_mistral': 4579, 'n_tokens_neox': 4364, 'n_words': 1348, 'path': 'notebooks/0106-LINQ.ipynb', 'repo_name': 'VincentVerweij/csharp_with_csharpfritz', 'size': '14245'}