Set Operations and Equality

Set operations produce a result set based on equivalent elements' absence or presence within another collection/set, or within the same collection.

The methods used to perform these operations follow: Distinct, Except, Intersect, and Union.

The Distinct method eliminates duplicate values in a collection. Review its syntax below:

public static IEnumerable<TSource> Distinct<TSource>(
	this IEnumerable<TSource> source
)

Review an example of its use below:

List<int> diagResults = new List<int> { 22, 36, 37, 25, 19, 22, 35, 37 };

IEnumerable<int> distinctVal = diagResults.Distinct();

Console.WriteLine("Distinct diagnostic test results:");

foreach (int val in distinctVal)
{
	Console.WriteLine(val);
}

When working with sequences of custom data type objects, implement IEquatable to return distinct elements. Review an example below:

public class Inventory : IEquatable<Product>
{
	public string Name { get; set; }
	public int ID { get; set; }

	public bool Equals(Inventory other)
	{
				
		//Check for null.
		if (Object.ReferenceEquals(other, null)) return false;
				
		//Check for references to the same data.
		if (Object.ReferenceEquals(this, other)) return true;
				
		//Check for properties' equality.
		return ID.Equals(other.ID) && Name.Equals(other.Name);
	}
				
	
	// If Equals() evaluates true for object pair,
	// GetHashCode() must return an identical value for all objects.

	public override int GetHashCode()
	{
		//If not null, get hash code for Name
		int hashItemName = Name == null ? 0 : Name.GetHashCode();

		//Get hash code for ID
		int hashItemCode = ID.GetHashCode();
				
		//Calculate the hash code
		return hashItemName ^ hashItemCode;
	}
}

After implementation, use Distinct:

Inventory[] items = { new Inventory { Name = "plum", ID = 8 },
									  new Inventory { Name = "peach", ID = 2 },
									  new Inventory { Name = "plum", ID = 8 },
									  new Inventory { Name = "pear", ID = 15 } };
				
//Exclude duplicate items
IEnumerable<Inventory> dupliX =
	items.Distinct();

foreach (var item in dupliX)
	Console.WriteLine(item.Name + " " + item.ID);

The Except method returns differing elements of a set meaning the elements appearing in one collection, but not the other. Review its syntax below:

public static IEnumerable<TSource> Except<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

Review an example of its use below:

double[] values1 = { 3.0, 3.1, 3.2, 3.3, 3.4, 3.5 };
double[] values2 = { 3.2 };

IEnumerable<double> FirstSetOnly = values1.Except(values2);

foreach (double value in FirstSetOnly)
	Console.WriteLine(value);

When comparing sequences of custom data type objects, utilize IEqualityComparer:

public class ItemOne
{
	public string Name { get; set; }
	public int ID { get; set; }
}
public class ItemComparer : IEqualityComparer<ItemOne>
{
	public bool Equals(ItemOne x, ItemOne y)
	{
		//Verify objects are different objects
		if (Object.ReferenceEquals(x, y)) return true;

		//Determine if item properties are equal
		return x != null && y != null && x.ID.Equals(y.ID) && x.Name.Equals(y.Name);
	}

	public int GetHashCode(ItemOne obj)
	{
		//If not null, get hash code for Name
		int hashItemName = obj.Name == null ? 0 : obj.Name.GetHashCode();

		//Get hash code for ID
		int hashItemID = obj.ID.GetHashCode();

		//Calculate item hash code
		return hashItemName ^ hashItemID;
	}
}

After implementation, use Except:

ItemOne[] fruityOne = { new ItemOne { Name = "plum", ID = 8 },
						new ItemOne { Name = "peach", ID = 5 },
						new ItemOne { Name = "pear", ID = 11 } };
				
ItemOne[] fruityTwo = { new ItemOne { Name = "plum", ID = 8 } };
				
//Gather all first array elements, but exclude second array elements.
IEnumerable<ItemOne> except = fruityOne.Except(fruityTwo);
				
foreach (var item in except)
	Console.WriteLine(item.Name + " " + item.ID);

The Intersect method returns intersection elements meaning elements appearing in both collections. Review its syntax below:

public static IEnumerable<TSource> Intersect<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

Review an example of its use below:

