I have trouble deleting the duplicate references in my list.
I have this list
List<SaveMongo> toReturn
with my class SaveMongo that looks like this
public class SaveMongo
{
public ObjectId _id { get; set; }
public DateTime date { get; set; }
public Guid ClientId { get; set; }
public List<TypeOfSave> ListType = new List<TypeOfSave>();
public List<ObjectId> ListObjSave = new List<ObjectId>();
public SaveMongo()
{ }
}
Whenever I want to add an element to my list I use the following code
public static fctName(BsonDocument doc)
{
toReturn.Add(AddingSaveMongo(doc.GetValue("_id")));
}
public static SaveMongo AddingSaveMongo(BsonValue ObjValue)
{
foreach (SaveMongo doc in SpeCollection.FindAll())
{
foreach (var id in doc.ListObjSave)
{
if (id == ObjValue)
return (doc);
}
}
return (null);
}
However, I sometimes get duplicates references. I tried using this
toReturn = toReturn.Distinct().ToList();
to delete them. Without success.
I also tried to do this
if (!toReturn.Contains(AddingSaveMongo(doc.GetValue("_id"))))
toReturn.Add(AddingSaveMongo(doc.GetValue("_id")));
Still without success. But whenever I print the references in my list I get those result
What I am missing here so that I still have duplicates references in my List ?
Currently, Distinct is matching your objects using object.Equals, which is doing reference equality. One way to tell it to match objects based on other criteria is by implementing IEquatable<SaveMongo>. This example compares objects based on their Id:
public class SaveMongo : IEquatable<SaveMongo>
{
public ObjectId _id { get; set; }
public DateTime date { get; set; }
public Guid ClientId { get; set; }
public List<TypeOfSave> ListType = new List<TypeOfSave>();
public List<ObjectId> ListObjSave = new List<ObjectId>();
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((SaveMongo) obj);
}
public override int GetHashCode()
{
return _id.GetHashCode();
}
public bool Equals(SaveMongo other)
{
return _id.Equals(other._id);
}
}
Use grouping:
toReturn = (from e in toReturn
group e by e._id into g
select g.First()).ToList();
Also, you can group by two (or more) fields:
toReturn = (from e in toReturn
// group by ID and Date component
group e by new { e._id, e.date.Date } into g
select g.First()).ToList();
Related
I'm trying to create an audit-trail like order state history table. This way, Orders could have many OrderStates, and a single State which points to the most recent history item. So far so good when saving an updating. The problems arise when I try to query as if I was using an enum:
public class OrderState
{
public static OrderState Placed = new OrderState("Placed", 1, 1);
public static OrderState Accepted = new OrderState("Accepted", 10, 2);
public static OrderState Cancelled = new OrderState("Cancelled", 20, 3);
public static OrderState Completed = new OrderState("Completed", 30, 4);
protected OrderState()
{
}
public OrderState(string name, int order, int id)
{
Name = name;
Order = order;
Id = id;
}
public int Id { get; set; }
public string Name { get; protected set; }
public int Order { get; protected set; }
public static bool operator == (OrderState state1, OrderState state2)
{
if (ReferenceEquals(state1, null))
{
return ReferenceEquals(state2, null);
}
return state1.Equals(state2);
}
public static bool operator !=(OrderState state1, OrderState state2)
{
return !(state1 == state2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (!(obj is OrderState))
{
return false;
}
return Equals((OrderState)obj);
}
public virtual bool Equals(OrderState other)
{
return other.Id.Equals(Id);
}
public override int GetHashCode()
{
unchecked
{
return ((Id.GetHashCode())*397) ^ Order;
}
}
}
Order class
public class Order
{
public Order()
{
Progress(OrderState.Placed);
}
public int Id { get; set; }
public virtual OrderState State
{
get { return States.OrderByDescending(x => x.State.Order).FirstOrDefault()?.State; }
}
public void Progress(OrderState state)
{
if (States.All(x => x.State != state))
{
States.Add(new OrderStateHistory()
{
Order = this,
State = state
});
}
}
public virtual ICollection<OrderStateHistory> States { get; set; } = new List<OrderStateHistory>();
}
In my code, things like these work fine:
order.Progress(OrderState.Accepted);, if (order.State == OrderState.Accepted)
However, what I'd like to get to is Where(x => x.State.Equals(OrderState.Accepted)) or Where(x => x.State == OrderState.Accepted)
Unfortunately, either of the criterias will yield an 'The specified type member 'State' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.' error.
I know I have been able to do this with NHibernate. Can I even do this with EF?
Since EF needs to translate your LINQ statements to SQL statements,
you can't do this. If you have complex comparison logic in your
overridden Equals() method you will have to duplicate that in the
LINQ statement.
public IQueryable<Foo> FoosEqualTo(IQueryable<Foo> allFoos, Foo target) {
return from foo in allFoos
where foo.Id == target.Id // or other comparison logic...
select foo;
}
public Foo getFoo(Foo target) {
return FoosEqualTo(DC.foos, target).FirstOrDefault();
}
This is my object:
public class MyObject
{
public int id { get; set; }
public string fileName { get; set; }
public string browser { get; set; }
public string protocol { get; set; }
public string family { get; set; }
}
and i have a list of my object:
List<Capture> list = db.Captures.Where(x => x.family == "Web").ToList();
What i want to do is get new list that removed the duplicate protocol.
for example if i have in my list 10 object and 9 of them with protocol DOC and 1 PDF i want a new list with only 2 object DOC and 1 PDF
There are several ways to do this, depending on how you generally want to use the instances of your MyObject class.
The easiest one is implementing the IEquatable<T> interface so as to compare only the protocol fields:
public class MyObject : IEquatable<MyObject>
{
public sealed override bool Equals(object other)
{
return Equals(other as MyObject);
}
public bool Equals(MyObject other)
{
if (other == null) {
return false;
} else {
return this.protocol == other.protocol;
}
}
public override int GetHashCode()
{
return protocol.GetHashCode();
}
}
You can then call Distinct before converting your enumerable into a list.
Alternatively, you can use the Distinct overload that takes an IEqualityComparer.
The equality comparer would have to be an object that determines equality based on your criteria, in the case described in the question, by looking at the protocol field:
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
if (x == null) {
return y == null;
} else {
if (y == null) {
return false;
} else {
return x.protocol == y.protocol;
}
}
}
public int GetHashCode(MyObject obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
return obj.protocol.GetHashCode();
}
}
I believe this is the simplest approach: The following will group list by protocol and then get the first instance from each group to produce an enumerable with one instance of each type of protocol.
list.GroupBy(x => protocol, x => x)
.SelectMany(k, v => v.First());
You could either use Distinct, or use the same solution provided here:
Distinct() with lambda?
Select distinct protocols, loop on them and subselect only first object of the same protocol - thus you'll get the list you need.
I have an Entity Framework Entity that looks something like this:
class ListItemEtlObject
{
public int ID { get; set; }
public string ProjectName { get; set; }
public string ProjectType { get; set; }
public string ProjectCode { get; set; }
public string ProjectDescription { get; set; }
public string JobNo { get; set; }
public string JobDescription { get; set; }
public bool Include { get; set; }
}
I am pulling items from two different data sources into IEnumerable lists. How might I go about comparing the items without using a bunch of if statements to check if there are differences between the properties' values and then set the property's value if they do not match? The idea is to keep the lists synchronized. Also list A has an ID value set, list B does not. I just feel there is a better way to do this than a bunch of
if(objectA.ProjectName != objectB.ProjectName)
{
objectA.ProjectName = objectB.ProjectName;
}
If you have control of the source object then the best declarative way to support value based equality is to implement IEquatable<T>. This does unfortunately require you to enumerate out all of those checks but it's done once at the actual object definition location and doesn't need to be repeated throughout the code base.
class ListItemEtlObject : IEquatable<ListITemEtlObject>
{
...
public void Equals(ListITemEtlObject other) {
if (other == null) {
return false;
}
return
ID == other.ID &&
ProjectName == other.ProjectName &&
ProjectType == other.ProjectType &&
... ;
}
}
Additionally you could choose to overload the equality operator on the object type and allow consumers to simply use != and == on ListItemEtlObject instances and get value equality instead of reference equality.
public static bool operator==(ListItemEtlObject left, ListItemEtlObject right) {
return EqualityComparer<ListItemEtlObject>.Default.Equals(left, right);
}
public static bool operator!=(ListItemEtlObject left, ListItemEtlObject right) {
return !(left == right);
}
The easiest way would be to provide a method on your class that computes a specific hash, much like GetHashCode, and then if two instances compute the same hash, they can be said to be equivalent.
You could simplify it using reflection =)
public virtual void SetDifferences(MyBaseClass compareTo)
{
var differences = this.GetDifferentProperties(compareTo);
differences.ToList().ForEach(x =>
{
x.SetValue(this, x.GetValue(compareTo, null), null);
});
}
public virtual IEnumerable<PropertyInfo> GetDifferentProperties(MyBaseClass compareTo)
{
var signatureProperties = this.GetType().GetProperties();
return (from property in signatureProperties
let valueOfThisObject = property.GetValue(this, null)
let valueToCompareTo = property.GetValue(compareTo, null)
where valueOfThisObject != null || valueToCompareTo != null
where (valueOfThisObject == null ^ valueToCompareTo == null) || (!valueOfThisObject.Equals(valueToCompareTo))
select property);
}
And here are a couple of tests I did for you
[TestMethod]
public void CheckDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("Include"));
g.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
g.JobDescription = "my job";
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("JobDescription"));
}
[TestMethod]
public void SetDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
g.Include = true;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
g.Include = false;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include.Should().BeFalse();
}
This question already has answers here:
What's the best strategy for Equals and GetHashCode?
(7 answers)
Closed 9 years ago.
I have never really done this before so i was hoping that someone could show me the correct what of implementing a override of Except() and GetHashCode() for my class.
I'm trying to modify the class so that i can use the LINQ Except() method.
public class RecommendationDTO{public Guid RecommendationId { get; set; }
public Guid ProfileId { get; set; }
public Guid ReferenceId { get; set; }
public int TypeId { get; set; }
public IList<TagDTO> Tags { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public bool IsActive { get; set; }
public object ReferencedObject { get; set; }
public bool IsSystemRecommendation { get; set; }
public int VisibilityScore { get; set; }
public RecommendationDTO()
{
}
public RecommendationDTO(Guid recommendationid,
Guid profileid,
Guid referenceid,
int typeid,
IList<TagDTO> tags,
DateTime createdon,
DateTime modifiedon,
bool isactive,
object referencedobject)
{
RecommendationId = recommendationid;
ProfileId = profileid;
ReferenceId = referenceid;
TypeId = typeid;
Tags = tags;
CreatedOn = createdon;
ModifiedOn = modifiedon;
ReferencedObject = referencedobject;
IsActive = isactive;
}
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
RecommendationDTO p = obj as RecommendationDTO;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (ReferenceId == p.ReferenceId);// && (y == p.y);
}
public bool Equals(RecommendationDTO p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (ReferenceId == p.ReferenceId);// && (y == p.y);
}
//public override int GetHashCode()
//{
// return ReferenceId;// ^ y;
//}}
I have taken a look at http://msdn.microsoft.com/en-us/library/ms173147.aspx but i was hoping someone could show me within my own example.
Any help would be appreciated.
Thank you
You can override Equals() and GetHashCode() on your class like this:
public override bool Equals(object obj)
{
var item = obj as RecommendationDTO;
if (item == null)
{
return false;
}
return this.RecommendationId.Equals(item.RecommendationId);
}
public override int GetHashCode()
{
return this.RecommendationId.GetHashCode();
}
public override bool Equals(System.Object obj)
{
// Check if the object is a RecommendationDTO.
// The initial null check is unnecessary as the cast will result in null
// if obj is null to start with.
var recommendationDTO = obj as RecommendationDTO;
if (recommendationDTO == null)
{
// If it is null then it is not equal to this instance.
return false;
}
// Instances are considered equal if the ReferenceId matches.
return this.ReferenceId == recommendationDTO.ReferenceId;
}
public override int GetHashCode()
{
// Returning the hashcode of the Guid used for the reference id will be
// sufficient and would only cause a problem if RecommendationDTO objects
// were stored in a non-generic hash set along side other guid instances
// which is very unlikely!
return this.ReferenceId.GetHashCode();
}
Be careful when using a primary key as your test for equality in overriding Equals() because it only works AFTER the object has been persisted. Prior to that your objects don't have primary keys yet and the IDs of the ones in memory are all zero.
I use base.Equals() if either of the object IDs is zero but there probably is a more robust way.
I got the following piece of code:
public class Collect
{
public string name{ get; set; }
public int id { get; set; }
public DateTime registerDate { get; set; }
}
public class ControllingMyList
{
public void prepareList()
{
List<Collect> list = new List<Collect>();
list= loadList();
//Rest of the opperations
}
}
Considering that my loadList method returns for me many duplicated records (id variable) I want to get only one record by ID.
The Distinct() function seems to be a good solution but if I remember correctly, Distinct() filters all the members of the object so just because of a second of difference from "registerDate" variable is considered a criteria to make it distinct, even if its with the same ID.
var list= loadList();
list = list
.GroupBy(i => i.id)
.Select(g => g.First())
.ToList();
You have several options:
Use DistinctBy() extension method from the MoreLinq project
Use the Distinct() overload that accepts a custom equality comparer, and implement a custom comparer
Use Linq GroupBy( x=> x.id) and then take the first item of each group.
Pass in a comparer that uses the id:
public class idCompare : IEqualityComparer<Collect>
{
public bool Equals(Collect x, Collect y)
{
return Equals(x.id, y.id);
}
public int GetHashCode(Collect obj)
{
return obj.id.GetHashCode();
}
}
....
list.Distinct(new idCompare());
static List GetUniques(IEnumerable collection, string attribute) where T : Entity
{
Dictionary<string, bool> tempkvp = new Dictionary<string, bool>();
List<T> uniques = new List<T>();
List<string> columns = collection.FirstOrDefault().GetType().GetProperties().ToList().ConvertAll<string>(x => x.Name.ToLower());
var property = attribute != null && collection.Count() > 0 && columns.Contains(attribute.ToLower()) ? ViewModelHelpers.GetProperty(collection.FirstOrDefault(), attribute) : null;
if (property != null)
{
foreach (T obj in collection)
{
string value = property.GetValue(obj, null).ToString();
if (!(tempkvp.ContainsKey(value)))
{
tempkvp.Add(value, true);
uniques.Add(obj);
}
}
}
return uniques;
}
Implement IEquatable<T> and override Equals and GetHashCode. You can have those take into account only id.
using System;
public class Collect : IEquatable<Collect>
{
public string name{ get; set; }
public int id { get; set; }
public DateTime registerDate { get; set; }
public bool Equals(Collect other)
{
if(other == null)
{
return false;
}
return this.id == other.id;
}
}