List contains list - c#

Hi I have the following lists:
var CustomerName = new List<Customer>();
var DummyData = new List<Customer>();
How can I quickly check that DummyData is contained inside of CustomerName? Also performance is key as these lists might contain thousands of values.

Brute Force Method
Use linq all method against the DummyData variable O(N*K)
// If you override Equals and GetHashCode or are comparing by reference
DummyData.All(a=>CustomerName.Contains(a))
//If you compare by property
DummyData.All(a=>
CustomerName.Any(b=>
a.FirstName==b.FirstName &&
a.LastName == b.LastName
//repeat to include checks for all properties
)
);
Using a HashSet
Put your results into a hashset and use linq's All method again checking if hashset contains items, takes N steps to build hashset and K steps to check, complexity is O(N+K)
var hs = new HashSet<Customer>(CustomerName);
DummyData.All(a=>hs.Contains(a));
You will need to override Equals And GetHashCode
If you haven't overriden these two yet you'll need to unless you want to compare properties and this prevents you from using the hash set method as well
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
var customer = obj as Customer;
return customer != null && Equals(customer);
}
protected bool Equals(Customer other)
{
return string.Equals(FirstName, other.FirstName) && string.Equals(LastName, other.LastName);
}
public override int GetHashCode()
{
unchecked
{
return ((FirstName?.GetHashCode() ?? 0)*397) ^ (LastName?.GetHashCode() ?? 0);
}
}
}

Related

How to check if properties of two objects are equal

