Hooked on LINQ

Hooked on LINQ - Developers' Wiki
for .NET Language Integrated Query

Companion book for this site
LINQ to Objects Using C# 4.0:
Using and Extending LINQ to Objects and Parallel LINQ (PLINQ)
Quick Search

Advanced Search »
Edit

Chapter 6 - Working with Sets





Edit

Set Operators

Edit

Listing 6-1 : Concat and Union Operators

This sample demonstrates the most basic Concat and Union LINQ set operators.

public void Listing_6_1_ConcatAndUnionOperators()
{
    int[] first = new int[] { 1, 2, 3 };
    int[] second = new int[] { 3, 4, 5 }; 
 
    // concat returns elements from both collections
    var q = first.Concat(second);
 
    Console.WriteLine(
        "Concat example: 1,2,3 concatenated with 3,4,5 – ");
 
    foreach (var item in q)
        Console.Write(item + " ");
 
    // union returns the distinct concat of the collections
    var q1 = first.Union(second);
 
    Console.WriteLine();
    Console.WriteLine(
        "Union example: 1,2,3 unioned with 3,4,5 – ");
 
    foreach (var item in q1)
        Console.Write(item + " ");
}
 

Console output (Execution time: 23ms): [Hide/Show]


Top



Edit

Listing 6-2 : Concat Operator for adding extra values to a sequence.

This sample demonstrates how to add additional entries to a collection using the concat operator.

public void Listing_6_2_ConcatOperator()
{
    // the actual list of status
    string[] status = new string[] { 
        "Not Started", "Started", "Complete" };
 
    // the desired first entry
    string[] prompt = new string[] {
        "–– none chosen ––"};
 
    ComboBox combo = new ComboBox();
 
    // this is the where the two sequences get
    // combined and bound to the combobox
    combo.DataSource = prompt.Concat(status).ToList();
 
    // display resulting combo in test form.
    Form form = new Form();
    form.Controls.Add(combo);
    form.ShowDialog();
}
 



Top



Edit

Listing 6-3 : Distinct

This sample demonstrates the most basic Distinct LINQ set operators.

public void Listing_6_3_DistinctOperator()
{
    int[] source = new int[] { 1, 2, 3, 1, 2, 3, 4, 5 };
 
    // distinct de–duplicates a collection
    var q = source.Distinct();
 
    Console.WriteLine(
        "Distinct example: 1, 2, 3, 1, 2, 3, 4, 5 – ");
 
    foreach (var item in q)
        Console.Write(item + " ");
 
    // distinct on string using comparer
    string[] names = new string[] 
        { "one", "ONE", "One", "Two", "Two" };
 
    /* built–in string comparer statics are helpful –
     * 
     * StringComparer.CurrentCulture: Case–sensitive string comparison 
     * using the word comparison rules of the current culture.
     * 
     * StringComparer.CurrentCultureIgnoreCase: Case–insensitive string 
     * comparison using the word comparison rules of the current culture.
     * 
     * StringComparer.InvariantCulture: Case–sensitive string comparison 
     * using the word comparison rules of the invariant culture.
     * 
     * StringComparer.InvariantCultureIgnoreCase: Case–insensitive string 
     * comparison using the word comparison rules of the invariant culture.
     * 
     * StringComparer.Ordinal: Case–sensitive ordinal string comparison.
     * 
     * StringComparer.OrdinalIgnoreCase: Case–insensitive ordinal string 
     * comparison.
     */
 
    var q1 = names.Distinct(
        StringComparer.CurrentCultureIgnoreCase);
 
    Console.WriteLine();
    Console.WriteLine(
        "Distinct example: one, ONE, One, Two, Two – ");
 
    foreach (var item in q1)
        Console.Write(item + " ");
}
 

Console output (Execution time: 9ms): [Hide/Show]


Top



Edit

Listing 6-4 : Except Operator

This sample demonstrates the most basic use of the Except LINQ set operator.

public void Listing_6_4_ExceptOperator()
{
    int[] first = new int[] { 1, 2, 3 };
    int[] second = new int[] { 3, 4, 5 };
 
    // except returns all elements from the first collection
    // that are not in the second collection.
    var q = first.Except(second);
 
    Console.WriteLine(
        "Except example: 1,2,3 Except with 3,4,5 – ");
 
    foreach (var item in q)
        Console.Write(item + " ");
}
 

Console output (Execution time: 10ms): [Hide/Show]


Top



Edit

Listing 6-5 : Intersect Operator

This sample demonstrates the most basic use of the Intersect LINQ set operator.

public void Listing_6_5_IntersectOperator()
{
    int[] first = new int[] { 1, 2, 3 };
    int[] second = new int[] { 3, 4, 5 };
 
    // intersect returns only elements from the first collection
    // collection that are ALSO in the second collection.
    var q = first.Intersect(second);
    Console.WriteLine(
        "Intersect example: 1,2,3 Intersect with 3,4,5 – ");
 
    foreach (var item in q)
        Console.Write(item + " ");
}
 

Console output (Execution time: 11ms): [Hide/Show]


Top



Edit

