The IEnumerable and IQueryable are used to hold a collection of data and also used to perform data manipulation operations such as filtering, Ordering, Grouping, etc.
The main difference is this;
We can use SQL Profiler to confirm this behavior.
Source, Ingest, Prepare, Analyze and Consume
The IEnumerable and IQueryable are used to hold a collection of data and also used to perform data manipulation operations such as filtering, Ordering, Grouping, etc.
The main difference is this;
We can use SQL Profiler to confirm this behavior.
A short list of LINQ operators.
Using IN clause
This is similar to database IN keyword;
var myInClause = new string[] {"One", "Two", "Three"};
var results = from x in MyTable
where myInClause.Contains(x.SomeColumn)
select x;
// OR
var results = MyTable.Where(x => myInClause.Contains(x.SomeColumn));
Using ALL operator
Working with simple types
//does all numbers are greater than 10
int[] IntArray = { 11, 22, 33, 44, 55 };
var Result = IntArray.All(x => x > 10);
Console.WriteLine("Is All Numbers are greater than 10 : " + Result);
//does all names has characters greater than three
string[] stringArray = { "Khan", "Ali", "Adam", "Eve", "Joe" };
Result = stringArray.All(name => name.Length > 3);
Console.WriteLine("Is All Names are greater than 3 Characters : " + Result);
var letterResult = stringArray.All(name => name.StartsWith("A"));
Console.WriteLine("Is All Names start with letter A : " + letterResult);
//all numbers can be divided by three
int[] numbers = { 3, 6, 9, 12};
bool iSNumbersDivided = numbers.All(number => number % 3 == 0);
Console.WriteLine($"Numbers are divisible by three = {iSNumbersDivided}");
Working with complex types
//Check whether age of all animals in the zoo is greater than 1 year
bool response = animalData.All(x => x.AnimalAge > 1);
Console.WriteLine($"Is All Animals are greater than 1 years old : {response}");
//get all animas who are feed by milk
var zooSubSet = animalData.Where(x => x.Food.All(y => y.FoodType == "Milk"));
foreach(var item in zooSubSet)
{
Console.WriteLine($"Animal Name: {item.AnimalName}");
}
Resources
https://stackoverflow.com/questions/959752/where-in-clause-in-linq
https://coderedirect.com/questions/644629/linq-nested-list-contains
The KeyValue pair class stores a pair of values in a single list.
It’s super easy to create a list of single value. Here is an example;
List<string> firstList = new List<string> {"'cover page$'", "'i# milestones$'", "'ii# tasks$'" };
List<string> secondList = new List<string> { "'cover page$'", "'i# milestones$'", "'ii# tasks$'" };
var exceptList = secondList.Except(firstList);
Console.WriteLine($"\nsingle string: Value in second list that are not in first List");
foreach (var val in exceptList)
{
Console.WriteLine($"single string: {val}");
}
What if we want to store pair of values instead of creating any custom classes? We can use KeyValue pair class;
var parentList = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("v2-2021", "'cover page$'"),
new KeyValuePair<string, string>("v2-2021", "'i# milestones$'"),
new KeyValuePair<string, string>("v2-2021", "'ii# tasks$'"),
new KeyValuePair<string, string>("v2-2021", "'iii# spendplan$'"),
};
var parentSubList = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("v2-2021", "'cover page$'"),
new KeyValuePair<string, string>("v2-2021", "'i# milestones$'"),
new KeyValuePair<string, string>("v2-2021", "'ii# tasks$'"),
};
var exceptList1 = parentSubList.Except(parentList);
Console.WriteLine($"\nparentSubList->parentList: Value in second list that are not in first List");
foreach (var val in exceptList1)
{
Console.WriteLine($"{val}");
}
IsASubset = parentSubList.All(i => parentList.Contains(i));
Console.WriteLine($"\nparentSubList->parentList: all members of subset (parentSubList) exists in list1 (parentList): {IsASubset}");
}
KeyValue pair class can also be used like this;
var myList = new List<KeyValuePair<string, string>>();
//add elements now
myList.Add(new KeyValuePair<string, string>("v2-2021", "'cover page$'"));
myList.Add(new KeyValuePair<string, string>("v2-2021", "'i# milestones$'"));
myList.Add(new KeyValuePair<string, string>("v2-2021", "'ii# tasks$'"));
foreach (var val in myList)
{
Console.WriteLine($"Another style: {val}");
}
LINQ methods, for example Except can be used without implementing any Comparer classes.
I have a master list;
v2-2021 – ‘cover page$’
v2-2021 – ‘i# milestones$’
v2-2021 – ‘ii# tasks$’
v2-2021 – ‘iii# spendplan$’
I have a sub list;
v2-2021 – ‘cover page$’
v2-2021 – ‘i# milestones$’
v2-2021 – ‘ii# tasks$’
I want to make sure that all elements in my sub list exists in master list.
To solve this i have created this class;
internal class ExcelVersions
{
public string VersionNumber { get; set; }
public string TableName { get; set; }
}
I have created following objects based on this class;
List<ExcelVersions> cfirstList = new List<ExcelVersions>
{
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'cover page$'" },
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'i# milestones$'" },
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'ii# tasks$'" },
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'iii# spendplan$'" }
};
List<ExcelVersions> csecondList = new List<ExcelVersions>
{
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'cover page$'" },
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'i# milestones$'" },
new ExcelVersions { VersionNumber = "v2-2021", TableName = "'ii# tasks$'" }
};
//var cexceptList = csecondList.Except(cfirstList, new ExcelVersionsComparer());
var cexceptList = csecondList.Except(cfirstList);
Console.WriteLine($"\ncSecondList-->cFirstList: Value in second list that are not in first List");
foreach (var val in cexceptList)
{
Console.WriteLine($"{val.TableName}");
}
IsASubset = csecondList.All(i => cfirstList.Contains(i));
Console.WriteLine($"\ncSecondList-->cFirstList: all members of subset (cSecondList) exists in list1 (cFirstList): {IsASubset}");
}
This is the result i get;
To my surprise, none of LINQ comparison method worked on custom class. What’s wrong? The answer is in the LINQ implementation. To be correctly processed by the Except method, a type must implement the IEquatable<T> interface and provide its own Equals and GetHashCode methods.
Re-writing out custom type;
internal class ExcelVersions : IEquatable<ExcelVersions>
{
public string VersionNumber { get; set; }
public string TableName { get; set; }
public bool Equals(ExcelVersions other)
{
//check whether the compare object is null
if (Object.ReferenceEquals(other, null)) return false;
//check whether the compared object references the same data
if (Object.ReferenceEquals(this, other)) return true;
//check whether the object's properteis are equal
return VersionNumber.Equals(other.VersionNumber) && TableName.Equals(other.TableName);
}
//if Equals returns true for a pair of objects
//GetHashCode must return the same value for these objects
public override int GetHashCode()
{
//Get the hash code for the version number
int hashVersionNumber = VersionNumber == null ? 0 : VersionNumber.GetHashCode();
//get the hash code for the table name
int hashTableName = TableName.GetHashCode();
//calculate the hash code for the object
return hashVersionNumber ^ hashTableName;
}
}
This time the results are;
OK. Custom class is working but what if we cannot modify the type? What if it was provided by a library and we have no way of implementing the IEquiatable<T> interface. The answer is to create our own equality comparer and pass it as a parameter to the Except method.
The equality comparer must implement the IEqualityComparer<T> interface and provide GetHashCode and Equals method like this;
internal class ExcelVersionsComparer : IEqualityComparer<ExcelVersions>
{
public bool Equals(ExcelVersions x, ExcelVersions y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Equals(y);
}
public int GetHashCode(ExcelVersions excelVersion)
{
if (Object.ReferenceEquals(excelVersion, null)) return 0;
int hashVersion = excelVersion.VersionNumber == null ? 0 : excelVersion.GetHashCode();
int hashTable = excelVersion.TableName.GetHashCode();
return hashVersion ^ hashTable;
}
}
This is how we are going to pass the comparer to the Except method;
var cexceptList = csecondList.Except(cfirstList, new ExcelVersionsComparer());
These rules don’t just apply to Except method. For example, the same is true for the Distinct, Contains, Interset and Union methods. Generally, if you see that a LINQ method has an overload that accepts the IEqualityComparer<T> parameter, means that to use it with your own data type, you need to either implement IEquatable<T> in your class or create your own equality comparer.
If you want to use built-in class instead of creating custom class, consider this class;
Reference
https://stackoverflow.com/questions/16824749/using-linq-except-not-working-as-i-thought
https://grantwinney.com/how-to-compare-two-objects-testing-for-equality-in-c/
https://www.tutorialspoint.com/how-to-find-items-in-one-list-that-are-not-in-another-list-in-chash