I have two objects using the ff. class:
public class Test {
public string Name {get; set;}
public List<Input> Inputs {get;set;}
......
//some other properties I don't need to check
}
public class Input {
public int VariableA {get;set;}
public int VariableB {get;set;}
public List<Sancti> Sancts {get;set;}
}
public class Sancti {
public string Symbol {get;set;}
public double Percentage {get;set;}
}
I want to check if two instance of Test has the same Inputs value. I've done this using a loop but I believe this is not the way to do this.
I've read some links: link1, link2 but they seem gibberish for me. Are there simpler ways to do this, like a one-liner something like:
test1.Inputs.IsTheSameAs(test2.Inputs)?
I was really hoping for a more readable method. Preferrably Linq.
NOTE: Order of inputs should not matter.
One way is to check the set negation between the two lists. If the result of listA negated by listB has no elements, that means that everything in listA exists in listB. If the reverse is also true, then the two lists are equal.
bool equal = testA.Inputs.Except(testB.Inputs).Count() == 0
&& testB.Inputs.Except(testA.Inputs).Count() == 0;
Another is to simply check each element of listA and see if it exists in listB (and vice versa):
bool equal = testA.Inputs.All(x => testB.Inputs.Contains(x))
&& testB.Inputs.All(x => testA.Inputs.Contains(x));
This being said, either of these can throw a false positive if there is one element in a list that would be "equal" to multiple elements in the other. For example, the following two lists would be considered equal using the above approaches:
listA = { 1, 2, 3, 4 };
listB = { 1, 1, 2, 2, 3, 3, 4, 4 };
To prevent that from happening, you would need to perform a one-to-one search rather than the nuclear solution. There are several ways to do this, but one way to do this is to first sort both lists and then checking their indices against each other:
var listASorted = testA.Inputs.OrderBy(x => x);
var listBSorted = testB.Inputs.OrderBy(x => x);
bool equal = testA.Inputs.Count == testB.Inputs.Count
&& listASorted.Zip(listBSorted, (x, y) => x == y).All(b => b);
(If the lists are already sorted or if you'd prefer to check the lists exactly (with ordering preserved), then you can skip the sorting step of this method.)
One thing to note with this method, however, is that Input needs to implement IComparable in order for them to be properly sorted. How you implement it exactly is up to you, but one possible way would be to sort Input based on the XOR of VariableA and VariableB:
public class Input : IComparable<Input>
{
...
public int Compare(Input other)
{
int a = this.VariableA ^ this.VariableB;
int b = other.VariableA ^ other.VariableB;
return a.Compare(b);
}
}
(In addition, Input should also override GetHashCode and Equals, as itsme86 describes in his answer.)
EDIT:
After being drawn back to this answer, I would now like to offer a much simpler solution:
var listASorted = testA.Inputs.OrderBy(x => x);
var listBSorted = testB.Inputs.OrderBy(x => x);
bool equal = listASorted.SequenceEqual(listBSorted);
(As before, you can skip the sorting step if the lists are already sorted or you want to compare them with their existing ordering intact.)
SequenceEqual uses the equality comparer for a particular type for determining equality. By default, this means checking that the values of all public properties are equal between two objects. If you want to implement a different approach, you can define an IEqualityComparer for Input:
public class InputComparer : IEqualityComparer<Input>
{
public bool Equals(Input a, Input b)
{
return a.variableA == b.variableA
&& a.variableB == b.variableB
&& ... and so on
}
public int GetHashCode(Input a)
{
return a.GetHashCode();
}
}
You can change your Input and Sancti class definitions to override Equals and GetHasCode. The following solution considers that 2 Inputs are equal when:
VariableA are equal and
VariableB are equal and
The Sancts List are equal, considering that the Sancti elements with the same Symbol must have the same Percentage to be equal
You may need to change this if your specifications are different:
public class Input
{
public int VariableA { get; set; }
public int VariableB { get; set; }
public List<Sancti> Sancts { get; set; }
public override bool Equals(object obj)
{
Input otherInput = obj as Input;
if (ReferenceEquals(otherInput, null))
return false;
if ((this.VariableA == otherInput.VariableA) &&
(this.VariableB == otherInput.VariableB) &&
this.Sancts.OrderBy(x=>x.Symbol).SequenceEqual(otherInput.Sancts.OrderBy(x => x.Symbol)))
return true;
else
{
return false;
}
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + VariableA.GetHashCode();
hash = hash * 23 + VariableB.GetHashCode();
hash = hash * 23 + Sancts.GetHashCode();
return hash;
}
}
}
public class Sancti
{
public string Symbol { get; set; }
public double Percentage { get; set; }
public override bool Equals(object obj)
{
Sancti otherInput = obj as Sancti;
if (ReferenceEquals(otherInput, null))
return false;
if ((this.Symbol == otherInput.Symbol) && (this.Percentage == otherInput.Percentage) )
return true;
else
{
return false;
}
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + Symbol.GetHashCode();
hash = hash * 23 + Percentage.GetHashCode();
return hash;
}
}
}
Doing this, you just have to do this to check if Inputs are equal:
test1.Inputs.SequenceEqual(test2.Inputs);

c# List contain Item

