I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());
Related
It's better to start with code
public interface ICondition<TObject> where TObject : notnull
{
public TObject? Value { get; } //Condition value for primitives
public bool InverseResult { get; init; }
public bool IsSatisfiedBy(TObject obj)
{
return InverseResult ? !Check(obj) : Check(obj);
}
public bool Check(TObject obj);
}
This interface is implemented much in classes like IntegerCondition, BooleanCondition. And other primitives. For example GreaterIntegerCondition, ExactlyIntegerCondition
public interface ITemplate<TObject> : ICondition<TObject> where TObject : notnull
{
TObject? ICondition<TObject>.Value => default;
public IEnumerable<IMappedCondition<TObject>> ConditionsMap { get; }
bool ICondition<TObject>.InverseResult
{
get => false;
init { }
}
public string TemplateName { get; set; }
bool ICondition<TObject>.Check(TObject obj)
{
return ConditionsMap.All(c => c.CheckCondition(obj)) && ConditionsMap.Any();
}
}
This interface extends previous. It can contain many of conditions that mapped to TObject's properties. It is used on more complex objects that can have many properties.
At the end I have this class (simplified version below)
public class FooBunchTemplate
{
public List<ICondition<Foo>> Conditions { get; }
public bool CheckBunch(HashSet<Foo> bunch)
{
var remainingConditions = Conditions.ToList();
foreach(Foo foo in bunch)
{
var matched = remainingConditions.FirstOrDefault(c => c.IsSatisfiedBy(foo));
if(matched == null) return false;
remainingConditions.Remove(matched);
}
return true;
}
}
So, we have set of Foo. Every Foo in HashSet is unique. But problem is the one Foo must match only one condition "slot". There are templates that can be satisfied by different objects (even hundreads of them)
If I just exclude matches, then there might be a situation where the more general condition will be excluded by Foo which would fit also the narrower pattern. Then the CheckBunch() will return false, although if the conditions are set correctly, it should have returned true.
You can think of it as a slot machine. The order is unimportant, but the link 1 condition - 1 object is important.
Example template:
A or B
A
C
Bunch:
CAB
We search C template and exclue 3rd
We search A template and exclude 1st
We search B template but 1st was excluded before.
False is returned
//Wrong result. CAB matches this template, because we can put A in second slot and B in first.
Which approach will be fast and reliable in that situation. Number of conditions in BunchTemplate always fit count of Foo in bunch
I have an interface which defines a composite key:
public interface IKey : IEquatable<IKey>
{
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
int GetHashCode(); // never gets called
}
I have an object (with an ID) to which I want to add the composite key interface:
public class MyObject: IEquatable<MyObject>, IKey
{
public MyObject(int i, (bool keyPart1, uint keyPart2) key) {
{
Id=i;
KeyPart1 = key.keyPart1;
KeyPart2 = key.keyPart2;
}
public int Id { get; }
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
public bool Equals(MyObject other) => this.Id == other.Id;
public override bool Equals(object other) => other is MyObject o && Equals(o);
public override int GetHashCode() => Id.GetHashCode();
bool IEquatable<IKey>.Equals(IKey other) => this.KeyPart1 == other.KeyPart1
&& this.KeyPart2 == other.KeyPart2;
int IKey.GetHashCode() => (KeyPart1, KeyPart2).GetHashCode(); // never gets called
}
However, when have a list of these objects and try to group them using the interface, the grouping fails:
var one = new MyObject(1, (true, 1));
var two = new MyObject(2, (true, 1));
var three = new MyObject(1, (false, 0));
var items = new[] { one, two, three };
var byId = items.GroupBy(i => i);
// result: { [one, three] }, { [two] } -- as expected
var byKey = items.GroupBy<MyObject, IKey>(i => i as IKey);
// result: { [one, two, three] } // not grouped (by 'id' or 'key')
// expected: { [one, two] }, { [three] }
I'd expected that byId would have the items grouped by the Id property, and byKey would have the items grouped by the Key property.
However, byKey is not grouped at all. It appears that the override GetHashCode() method is always used rather than the explicitly implemented interface method.
Is it possible to implement something like this, where the type of the item being grouped determines the hash method to use (avoiding an EqualityComparer)?
I noticed this problem when passing the cast objects to another method expecting an IEnumerable<IKey>. I have a few different types implementing IKey and those with an existing GetHashCode() method did not work, while the others did.
Please note the objects have been simplified here and that I cannot easily change the interfaces (e.g. to use ValueTuple instead).
The GetHashCode() used in equality is either:
the one defined via object.GetHashCode(), if no equality comparer is provided
IEqualityComparer<T>.GetHashCode(T), if an equality comparer is provided
Adding your own GetHashCode() method on your own interface does nothing, and it will never be used, as it is not part of an API that the framework/library code knows about.
So, I'd forget about IKey.GetHashCode(), and either (or both):
make MyObject.GetHashCode() provide the functionality you need, or
provide a custom equality comparer separately to the MyObject instance
There are overloads of GroupBy that accept an IEqualityComparer<TKey>, for the second option.
Cant find a simple answer. My problem is I am trying to compare the VALUE of an object in a list to the VALUE of an object...
my class:
public class MatchList
{
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
I create theMatchList. It seems that I can only compare the object and not the values for object with 'theMatchList.Contains...'
MatchList ML = new MatchList();
ML.SomeInt = 12;
ML.SomeDecimal = 2.3;
if (theMatchlist.Contains(ML))
{
DoSomething;
}
How do get to fire 'DoSomething'? Assuming that there is an entry in 'theMatchList' where the values equal 12 and 2.3 respectively. I know it has something to do with iequatable, but I dont quite understand how that works. Thanks in advance!
Your naming is a bit unclear, I assume that you actually have a List<MatchList> that you want to find a particular MatchList in (I suggest renaming MatchList to at least MatchItem in that case and preferable something more descriptive).
Then from the documentation of List<T>.Contains:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).
So you will have to implement IEquatable<T> for your class. In addition, the advice is that
[i]f you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method.
If you implement GetHashCode, its result should not change over the lifetime of your object. In most cases, making the class immutable is sufficient. If you need to be able to update the fields, you need to implement GetHashCode differently.
So all in all, if you want to use Contains your class will end up looking something like below:
public class MatchList : IEquatable<MatchList>
{
// Note: Fields are readonly to satisfy GetHashCode contract
private readonly int someInt;
private readonly decimal someDecimal;
// Public constructor creates immutable object
public MatchList(int myInt, decimal myDecimal)
{
this.someInt = myInt;
this.myDecimal = myDecimal;
}
// Properties are now read-only too.
public int SomeInt { get { return this.someInt; } }
public decimal SomeDecimal { get { return this.someDecimal; } }
// Implementation of IEquatable<MatchList>
public bool Equals( MatchList other )
{
return (other != null)
&& (this.SomeInt == other.SomeInt)
&& (this.SomeDecimal == other.SomeDecimal);
}
// Override of Object.Equals
// Calls the IEquatable.Equals version if possible.
public override bool Equals( object obj )
{
return (obj is MatchList) && this.Equals(obj as MatchList);
}
public override int GetHashCode()
{
return (this.someInt * 17) ^ this.someDecimal.GetHashCode();
}
}
As I commented, your question is pretty unclear so I'll do my best to explain the concept.
It's pretty likely what you were trying to code is the items in the list not the list itself:
public class MatchItem : IEquatable<MatchItem>
{
public int SomeInt { get; set; }
public decimal SomeDecimal {get; set; }
public bool Equals(MatchItem item)
{
if(item == null)
return false;
return this.SomeInt == item.SomeInt && this.SomeDecimal == item.SomeDecimal;
}
// You should also override object.ToString, object.Equals & object.GetHashCode.
// Omitted for brevity here!
}
You'll note that has an implementation of IEquatable<MatchItem> which allows it to be compared to other instances of MatchItem.
Thereafter, this code will work:
var items = new List<MatchItem>()
{
new MatchItem{SomeInt = 1, SomeDecimal = 0.3M},
new MatchItem{SomeInt = 12, SomeDecimal = 2.3M}
};
var searchItem = new MatchItem{SomeInt = 1, SomeDecimal = 0.3M};
Console.WriteLine(items.Contains(searchItem)); // true
Working example: http://rextester.com/ZWNC6890
how can i compare 2 lists and have the not matching items but according to the specifics properties
public partial class Cable : StateObject
{
public int Id { get; set; }
public int CablePropertyId { get; set; }
public int Item { get; set; }
public int TagNo { get; set; }
public string GeneralFormat { get; set; }
public string EndString { get; set; }
public string CableRevision { get; set; }
}
I want to comparision accomplished accoring to the CablePropertyId,TagNo and CableRevision, if i use
var diffCables = sourceCables.Except(destinationCables).ToList();
the whole properties are compared to each other . how can i do that?
Use Linq except method with custom EqualityComparer.
http://msdn.microsoft.com/en-us/library/bb336390(v=vs.110).aspx
class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable x, Cable y)
{
return (x.CablePropertyId == y.CablePropertyId && ...);
}
public int GetHashCode(Cable x) // If you won't create a valid GetHashCode based on values you compare on, Linq won't work properly
{
unchecked
{
int hash = 17;
hash = hash * 23 + x.CablePropertyID;
hash = hash * 23 + ...
}
return hash;
}
}
var diffCables = sourceCables.Except(destinationCables, new CableComparer());
Also, ToList() operation on the result isn't really necessary. Most of the time you can just operate on the result of Linq query IEnumerable without specifying the exact type; this way you won't waste performance on unneeded ToList() operation.
By the way, a couple of others proposed Where-based queries with simple lambda. Such solution is easier to read (in my opinion), but it's also less optimized: it forces n^2 checks, while IEqualityComparer allows Linq to be more optimal because of GetHashCode() method. Here's a great answer on importance of GetHashCode, and here's a great guide on writing GetHashCode() override.
You can create your own IEqualityComparer<Cable> like this:
public class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable x, Cable y)
{
return x.CablePropertyId == y.CablePropertyId &&
x.TagNo == y.TagNo &&
x.CableRevision == y.CableRevision;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Cable x)
{
return x.CablePropertyId ^
x.TagNo.GetHashCode() ^
x.CableRevision.GetHashCode();
}
}
Then use this overload of Except:
var comparer = new CableComparer();
var diffCables = sourceCables.Except(destinationCables, comparer).ToList();
Alternatively, the MoreLINQ library (also available on NuGet) provides a convenient ExceptBy method:
var diffCables = sourceCables.ExceptBy(
destinationCables,
x => new {
x.CablePropertyId,
x.TagNo,
x.CableRevision
})
.ToList();
You can override the Equals and GetHashCode methods of Cable if you will always compare this object in this manner.
Otherwise you can write a custom comparer and use the overload for .Except
List.Except Method
I think you can use something like this:
sourceCables.Where(sc => !destinationCables.Any(dc => dc.CablePropertyId == sc.CablePropertyId && ...));
Essentially, when you want to compare your own types, you'll need to describe how they compare/differ from each other. Linq wouldn't know which properties in your Cable class are different right?
So you build a comparer which can be used generally to compare two types.
In this case, two Cable instances:
class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable c1, Cable c2)//these represent any two cables.
{
if (c1.Height == c2.Height && ...)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Cable c)
{
//this will work if each ID is unique
return c.Id.GetHashCode();
//otherwise you do this:
//return (c.Id ^ c. CablePropertyId).GetHashCode();
}
}
Then:
IEnumerable<Cable> except =
sourceCables.Except(destinationCables, new CableComparer());
If you use LINQ with IQueryable<>, there may be solution with Where()
var destinationCablesAnon = destinationCables.Select(a=>new {a.CablePropertyId, a.TagNo ,a.CableRevision}); // add ToArray() if use IEnumerable<>
var diffCables = sourceCables.Where(a=>!destinationCables.Contains(new {a.CablePropertyId, a.TagNo ,a.CableRevision})).ToList();
I have two lists of different objects, one from a third party API and one from my database - and I'm trying to link the two as a relationship. Ideally with a similar effect of how DBML's create relationships for tables with foreign keys (Customer.Orders).
From third party:
class ApiObject {
public string ID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
... 30 other properties ...
}
From my database:
class DbmlObject {
public int ID { get; set; }
public string ApiID { get; set; }
public string OtherString { get; set; }
}
They are related through ApiObject.ID == DbmlObject.ApiID
I do not want to merge these, nor join them into some anonymous object (and explicitly list 30+ properties) - but rather to make the DbmlObject a linked property of ApiObject. i.e.: addressable as:
apiObject.DbmlObjects.First().OtherString or ideally apiObject.DbmlObject.OtherString since it is a 1 to 1 relationship.
In controller:
List<ApiObject> apiObjects = _thirdParty.GetObjects();
DbmlDataContext model = new DbmlDataContext();
List<DbmlObject> dbmlObjects = model.GetAllDbmlObjects();
// relate them here
foreach (var apiObject in apiObjects)
Console.Write(apiObject.DbmlObject.OtherString)
// NOTE: ideally this foreach loop should not make a DBML query on each iteration, just the single GetAllDbmlObjects query above.
It sounds like a join:
var combined = from api in apiObjects
join dbml in dbmlObjects on api.ID equals dbml.ApiID
select new { api, dbml }
In order to get DbmlObject "in" the ApiObject, you will need to either inherit ApiObject and construct a new one of that class, which includes the Dbml property, or create a entirely new class to return. If you need static typing this is the best you can do - of course you could (mis)use dynamic to get what you want.
In this case, you are mentioning (in comments) that the ApiObject class is from a third party library that you can't change - in this case I would probably choose to create a new type which takes an instance of both objects in the constructor and exposes the properties you need - a decorator. Yes, it looks like a lot of code, but it is not complex, good tools will autogenerate it for you - and you get the class that you need for your code to be succinct.
In case you want to go further with returning an IEnumerable<dynamic>, you could build a "combining dynamic" object based on DynamicObject that then responds to all the properties of ApiObject and DbmlObject - or just adds DbmlObject as a property. I am not saying this is the right way to go, it depends on what you need it for - remember you are losing type safety. Here is a simple example:
void Main()
{
dynamic dyn = new CombiningDynamic(new Foo { X = 3 }, new Bar { Y = 42 });
Console.WriteLine(dyn.X);
Console.WriteLine(dyn.Y);
}
public class Foo
{
public int X {get;set;}
}
public class Bar
{
public int Y { get;set;}
}
public class CombiningDynamic : DynamicObject
{
private object [] innerObjects;
public CombiningDynamic(params object [] innerObjects)
{
this.innerObjects = innerObjects;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach(var instance in innerObjects)
{
Type t = instance.GetType();
PropertyInfo prop = t.GetProperty(binder.Name);
if (prop != null && prop.CanRead)
{
result = prop.GetValue(instance, null);
return true;
}
}
result = null;
return false;
}
}
Remember, this is example code. If you really go this way, you would want to perhaps override some more of the methods (TrySetMember, ...), and you most definetely would want to cache the reflection results so you don't need to walk the types each time - reflection is (comparatively) slow.