EditChapter 6 - Working with Sets
EditSet Operators
EditListing 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]
Concat example: 1,2,3 concatenated with 3,4,5 –
1 2 3 3 4 5
Union example: 1,2,3 unioned with 3,4,5 –
1 2 3 4 5
Top
EditListing 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
EditListing 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]
Distinct example: 1, 2, 3, 1, 2, 3, 4, 5 –
1 2 3 4 5
Distinct example: one, ONE, One, Two, Two –
one Two
Top
EditListing 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]
Except example: 1,2,3 Except with 3,4,5 –
1 2
Top
EditListing 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]
Intersect example: 1,2,3 Intersect with 3,4,5 –
3
Top
EditListing 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]
User Entry – Ka
Stewart Kagel
Mack Kamph
User Entry – 7
165 737 1656
546 607 5462
848 553 8487
278 918 2789
Top
EditListing 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]
Number of uniqe phonetic names = 4
Top
EditListing 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
EditListing 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]
SymetricExceptWith – 1,2,3 and 3,4,5
1 2 4 5
LINQ XOR – 1,2,3 and 3,4,5
1 2 4 5
Top
EditListing 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]
IsSupersetOf = False
IsProperSupersetOf = False
IsSubsetOf = True
IsProperSubsetOf = True
Overlaps = True
SetEquals = False
LINQ IsSupersetOf = False
LINQ IsProperSupersetOf = False
LINQ IsSubsetOf = True
LINQ IsProperSubsetOf = True
LINQ Overlaps = True
LINQ SetEquals = False
Top