I have a class named accessoire :
class accessoire
{
public int value1 { get; set; }
public string Value2 { get; set; }
}
then i have a List of that product
List<accessoire> accessoires
And i have an UI where the user pick the product he wants from a DataGridview and when he selected it launch an event that add this item to the list :
private void ProductBrowser_OnItemAdded(Accessoire item)
{
if (Cart.Contains(item))
{
MessageBox.Show("Produit deja ajoutée au panier ! ");
}
else
{
Cart.Add(item);
ProductView.Rows.Add(item.Ref, item.Name, Function.CatName(item.Cat), item.SellPrice, "1", Convert.ToDouble(item.SellPrice) * Convert.ToDouble(item.QtetoSell));
TotalPriceSet();
MessageBox.Show("Produit Ajouté !");
}
}
this doesnt work , but when i change it to :
private void ProductBrowser_OnItemAdded(Accessoire item)
{
var InList = Cart.Find(product => product.Ref == item.Ref);
if (Cart.Contains(InList))
{
MessageBox.Show("Product already in list ! ");
}
else
{
Cart.Add(item);
ProductView.Rows.Add(item.Ref, item.Name, Function.CatName(item.Cat), item.SellPrice, "1", Convert.ToDouble(item.SellPrice) * Convert.ToDouble(item.QtetoSell));
TotalPriceSet();
MessageBox.Show("product added !");
}
}
it works , but i'am still wondering why the first code doesnt work it keep adding that item to the list ? in other way how does the method .Contains()works ? what does it check to know if the item is or the list on not ?
The reason it doesn't find the object in the list is because it is a reference comparison, comparing the instances of the object, not the values. You can have two instances of your class with the same values in their properties, but if you compare them, they are not equal:
accessoire item1 = new accessoire();
item1.value1 = 1;
item1.value2 = "one";
accessoire item2 = new accessoire();
item2.value1 = 1;
item2.value2 = "one";
if(item1 == item2) MessageBox.Show("Same");
else MessageBox.Show("Different");
When you select the item from the list to compare with, you are pulling the specific instance, which does exist in the list.
You need to implement the Equals method of accessoire to properly compare all properties/fields in it. The default implementation of Equals uses ReferenceEquals, which only works if the two instances are in fact the same.
if (Cart.Contains(item))
is matching by equality.
If object.Equals(T) is not satisified, it will fail. That means the smallest change, even whitespace in a string, will return false. You'll also get a false result if you have two instances of the same class. Contains must refer to exactly item.
var InList = Cart.Find(product => product.Ref == item.Ref) is a match by property. This means that other properties of the product/item can all be different, as long as .Ref matches. I presume Ref is a primary key, which is why you're not getting problems in your result where Find() returns the wrong item.
You can get around the difference by overriding Equals for Cart, but I don't recommend it. It can make debugging hell later.
Just implement the equals method
// override object.Equals
public override bool Equals(object obj)
{
//
// See the full list of guidelines at
// http://go.microsoft.com/fwlink/?LinkID=85237
// and also the guidance for operator== at
// http://go.microsoft.com/fwlink/?LinkId=85238
//
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var data = (accessoire)obj;
return this.Ref.Equals(data.Ref);
}
// override object.GetHashCode
public override int GetHashCode()
{
return this.Ref.GetHashCode()
}
You were doing reference compare and the references don't match in your first example, but do in your second. You probably want to do equality comparison. Are the values of both objects the same.
Below is your class implemented with the various methods used for equality comparing. You would just need to modify them to suit your purposes.
// IEquatable<T> provides typed equality comparing
class accessoire : IEquatable<accessoire>
{
public int Value1 { get; set; }
public string Value2 { get; set; }
// you can override Equals.
public override bool Equals(object obj)
{
return this.Equals(obj as accessoire);
}
// this comes from IEquatable<T>
public bool Equals(accessoire other)
{
if (ReferenceEquals(null, other))
{
return false;
}
// return the comparison that makes them equal.
return
this.Value1.Equals(this.Value1) &&
this.Value2.Equals(this.Value2);
}
public override int GetHashCode()
{
unchecked
{
int hash = 37;
hash *= 23 + this.Value1.GetHashCode();
hash *= 23 + this.Value2.GetHashCode();
return hash;
}
}
// allows you to check equality with the == operator
public static bool operator ==(accessoire left, accessoire right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if ((oject)left == null || (object)right == null)
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(accessoire left, accessoire right)
{
return !left.Equals(right);
}
}

C# Linq .Distinct() method not working [duplicate]

This question already has answers here:
LINQ's Distinct() on a particular property
(23 answers)
Closed 20 days ago.
class Program
{
static void Main(string[] args)
{
List<Book> books = new List<Book>
{
new Book
{
Name="C# in Depth",
Authors = new List<Author>
{
new Author
{
FirstName = "Jon", LastName="Skeet"
},
new Author
{
FirstName = "Jon", LastName="Skeet"
},
}
},
new Book
{
Name="LINQ in Action",
Authors = new List<Author>
{
new Author
{
FirstName = "Fabrice", LastName="Marguerie"
},
new Author
{
FirstName = "Steve", LastName="Eichert"
},
new Author
{
FirstName = "Jim", LastName="Wooley"
},
}
},
};
var temp = books.SelectMany(book => book.Authors).Distinct();
foreach (var author in temp)
{
Console.WriteLine(author.FirstName + " " + author.LastName);
}
Console.Read();
}
}
public class Book
{
public string Name { get; set; }
public List<Author> Authors { get; set; }
}
public class Author
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
return true;
//if (obj.GetType() != typeof(Author)) return false;
//else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
}
}
This is based on an example in "LINQ in Action". Listing 4.16.
This prints Jon Skeet twice. Why? I have even tried overriding Equals method in Author class. Still Distinct does not seem to work. What am I missing?
Edit:
I have added == and != operator overload too. Still no help.
public static bool operator ==(Author a, Author b)
{
return true;
}
public static bool operator !=(Author a, Author b)
{
return false;
}
LINQ Distinct is not that smart when it comes to custom objects.
All it does is look at your list and see that it has two different objects (it doesn't care that they have the same values for the member fields).
One workaround is to implement the IEquatable interface as shown here.
If you modify your Author class like so it should work.
public class Author : IEquatable<Author>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Equals(Author other)
{
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
public override int GetHashCode()
{
int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode();
int hashLastName = LastName == null ? 0 : LastName.GetHashCode();
return hashFirstName ^ hashLastName;
}
}
Try it as DotNetFiddle
The Distinct() method checks reference equality for reference types. This means it is looking for literally the same object duplicated, not different objects which contain the same values.
There is an overload which takes an IEqualityComparer, so you can specify different logic for determining whether a given object equals another.
If you want Author to normally behave like a normal object (i.e. only reference equality), but for the purposes of Distinct check equality by name values, use an IEqualityComparer. If you always want Author objects to be compared based on the name values, then override GetHashCode and Equals, or implement IEquatable.
The two members on the IEqualityComparer interface are Equals and GetHashCode. Your logic for determining whether two Author objects are equal appears to be if the First and Last name strings are the same.
public class AuthorEquals : IEqualityComparer<Author>
{
public bool Equals(Author left, Author right)
{
if((object)left == null && (object)right == null)
{
return true;
}
if((object)left == null || (object)right == null)
{
return false;
}
return left.FirstName == right.FirstName && left.LastName == right.LastName;
}
public int GetHashCode(Author author)
{
return (author.FirstName + author.LastName).GetHashCode();
}
}
Another solution without implementing IEquatable, Equals and GetHashCode is to use the LINQs GroupBy method and to select the first item from the IGrouping.
var temp = books.SelectMany(book => book.Authors)
.GroupBy (y => y.FirstName + y.LastName )
.Select (y => y.First ());
foreach (var author in temp){
Console.WriteLine(author.FirstName + " " + author.LastName);
}
There is one more way to get distinct values from list of user defined data type:
YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();
Surely, it will give distinct set of data
Distinct() performs the default equality comparison on objects in the enumerable. If you have not overridden Equals() and GetHashCode(), then it uses the default implementation on object, which compares references.
The simple solution is to add a correct implementation of Equals() and GetHashCode() to all classes which participate in the object graph you are comparing (ie Book and Author).
The IEqualityComparer interface is a convenience that allows you to implement Equals() and GetHashCode() in a separate class when you don't have access to the internals of the classes you need to compare, or if you are using a different method of comparison.
You've overriden Equals(), but make sure you also override GetHashCode()
The Above answers are wrong!!!
Distinct as stated on MSDN returns the default Equator which as stated The Default property checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation. Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T
Which means as long as you overide Equals you are fine.
The reason you're code is not working is because you check firstname==lastname.
see https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx and https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx
You can achieve this several ways:
1. You may to implement the IEquatable interface as shown Enumerable.Distinct Method or you can see #skalb's answer at this post
2. If your object has not unique key, You can use GroupBy method for achive distinct object list, that you must group object's all properties and after select first object.
For example like as below and working for me:
var distinctList= list.GroupBy(x => new {
Name= x.Name,
Phone= x.Phone,
Email= x.Email,
Country= x.Country
}, y=> y)
.Select(x => x.First())
.ToList()
MyObject class is like as below:
public class MyClass{
public string Name{get;set;}
public string Phone{get;set;}
public string Email{get;set;}
public string Country{get;set;}
}
3. If your object's has unique key, you can only use the it in group by.
For example my object's unique key is Id.
var distinctList= list.GroupBy(x =>x.Id)
.Select(x => x.First())
.ToList()
You can use extension method on list which checks uniqueness based on computed Hash.
You can also change extension method to support IEnumerable.
Example:
public class Employee{
public string Name{get;set;}
public int Age{get;set;}
}
List<Employee> employees = new List<Employee>();
employees.Add(new Employee{Name="XYZ", Age=30});
employees.Add(new Employee{Name="XYZ", Age=30});
employees = employees.Unique(); //Gives list which contains unique objects.
Extension Method:
public static class LinqExtension
{
public static List<T> Unique<T>(this List<T> input)
{
HashSet<string> uniqueHashes = new HashSet<string>();
List<T> uniqueItems = new List<T>();
input.ForEach(x =>
{
string hashCode = ComputeHash(x);
if (uniqueHashes.Contains(hashCode))
{
return;
}
uniqueHashes.Add(hashCode);
uniqueItems.Add(x);
});
return uniqueItems;
}
private static string ComputeHash<T>(T entity)
{
System.Security.Cryptography.SHA1CryptoServiceProvider sh = new System.Security.Cryptography.SHA1CryptoServiceProvider();
string input = JsonConvert.SerializeObject(entity);
byte[] originalBytes = ASCIIEncoding.Default.GetBytes(input);
byte[] encodedBytes = sh.ComputeHash(originalBytes);
return BitConverter.ToString(encodedBytes).Replace("-", "");
}
The Equal operator in below code is incorrect.
Old
public bool Equals(Author other)
{
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
NEW
public override bool Equals(Object obj)
{
var other = obj as Author;
if (other is null)
{
return false;
}
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
Instead of
var temp = books.SelectMany(book => book.Authors).Distinct();
Do
var temp = books.SelectMany(book => book.Authors).DistinctBy(f => f.Property);

LINQ Intersect not returning items

I have implemented a comparison class for my custom class, so that I can use Intersect on two lists (StudentList1 and StudentList2). However, when I run the following code, I don't get any results.
Student:
class CompareStudent : IEqualityComparer<Student>
{
public bool Equals(Student x, Student y)
{
if (x.Age == y.Age && x.StudentName.ToLower() == y.StudentName.ToLower())
return true;
return false;
}
public int GetHashCode(Student obj)
{
return obj.GetHashCode();
}
}
class Student
{
public int StudentId{set;get;}
public string StudentName{set;get;}
public int Age{get;set;}
public int StandardId { get; set; }
}
Main:
IList<Student> StudentList1 = new List<Student>{
new Student{StudentId=1,StudentName="faisal",Age=29,StandardId=1},
new Student{StudentId=2,StudentName="qais",Age=19,StandardId=2},
new Student{StudentId=3,StudentName="ali",Age=19}
};
IList<Student> StudentList2 = new List<Student>{
new Student{StudentId=1,StudentName="faisal",Age=29,StandardId=1},
new Student{StudentId=2,StudentName="qais",Age=19,StandardId=2},
};
var NewStdList = StudentList1.Intersect(StudentList2, new CompareStudent());
Console.ReadLine();
The problem is within your GetHashCode() method, change it to:
public int GetHashCode(Student obj)
{
return obj.StudentId ^ obj.Age ^ obj.StandardId ^ obj.StudentName.Length;
}
In your current code, Equals is not called as the current GetHashCode() returns two different integers, so no point in calling Equals.
If GetHashCode of the first object is different than the second, the objects are not equal, if the result is the same, Equals is being called.
The GetHashCode I've written above is not ultimate, see What is the best algorithm for an overridden System.Object.GetHashCode? for more details on how to implement GetHashCode.
GetHashCode() is not (and cannot be) collision free, which is why the Equals method is required in the first place.
You are calling GetHashCode() on the base object, which will return a different value for the different references. I would implement it like this:
public override int GetHashCode(Student obj)
{
unchecked
{
return obj.StudentName.GetHashCode() + obj.Age.GetHashCode();
}
}

Distinct not working with LINQ to Objects [duplicate]

This question already has answers here:
LINQ's Distinct() on a particular property
(23 answers)
Closed 23 days ago.
class Program
{
static void Main(string[] args)
{
List<Book> books = new List<Book>
{
new Book
{
Name="C# in Depth",
Authors = new List<Author>
{
new Author
{
FirstName = "Jon", LastName="Skeet"
},
new Author
{
FirstName = "Jon", LastName="Skeet"
},
}
},
new Book
{
Name="LINQ in Action",
Authors = new List<Author>
{
new Author
{
FirstName = "Fabrice", LastName="Marguerie"
},
new Author
{
FirstName = "Steve", LastName="Eichert"
},
new Author
{
FirstName = "Jim", LastName="Wooley"
},
}
},
};
var temp = books.SelectMany(book => book.Authors).Distinct();
foreach (var author in temp)
{
Console.WriteLine(author.FirstName + " " + author.LastName);
}
Console.Read();
}
}
public class Book
{
public string Name { get; set; }
public List<Author> Authors { get; set; }
}
public class Author
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
return true;
//if (obj.GetType() != typeof(Author)) return false;
//else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
}
}
This is based on an example in "LINQ in Action". Listing 4.16.
This prints Jon Skeet twice. Why? I have even tried overriding Equals method in Author class. Still Distinct does not seem to work. What am I missing?
Edit:
I have added == and != operator overload too. Still no help.
public static bool operator ==(Author a, Author b)
{
return true;
}
public static bool operator !=(Author a, Author b)
{
return false;
}
LINQ Distinct is not that smart when it comes to custom objects.
All it does is look at your list and see that it has two different objects (it doesn't care that they have the same values for the member fields).
One workaround is to implement the IEquatable interface as shown here.
If you modify your Author class like so it should work.
public class Author : IEquatable<Author>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Equals(Author other)
{
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
public override int GetHashCode()
{
int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode();
int hashLastName = LastName == null ? 0 : LastName.GetHashCode();
return hashFirstName ^ hashLastName;
}
}
Try it as DotNetFiddle
The Distinct() method checks reference equality for reference types. This means it is looking for literally the same object duplicated, not different objects which contain the same values.
There is an overload which takes an IEqualityComparer, so you can specify different logic for determining whether a given object equals another.
If you want Author to normally behave like a normal object (i.e. only reference equality), but for the purposes of Distinct check equality by name values, use an IEqualityComparer. If you always want Author objects to be compared based on the name values, then override GetHashCode and Equals, or implement IEquatable.
The two members on the IEqualityComparer interface are Equals and GetHashCode. Your logic for determining whether two Author objects are equal appears to be if the First and Last name strings are the same.
public class AuthorEquals : IEqualityComparer<Author>
{
public bool Equals(Author left, Author right)
{
if((object)left == null && (object)right == null)
{
return true;
}
if((object)left == null || (object)right == null)
{
return false;
}
return left.FirstName == right.FirstName && left.LastName == right.LastName;
}
public int GetHashCode(Author author)
{
return (author.FirstName + author.LastName).GetHashCode();
}
}
Another solution without implementing IEquatable, Equals and GetHashCode is to use the LINQs GroupBy method and to select the first item from the IGrouping.
var temp = books.SelectMany(book => book.Authors)
.GroupBy (y => y.FirstName + y.LastName )
.Select (y => y.First ());
foreach (var author in temp){
Console.WriteLine(author.FirstName + " " + author.LastName);
}
There is one more way to get distinct values from list of user defined data type:
YourList.GroupBy(i => i.Id).Select(i => i.FirstOrDefault()).ToList();
Surely, it will give distinct set of data
Distinct() performs the default equality comparison on objects in the enumerable. If you have not overridden Equals() and GetHashCode(), then it uses the default implementation on object, which compares references.
The simple solution is to add a correct implementation of Equals() and GetHashCode() to all classes which participate in the object graph you are comparing (ie Book and Author).
The IEqualityComparer interface is a convenience that allows you to implement Equals() and GetHashCode() in a separate class when you don't have access to the internals of the classes you need to compare, or if you are using a different method of comparison.
You've overriden Equals(), but make sure you also override GetHashCode()
The Above answers are wrong!!!
Distinct as stated on MSDN returns the default Equator which as stated The Default property checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation. Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T
Which means as long as you overide Equals you are fine.
The reason you're code is not working is because you check firstname==lastname.
see https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx and https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx
You can achieve this several ways:
1. You may to implement the IEquatable interface as shown Enumerable.Distinct Method or you can see #skalb's answer at this post
2. If your object has not unique key, You can use GroupBy method for achive distinct object list, that you must group object's all properties and after select first object.
For example like as below and working for me:
var distinctList= list.GroupBy(x => new {
Name= x.Name,
Phone= x.Phone,
Email= x.Email,
Country= x.Country
}, y=> y)
.Select(x => x.First())
.ToList()
MyObject class is like as below:
public class MyClass{
public string Name{get;set;}
public string Phone{get;set;}
public string Email{get;set;}
public string Country{get;set;}
}
3. If your object's has unique key, you can only use the it in group by.
For example my object's unique key is Id.
var distinctList= list.GroupBy(x =>x.Id)
.Select(x => x.First())
.ToList()
You can use extension method on list which checks uniqueness based on computed Hash.
You can also change extension method to support IEnumerable.
Example:
public class Employee{
public string Name{get;set;}
public int Age{get;set;}
}
List<Employee> employees = new List<Employee>();
employees.Add(new Employee{Name="XYZ", Age=30});
employees.Add(new Employee{Name="XYZ", Age=30});
employees = employees.Unique(); //Gives list which contains unique objects.
Extension Method:
public static class LinqExtension
{
public static List<T> Unique<T>(this List<T> input)
{
HashSet<string> uniqueHashes = new HashSet<string>();
List<T> uniqueItems = new List<T>();
input.ForEach(x =>
{
string hashCode = ComputeHash(x);
if (uniqueHashes.Contains(hashCode))
{
return;
}
uniqueHashes.Add(hashCode);
uniqueItems.Add(x);
});
return uniqueItems;
}
private static string ComputeHash<T>(T entity)
{
System.Security.Cryptography.SHA1CryptoServiceProvider sh = new System.Security.Cryptography.SHA1CryptoServiceProvider();
string input = JsonConvert.SerializeObject(entity);
byte[] originalBytes = ASCIIEncoding.Default.GetBytes(input);
byte[] encodedBytes = sh.ComputeHash(originalBytes);
return BitConverter.ToString(encodedBytes).Replace("-", "");
}
The Equal operator in below code is incorrect.
Old
public bool Equals(Author other)
{
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
NEW
public override bool Equals(Object obj)
{
var other = obj as Author;
if (other is null)
{
return false;
}
if (FirstName == other.FirstName && LastName == other.LastName)
return true;
return false;
}
Instead of
var temp = books.SelectMany(book => book.Authors).Distinct();
Do
var temp = books.SelectMany(book => book.Authors).DistinctBy(f => f.Property);

Categories

Resources