Lets say I have two lists of the same class.
public class emailfilter
{
public string from {get;set;}
public string to {get;set;}
public string cc {get;set;}
public string subj {get;set;}
public string body {get;set;}
public string emailid {get;set;}
}
//there are two lists of type emailfilter. 1 list is formed dynamically from the config file
List<emailfilter> configfilterlist = //mock sudo code//
{
efilter tempobj = new efilter();
tempobj.from = config.from or "" if no value
tempobj.to = config.to or "" if no value
tempobj.cc = config.cc or "" if no value
tempobj.subj = config.subj or "" if no value
tempobj.body = config.body or "" if no value
configfilterlist.add(tempobj);
}
//List1 will never have an emailID
//List2 is formed from email items pulled from exchange and made into efilter objects and those do have an emailid.
//List2 will typically have all object fields populated. List1, the object fields are optional
So I want to compare/intersect list1 of filter items against list2 of email items to a combined list without duplicates that includes only the items that have all filter criteria of list1 and includes the mailid of list2. If there's no value for a value on List1, I want to ignore that and just match on the config values provided skipping over any "" blank strings. I'm hoping there's a way to do this with lambda and linq, but I haven't seen any examples with comparison on multiple values and ignoring others like in this case emailID.
UPDATE: Thank you @wertzui for providing the answer I needed to solve this. The final solution was just slightly different so updating the post/question with essentially the final solution in case it helps another lost soul.
public class emailfilter: IEquatable<emailfilter>
{
public string from { get; set; }
public string to { get; set; }
public string cc { get; set; }
public string subj { get; set; }
public string body { get; set; }
public string emailid { get; set; }
public override int GetHashCode()
{
return System.HashCode.Combine(from, to, cc, subj, body);
}
public override bool Equals(object? obj) => Equals(obj as emailfilter);
public bool Equals(emailfilter? other)
{
return
other != null &&
(from.Contains(other.from) || other.from == "") &&
(to.Contains(other.sentto) || other.to == "") &&
(cc.Contains(other.cc) || other.cc == "") &&
(subj.Contains(other.subj) || other.subj == "") &&
(body.Contains(other.body) || other.body == "");
}
}
//emailsasfilters is List2 = all exchange emails as filter objects
var combinedSet = new HashSet<emailfilter>();
foreach (var filter in configfilterlist) //configfilterlist is List1 = filters from Config
{
if (emailsasfilters.Contains(filter))
combinedSet.Add(emailsasfilters.ElementAt(emailsasfilters.IndexOf(filter)));
}
combinedSet.Dump();
CodePudding user response:
Since .Net 6, there is a new UnionBy
method which you can use.
var combined = configfilterlist
.UnionBy(
exchangefilterlist,
e => new { e.from, e.to, e.cc, e.subj, e.body })
.ToList();
Another method which works with older Framework versions is, to use a HashSet<T>
and implement GetHashCode
and Equals
public class emailfilter: IEquatable<emailfilter>
{
public string from { get; set; }
public string to { get; set; }
public string cc { get; set; }
public string subj { get; set; }
public string body { get; set; }
public string emailid { get; set; }
public override int GetHashCode()
{
return System.HashCode.Combine(from, to, cc, subj, body);
}
public override bool Equals(object? obj) => Equals(obj as emailfilter);
public bool Equals(emailfilter? other)
{
return
other != null &&
from == other.from &&
to == other.to &&
cc == other.cc &&
subj == other.subj &&
body == other.body;
}
}
var combinedSet = new HashSet<emailfilter>(configfilterlist);
foreach (var email in exchangefilterlist)
{
combinedSet.Add(email);
}
combinedSet.Dump();