int[] IDxone = { 427, 125, 112, 226, 361, 418 };
int[] IDxtwo = { 309, 509, 480, 427, 226, 224, 130 };

IEnumerable<int> bothCol = IDxone.Intersect(IDxtwo);

foreach (int id in bothCol)
	Console.WriteLine(id);

In comparisons of sequences of custom data type objects, implement IEqualityComparer:

public class ItemOne
{
	public string Name { get; set; }
	public int ID { get; set; }
}

public class ItemComparer : IEqualityComparer<ItemOne>
{
	public bool Equals(ItemOne x, ItemOne y)
	{
		//Verify objects are different
		if (Object.ReferenceEquals(x, y)) return true;

		//Determine if item properties are equal.
		return x != null && y != null && x.ID.Equals(y.ID) && x.Name.Equals(y.Name);
	}

	public int GetHashCode(ItemOne obj)
	{
		//If not null, get hash code for Name
		int hashItemName = obj.Name == null ? 0 : obj.Name.GetHashCode();

		//Get hash code for the Code field.
		int hashItemID = obj.ID.GetHashCode();

		//Calculate item hash code
		return hashItemName ^ hashItemID;
	}
}

After implementation, use Intersect:

ItemOne[] shop1 = { new ItemOne { Name = "pear", ID = 8 },
					new ItemOne { Name = "plum", ID = 3 } };
ItemOne[] shop2 = { new ItemOne { Name = "pear", ID = 8 },
					new ItemOne { Name = "peach", ID = 11 } };
IEnumerable<ItemOne> duplicates = shop1.Intersect(shop2, new ItemComparer());
foreach (var item in duplicates)
	Console.WriteLine(item.Name + " " + item.ID);

The Union method returns union elements meaning unique elements of both collections. Review its syntax below:

public static IEnumerable<TSource> Union<TSource>(
	this IEnumerable<TSource> first,
	IEnumerable<TSource> second
)

Review an example of its use below:

int[] valz1 = { 1, 6, 4, 7, 1, 9, 3, 7 };
int[] valz2 = { 8, 3, 6, 8, 2, 4, 5, 0 };

IEnumerable<int> union = valz1.Union(valz2);

foreach (int val in union)
{
	Console.Write("{0} ", val);
}

In comparisons of sequences of custom data type objects, implement the IEqualityComparer as seen with the Except method. After implementation, use Union:

ItemOne[] shop1 = { new ItemOne { Name = "pear", ID = 8 },
					new ItemOne { Name = "plum", ID = 3 } };

ItemOne[] shop2 = { new ItemOne { Name = "pear", ID = 8 },
					new ItemOne { Name = "peach", ID = 11 } };

IEnumerable<ItemOne> union = shop1.Union(shop2);

foreach (var item in union)
	Console.WriteLine(item.Name + " " + item.ID);

EQUALITY

Equality operations simply test for the equality of two sequences by comparing elements and element count. These operations utilize the SequenceEqual method to determine the equality of two sequences through pair-wise comparisons. Review its syntax below:

public static bool SequenceEqual<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second
)

Review an example of its use below:

class Plants
{
	public string Name { get; set; }
	public int Age { get; set; }
}

public static void Example()
{
	Plants plant1 = new Plants { Name = "Chili", Age = 5 };
	Plants plant2 = new Plants { Name = "Thyme", Age = 3 };

	// Make two plant lists
	List<Plants> plants1 = new List<Plants> { plant1, plant2 };
	List<Plants> plants2 = new List<Plants> { plant1, plant2 };

	bool equal = plants1.SequenceEqual(plants2);
	Console.WriteLine(
		"The lists {0} equal.",
		equal ? "are" : "are not");
}

Note comparisons of sequences containing identical data, and objects with different references, do not evaluate as equal.

In comparisons of actual data rather than references, implement IEqualityComparer as seen with the Except method. After implementation, use SequenceEqual:

ItemOne[] shop1 = { new ItemOne { Name = "pear", ID = 8 },
					new ItemOne { Name = "plum", ID = 3 } };

ItemOne[] shop2 = { new ItemOne { Name = "pear", ID = 8 },
					new ItemOne { Name = "plum", ID = 3 } };

bool equal12 = shop1.SequenceEqual(shop2);

Console.WriteLine("Equal? " + equal12);