Reputation: 127
I have two lists:
ListA ListB
Name Name
Marc Marc
micheal micheal
Jolie Jolie
I want to compare this two lists A and B, if they have same names it shoulds return true. If one list has different values than other it should return false, if one has 2 values and the other has 3 values, it shoulds return false too. this traitement return me always false even they have same values:
var SameNames = ListA.All(x => ListB.All(y => y.Name.Equals(x.Name)));
I did it in wrong way ? please
Upvotes: 1
Views: 7679
Reputation: 1715
You can use var SameNames = ListA.SequenceEquals(ListB)
. That will use the default equals defined in the object. It will throw an exception if any of the two objects are null. So make sure your lists are not null.
Equals implementation
class YourListItem {
public string Name { get; set; }
public override bool Equals(object obj)
{
if(obj is YourListItem other) {
return this.Name == other.Name;
}
return false;
}
public override int GetHashCode() => this.Name.GetHashCode();
}
If you need a custom compare logic that does not really 'fit' into the Objects equals
method or you need multiple different equality implementations. You can create another class that implements IEqualityComparer
for your type and pass that as the second parameter var SameNames = ListA.SequenceEquals(ListB, new MyEqualityComparer())
.
Equality comparer
class MyEqualityComparer: IEqualityComparer<YourListItem> {
public bool Equals(YourListItem first, YourListItem second) {
return first.Name == second.Name;
}
public int GetHashCode(YourListItem obj) => obj.Name.GetHashCode();
}
Note: You should always be implementing GetHashCode as well when overriding Equals. Its like a shorter equals (in a sence) if the Hash code is not the same Equals is redundant and does not need to be executed. A lot of prebuilt methods that use EqualityComparers or Equals will not run Equals if the HashCode´s are different. The default HashCode is the Object reference so it would falsely match two lists to not be Equal that really should be Equal (if you do not overwride the HashCode). Pick some field that you ecpect to be different for most objects for your HashCode implementation. If you have a tiny object like this it looks stupid, but you can have gigantic objects with complex Equals implementations or generic reflection based equality checks that are slow. So being able to skip the Equality check entierly most of the time when HashCode´s are not the same is suddenly relevant then.
Upvotes: 0
Reputation: 53
Following is the code to achieve your requirement.
var isEqual = ListA.OrderBy(o=>o.Name).SelectMany(x=>x.Name).SequenceEqual(ListB.OrderBy(o=>o.Name).SelectMany(x => x.Name));
Upvotes: 0
Reputation: 13773
var SameNames = ListA.All(x => ListB.All(y => y.Name.Equals(x.Name)));
What you wrote here is a requirement that all names in A match all names in B. That clearly conflicts with your example data, as the "Marc" from A does not equal "Jolie" from B.
Your current code would only return true if A and B in total only contain one distinct name. Which makes no sense for the problem you're trying to solve.
What you're actually trying to solve is if all names from A have one match in B. That would be achieved by doing:
var sameNames = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
Note the Any
. It returns true
if it find (at least) one match. This is different from All
, which only returns true if all values in B are a match.
However, this is not enough yet. You've now ascertained that all names from A appear in B, but it's still possible that B contains more names that aren't in A. Take the following example:
var listA = new List<string>() { "Andy", "Bobby", "Cindy" };
var listB = new List<string>() { "Andy", "Bobby", "Cindy", "David" };
var SameNames = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
SameNames
will be true
, but that's because you only one direction. You need to check in both directions:
var listA_in_listB = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
var listB_in_listA = listB.All(b => listA.Any(a => a.Name.Equals(b.Name)));
var sameNames = listA_in_listB && listB_in_listA;
This gives you the result you want.
Note that there are a few other variations on how to approach this.
If you can guarantee that each list does not contain any duplicates, then instead of doing the same check twice, you can simple do one of the checks and then confirm that the lists are of equal length:
var listA_in_listB = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
var sameLength = listA.Length == listB.Length;
var sameNames = listA_in_listB && sameLength;
This is more efficient, but it does require knowing that your names are unique within each list.
Upvotes: 3
Reputation: 190
If order should be relevant :
bool result = Enumerable.SequenceEquals<string>(ListA, ListB);
if order should be irrelevant :
bool result = new HashSet<string>(ListA).SetEquals(ListB);
doing this overcomplicated in LINQ is just overtuning, you could just use ForEachs at that point. If u want a simple and clean solution, these are for you.
Greetings
Upvotes: 1