Listing 6-6 : Anonymous Type Union

This sample demonstrates how multiple sources can be joined using union and anonymous types.

public void Listing_6_6_AnonymousTypesUnion()
{
    // lookup recent  phone number OR contact first and last 
    // names to incrementally build a convenient  picklist on 
    // partial user entry (narrow the list as data is typed).
    string userEntry = "Ka";
 
    var q = (
 
             // userEntry is contact name               
             from contact in Contact.SampleData()
             where contact.FirstName.StartsWith(userEntry)
                || contact.LastName.StartsWith(userEntry)
             select new { Display = contact.FirstName + " " + 
                                    contact.LastName }).Distinct()
 
            .Union(
 
            // userEntry is partial phone number
            (from call in CallLog.SampleData()
             where call.Number.Contains(userEntry)
                && call.Incoming == false
             select new { Display = call.Number }).Distinct()
 
            );
 
    Console.WriteLine(
        "User Entry – " + userEntry);
 
    foreach (var item in q)
        Console.WriteLine(item.Display);
 
 
    // numeric entry
    userEntry = "7";
 
    var q1 = (
        // userEntry is contact name               
             from contact in Contact.SampleData()
             where contact.FirstName.StartsWith(userEntry)
                || contact.LastName.StartsWith(userEntry)
             select new { Display = contact.FirstName + " " + contact.LastName })
 
            .Union(
 
            // userEntry is partial phone number
            from call in CallLog.SampleData()
            where call.Number.Contains(userEntry)
            select new { Display = call.Number }
 
            );
 
    Console.WriteLine();
    Console.WriteLine(
        "User Entry – " + userEntry);
 
    foreach (var item in q1)
        Console.WriteLine(item.Display);
}
 

Console output (Execution time: 26ms): [Hide/Show]


Top



Edit

Listing 6-7 : Custom Equality Comparer - Soundex

This sample demonstrates how to use a custom Equality Comparer in set operators.

public void Listing_6_7_SetCustomEqualityComparer()
{
    // find number of phonetic common names in list
    string[] names = new string[] { "Janet", "Janette", "Joanne", 
        "Jo–anne", "Johanne", "Katy", "Katie", "Ralph", "Ralphe" };
 
    var q = names.Distinct(
        new SoundexEqualityComparer());
 
    Console.WriteLine("Number of uniqe phonetic names = {0}",
        q.Count());
}
 
public class SoundexEqualityComparer
    : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return GetHashCode(x) == GetHashCode(y);
    }
 
    public int GetHashCode(string obj)
    {
        // E.g. convert soundex code A123,
        // to an integer: 65123
        int result = 0;
 
        string s = soundex(obj);
        if (string.IsNullOrEmpty(s) == false)
            result = Convert.ToInt32(s[0]) * 1000 +
                     Convert.ToInt32(s.Substring(1, 3));
 
        return result;
    }
 
    private string soundex(string s)
    {
        // Algorithm as listed on 
        //     http://en.wikipedia.org/wiki/Soundex.
        // builds a string code in the format: 
        //     A–Z0–60–60–6
        // based on the phonetic sound of the input.
 
        if (String.IsNullOrEmpty(s))
            return null;
 
        StringBuilder result =
            new StringBuilder();
 
        string source = s.ToUpper().Replace(" ", "");
 
        // add the first character, then loop the
        // string mapping as we go
        result.Append(source[0]);
        char previous = '0';
 
        for (int i = 1; i < source.Length; i++)
        {
            // map to the soundex numeral
            char mappedTo = '0';
            char thisChar = source[i];
            if ("BFPV".Contains(thisChar))
                mappedTo = '1';
            else if ("CGJKQSXZ".Contains(thisChar))
                mappedTo = '2';
            else if ("DT".Contains(thisChar))
                mappedTo = '3';
            else if ('L' == thisChar)
                mappedTo = '4';
            else if ("MN".Contains(thisChar))
                mappedTo = '5';
            else if ('R' == thisChar)
                mappedTo = '6';
 
            // ignore adjacent duplicates and 
            // non–matched characters
            if (mappedTo != previous && mappedTo != '0')
            {
                result.Append(mappedTo);
                previous = mappedTo;
            }
        }
 
        while (result.Length < 4)
            result.Append("0");
 
        return result.ToString(0, 4);
    }
}
 

Console output (Execution time: 6ms): [Hide/Show]


Top



Edit

Listing 6-8 : HashSet

This sample demonstrates how to use HashSet in a LINQ query.

public void Listing_6_8_HashSetAndLINQ()
{
    int[] first = new int[] { 1, 2, 3 };
    int[] second = new int[] { 3, 4, 5 };
 
    // modify the current set by unioning with second.
    HashSet<int> set = new HashSet<int>(first);
    set.UnionWith(second);
 
    // return only the even values from the set.
    // the values in the HashSet are IEnumerable<T>.
    var q = from i in set
            where i % 2 == 0
            select i;
 
    foreach (var item in q)
        Console.WriteLine(item);
}
 

Console output (Execution time: 25ms): [Hide/Show]


Top



Edit

Listing 6 : SymetricExceptWith (XOR)

This sample demonstrates how to create an ExclusiveOR LINQ query.

public void Listing_6_ExclusiveOr()
{
    int[] first = new int[] { 1, 2, 3 };
    int[] second = new int[] { 3, 4, 5 };
    
    // Modify the current set by unioning with second.
    HashSet<int> set = new HashSet<int>(first);
    set.SymmetricExceptWith(second);
 
    Console.WriteLine("SymetricExceptWith – 1,2,3 and 3,4,5");
    foreach (var item in set)
        Console.Write(item + " ");
 
    // concat two reciprocal except operations
    var q = first.Except(second)
            .Concat(
            second.Except(first));
 
    Console.WriteLine();
    Console.WriteLine("LINQ XOR – 1,2,3 and 3,4,5");
    foreach (var item in q)
        Console.Write(item + " ");
}
 

Console output (Execution time: 52ms): [Hide/Show]


Top



Edit

Listing 6 : Subsets and Supersets

This sample demonstrates how to determine subsets and supersets.

public void Listing_6_SuperSetAndSubset()
{
    int[] first = new int[] { 1, 2, 3, 4, 5, 5 };
    int[] second = new int[] { 5, 4, 3, 2, 1, 1, 6 };
 
    // Hashset
    HashSet<int> set = new HashSet<int>(first);
    Console.WriteLine("IsSupersetOf = {0}", set.IsSupersetOf(second));
    Console.WriteLine("IsProperSupersetOf = {0}", set.IsProperSupersetOf(second));
    
    HashSet<int> set2 = new HashSet<int>(second);
    Console.WriteLine("IsSubsetOf = {0}", set2.IsSupersetOf(first));
    Console.WriteLine("IsProperSubsetOf = {0}", set2.IsProperSupersetOf(first));
    Console.WriteLine("Overlaps = {0}", set2.Overlaps(first));
    Console.WriteLine("SetEquals = {0}", set.SetEquals(second));
 
    // LINQ Custom Extensions
    Console.WriteLine("LINQ IsSupersetOf = {0}", first.IsSupersetOf(second));
    Console.WriteLine("LINQ IsProperSupersetOf = {0}", first.IsProperSupersetOf(second));
    Console.WriteLine("LINQ IsSubsetOf = {0}", second.IsSupersetOf(first));
    Console.WriteLine("LINQ IsProperSubsetOf = {0}", second.IsProperSupersetOf(first));
    Console.WriteLine("LINQ Overlaps = {0}", second.Overlaps(first));
    Console.WriteLine("LINQ SetEquals = {0}", first.SetEquals(second));
}
 
public static class LINQSetOperatorExtensions
{
    public static IEnumerable<T> SymmetricExcept<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second)
    {
        if (first == null) 
            throw new ArgumentNullException("first");
        
        if (second == null) 
            throw new ArgumentNullException("second");
 
        return  first.Except(second)
                .Concat(
                second.Except(first));
    }
 
    public static bool Overlaps<T>(
        this IEnumerable<T> first,
        IEnumerable<T> second)
    {
        if (first == null)
            throw new ArgumentNullException("first");
 
        if (second == null)
            throw new ArgumentNullException("second");
 
        return second.Intersect(first).Distinct().Any();
    }
 
    public static bool IsSupersetOf<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second)
    {
        if (first == null) 
            throw new ArgumentNullException("first");
 
        if (second == null) 
            throw new ArgumentNullException("second");
 
        var secondCount = second.Distinct().Count();
        
        var intersectCount = second
            .Intersect(first)
            .Distinct()
            .Count();
 
        return intersectCount == secondCount;
    }
 
    public static bool IsProperSupersetOf<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second)
    {
        if (first == null) 
            throw new ArgumentNullException("first");
 
        if (second == null) 
            throw new ArgumentNullException("second");
 
        var firstCount = first.Distinct().Count();
        var secondCount = second.Distinct().Count();
        
        var intersectCount = 
            second
            .Intersect(first)
            .Distinct()
            .Count();
 
        return (intersectCount < firstCount) && 
               (intersectCount == secondCount);
    }
 
    public static bool IsSubsetOf<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second)
    {
        // call the Superset operator and reverse the arguments
        return IsSupersetOf(second, first);
    }
 
    public static bool IsProperSubsetOf<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second)
    {
        // call the Superset operator and reverse the arguments
        return IsProperSupersetOf(second, first);
    }
 
    public static bool SetEquals<T>(
        this IEnumerable<T> first,
        IEnumerable<T> second)
    {
        return first.Distinct().OrderBy(x => x)
            .SequenceEqual(
            second.Distinct().OrderBy(y => y));
    }
}
 

Console output (Execution time: 16ms): [Hide/Show]


Top



If you would like to comment on this page, click on the Discuss button located on the top-right of each page. Feel free to edit any mistakes or omissions you find. If you have an objection or find in-appropriate content then contact the administrator. This website is not affiliated with Microsoft®, all content and opinions are those of the specific author and some advice, solutions and article may contain unintentional errors - please use care. Other websites by this author: Focused Objective, Geek Speak Decoded.