Edit:
_existingRatings and _targetRatings are both collections from a database, where Rating and RatingType are both key/values.
I've got two RatingDto collections that I need to compare each other against: _existingRates and _targetRates. I'll be simply doing a comparison on the RatingTypeId, then need to check if the same one in the _targetRates has an empty Rating string.
As it's Monday morning, my brain is still asleep and I'm sure there's a better and simpler way to do this with LINQ. This is what I'm currently doing:
class RatingDto
{
public int RatingTypeId { get; set; }
public string RatingType { get; set; }
public string Rating { get; set; }
}
foreach (var existing in _existingRatings)
{
foreach(var target in _targetRatings)
{
if(existing.RatingTypeId == target.RatingTypeId)
{
if(target.Rating == string.Empty)
{
_targetHasMissingRatings = true;
}
}
}
}
This should be okay as the maximum amount is around 7 in each collection, but I'm sure there's a better and cleaner way with LINQ.
That's should what you are looking for :
(i love to use string.IsNullOrEmpty rather than cmp string.Empty)
_targetHasMissingRatings = _existingRatings.Any(er => string.IsNullOrEmpty(_targetRatings.FirstOrDefault(tr => er.RatingTypeId == tr.RatingTypeId)?.Rating));
Use IComparable so you can use OrderBy on the entire class or simply compare two instances of the class.
List<RatingDto> ordered = _existingRatings.OrderBy(x => x).ToList();
See code below :
class RatingDto : IComparable<RatingDto>
{
public int RatingTypeId { get; set; }
public string RatingType { get; set; }
public string Rating { get; set; }
public int CompareTo(RatingDto other)
{
if (this.RatingTypeId != other.RatingTypeId)
{
return this.RatingTypeId.CompareTo(other.RatingTypeId);
}
else
{
return this.RatingType.CompareTo(other.RatingType);
}
}
}
Code would look like this :
List<RatingDto> _existingRatings = new List<RatingDto>();
List<RatingDto> _targetRatings = new List<RatingDto>();
Boolean _targetHasMissingRatings = false;
foreach (var existing in _existingRatings)
{
foreach (var target in _targetRatings)
{
if (existing == target)
{
_targetHasMissingRatings = true;
break;
}
}
if (_targetHasMissingRatings == true) break;
}
Related
I have these classes:
public class AlertEvaluation
{
public string AlertId { get; set; }
public ICollection<EvaluatedTag> EvaluatedTags { get; set; }
public string TransactionId { get; set; }
public EvaluationStatus EvaluationStatus { get; set; }
public DateTime EvaluationDate { get; set; }
}
public class EvaluatedTag
{
public string Id { get; set; }
public string Name { get; set; }
}
And I would like to get a list of alert evaluations grouped by AlertId, and by EvaluatedTags, meaning that I would like to compare and group evaluations that not only have the same AlertId, but to also have the same list of EvaluatedTags. (And also get the last evaluation in time)
I tried this:
var evaluationsGroupedAndOrdered = evaluations.GroupBy(x => new { x.AlertSettingId, x.EvaluatedLabels })
.Select(x => x.OrderByDescending(z => z.EvaluationDate ).FirstOrDefault()).ToList();
But of course, the comparing of list properties like that did not work.
I read something about adding an equality comparer in GroupBy, which would mean comparing the lists inside the objects right? But I'm not sure of how to implement it in the right way.
I tried (based on GroupBy on complex object (e.g. List<T>)) :
public class AlertEvaluationComparer : IEqualityComparer<AlertEvaluation>
{
public bool Equals(AlertEvaluation x, AlertEvaluation y)
{
return x.AlertId == y.AlertId && x.EvaluatedTags.OrderBy(val => val.Name).SequenceEqual(y.EvaluatedTags.OrderBy(val => val.Name));
}
public int GetHashCode(AlertSettingEvaluation x)
{
return x.AlertId.GetHashCode() ^ x.EvaluatedTags.Aggregate(0, (a, y) => a ^ y.GetHashCode());
}
}
But did not work either.. Maybe because my list EvaluatedTags is not a list of strings but of individual objects.
Does anybody have a nice solution for this?
A typical way to compare two lists is to use the System.Linq exension method, SequenceEquals. This method returns true if both lists contain the same items, in the same order.
In order to make this work with an IEnumerable<EvaluatedTag>, we need to have a way to compare instances of the EvaluatedTag class for equality (determining if two items are the same) and for sorting (since the lists need to have their items in the same order).
To do this, we can override Equals and GetHashCode and implement IComparable<EvaluatedTag> (and might as well do IEquatable<EvaluatedTag> for completeness):
public class EvaluatedTag : IEquatable<EvaluatedTag>, IComparable<EvaluatedTag>
{
public string Id { get; set; }
public string Name { get; set; }
public int CompareTo(EvaluatedTag other)
{
if (other == null) return -1;
var result = string.CompareOrdinal(Id, other.Id);
return result == 0 ? string.CompareOrdinal(Name, other.Name) : result;
}
public bool Equals(EvaluatedTag other)
{
return other != null &&
string.Equals(other.Id, Id) &&
string.Equals(other.Name, Name);
}
public override bool Equals(object obj)
{
return Equals(obj as EvaluatedTag);
}
public override int GetHashCode()
{
return Id.GetHashCode() * 17 +
Name.GetHashCode() * 17;
}
}
Now we can use this in the custom comparer you have in your question, for sorting and comparing the EvaluatedTags:
public class AlertEvaluationComparer : IEqualityComparer<AlertEvaluation>
{
// Return true if the AlertIds are equal, and the EvaluatedTags
// contain the same items (call OrderBy to ensure they're in
// the same order before calling SequenceEqual).
public bool Equals(AlertEvaluation x, AlertEvaluation y)
{
if (x == null) return y == null;
if (y == null) return false;
if (!string.Equals(x.AlertId, y.AlertId)) return false;
if (x.EvaluatedTags == null) return y.EvaluatedTags == null;
if (y.EvaluatedTags == null) return false;
return x.EvaluatedTags.OrderBy(et => et)
.SequenceEqual(y.EvaluatedTags.OrderBy(et => et));
}
// Use the same properties in GetHashCode that were used in Equals
public int GetHashCode(AlertEvaluation obj)
{
return obj.AlertId?.GetHashCode() ?? 0 * 17 +
obj.EvaluatedTags?.Sum(et => et.GetHashCode() * 17) ?? 0;
}
}
And finally we can pass your AlertEvaluationComparer to the GroupBy method to group our items:
var evaluationsGroupedAndOrdered = evaluations
.GroupBy(ae => ae, new AlertEvaluationComparer())
.OrderBy(group => group.Key.EvaluationDate)
.ToList();
Here's a go at it, getting away from Linq a bit to make it easier to build the groups one at a time while leveraging sorting:
// Build groups by using a combination of AlertId and EvaluatedTags hashcode as group key
var groupMap = new Dictionary<string, SortedSet<AlertEvaluation>>();
foreach (var item in evals)
{
var combinedKey = item.AlertId + EvaluatedTag.GetCollectionHashCode(item.EvaluatedTags);
if (groupMap.TryGetValue(combinedKey, out SortedSet<AlertEvaluation>? groupItems))
{
// Add to existing group
groupItems.Add(item);
}
else
{
// Create new group
groupMap.Add(combinedKey, new SortedSet<AlertEvaluation> { item });
}
}
// Get a list of groupings already sorted ascending by EvaluationDate
List<SortedSet<AlertEvaluation>>? groups = groupMap.Values.ToList();
This assumes that the classes implement IComparable and Equals/GetHashCode to facilitate sorting:
public class AlertEvaluation : IComparable<AlertEvaluation>
{
public string AlertId { get; set; }
public ICollection<EvaluatedTag> EvaluatedTags { get; set; }
public string TransactionId { get; set; }
public EvaluationStatus EvaluationStatus { get; set; }
public DateTime EvaluationDate { get; set; }
// Used by SortedSet
public int CompareTo(AlertEvaluation? other)
{
if (other is null)
{
return 1;
}
return EvaluationDate.CompareTo(other.EvaluationDate);
}
}
public class EvaluatedTag : IEquatable<EvaluatedTag?>
{
public string Id { get; set; }
public string Name { get; set; }
public bool Equals(EvaluatedTag? other) => other != null && Id == other.Id && Name == other.Name;
public override int GetHashCode() => HashCode.Combine(Id, Name);
// Helper to get a hash of item collection
public static int GetCollectionHashCode(ICollection<EvaluatedTag> items)
{
var code = new HashCode();
foreach (var item in items.OrderBy(i => i.Id))
{
code.Add(item);
}
return code.ToHashCode();
}
}
By the way, I'm using the fancy new HashCode class in .NET Core to override hash codes.
Our users will have a List<EmailRecords> collection, and only one of which will be marked (bool)IsPrimary = true.
I'd like to write Entity Framework code to update this.
Something like this:
UPDATE dbo.EmailRecords
SET IsPrimary = 1
WHERE EmailRecordId = #RecordId
UPDATE dbo.EmailRecords
SET IsPrimary = 0
WHERE EmailRecordId != #RecordId
AND ParentRecordId = #ParentRecordId
My object is:
public class EmailRecords
{
public int EmailRecordId { get; set; }
public int ParentRecordId { get; set; }
public string EmailAddress { get; set; }
public bool IsPrimary { get; set; }
}
How can I do this with Entity Framework?
I was thinking about doing something like this:
foreach (var thisRecord in profile.EmailRecords)
{
if (thisRecord.EmailRecordId == thisId)
{
thisRecord.IsPrimary = true;
}
else
{
thisRecord.IsPrimary = false;
}
}
db.SaveChanges();
Is there a cleaner way to do this?
Are you looking for C# code, something along these lines?
foreach(var record in profile.EmailRecords)
{
record.IsPrimary = record.EmailRecordId == recordId;
}
Using this example model you've given.
public class EmailRecords
{
public int EmailRecordId { get; set; }
public int ParentRecordId { get; set; }
public string EmailAddress { get; set; }
public bool IsPrimary { get; set; }
}
You can unset this primary behavior using this combination of LINQ and code.
// Change this Int32 to match your #RecordId and #ParentRecordId. Possibly parameterize this entire code snippet into a method.
Int32 recordIdToChange = 1;
Int32 parentRecordIdToChange = 1;
// Set the new primary.
EmailRecords emailRecordToSetToPrimary = profile.EmailRecords.Where(cs => cs.EmailRecordId == recordIdToChange).FirstOrDefault();
if (emailRecordToSetToPrimary != null){
emailRecordToSetToPrimary.IsPrimary = true;
}
// Only unset those records whose id does not match the new primary AND is currently set as a primary.
List<EmailRecords> emailRecordsToUnsetFromPrimary = profile.EmailRecords.Where(cs => cs.EmailRecordId != recordIdToChange && cs.IsPrimary == true && cs.ParentRecordId == parentRecordIdToChange).ToList();
foreach (EmailRecords emailRecordToUnsetFromPrimary in emailRecordsToUnsetFromPrimary){
emailRecordToUnsetFromPrimary.IsPrimary = false;
}
// Perform your save on the emailRecords list collection.
Im making a new years eve quiz for some friends. The quiz itself is done and working I just thought it would be cool to autocorrect the answers once they are posted.
The question is what's the best way to compare the posted object with an object that has all the right answers, reflection?. There has to be a slick way to do it and avoid having a lot of if's.
public class QuizModel
{
public string Name { get; set; }
public string Quiz1 { get; set; }
public string Quiz2 { get; set; }
public string Quiz3 { get; set; }
etc..
}
You dont have to write me any code. I just want some directions on what the best ( and most important the coolest) way to do it :)
As for your explanation, the implementation of this class should be a list of answers (or a dictionary, or an array, etc):
public class QuizModel
{
public string Name { get; set; }
public List<string> Quizs { get; set; }
}
Then, to check if all the answers are the same:
public bool AreEqual(model1, model2){
for(var i = 0; i < Math.Min(model1.Quizs.Count, model2.Quizs.Count); ++i)
if(model1.Quizs[i] != model2.Quizs[i])
return false;
return true;
}
In a similar way you can get the number of answers that are identical.
You don't need to use reflection to compare values, no :)
If all of the values are encapsulated within a model, then so should be the comparison logic. One simple approach could be to just override .Equals(). Something like this:
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (!(obj is QuizModel))
return false;
var quiz = obj as QuizModel;
return
quiz.Name.Equals(this.Name) &&
quiz.Quiz1.Equals(this.Quiz1) &&
// etc.
}
Of course, that's the easy part. The real challenge is going to be that string comparison logic. Strings sounds like free text input. So if one of the answers is "Christmas Day" then what do you do with inputs like "Christmas day" or "christmas" or "december 25th" and so on? That's up to your business logic. But the actual logic of "are these two objects equal" is pretty straightforward.
Semantically you might choose not to override .Equals() for this purpose. I could see a good argument against it, claiming that the objects are different but simply contain the same values. (A great real world example of this are identical twins.) So you may choose to implement something else, like IEquatable or just a custom method like .IsEqualTo(QuizModel quiz). But the logic therein would be the same either way.
I'm giving you two solutions to your question. I think you want to give scores to your friends(I would do that). If so, here is a solution that give you the score for the answer, I'm supposing that every quiz has the same value and you have the correct answer for all the quiz. First you could try by reflection compare all the values of the quizes, and return total(assuming your quizes have the same value. Then if you want to be more flexible below I suggest you a possible solution when the quizes may have a different value(score).
1)
public class QuizModel
{
public string Name { get; set; }
public string Quiz1 { get; set; }
public string Quiz2 { get; set; }
public string Quiz3 { get; set; }
}
public class QuizComparer
{
public QuizComparer(QuizModel correctOne, IComparer<string> comparer, int quizValue = 1)
{
this.CorrectOne = correctOne;
this.Comparer = comparer;
this.QuizValue = quizValue;
}
public int Compare(QuizModel toCompareOne)
{
Type type = toCompareOne.GetType();
var propertiesInfo = type.GetProperties();
int result = 0;
foreach (var propertyInfo in propertiesInfo)
{
if (propertyInfo.CanRead)
{
var toCompareOnePropertyValue = type.GetProperty(propertyInfo.Name).GetValue(toCompareOne).ToString();
var correctOnePropertyValue = type.GetProperty(propertyInfo.Name).GetValue(this.CorrectOne).ToString();
if (Comparer.Compare(toCompareOnePropertyValue, correctOnePropertyValue) == 0)//equals
{
result += QuizValue;
}
}
}
return result;
}
public QuizModel CorrectOne { get; set; }
public IComparer<string> Comparer { get; set; }
public int QuizValue { get; set; }
}
2) Secondly if you want to give to your quizes individual scores, you could find this very helpful:
public class QuizModel
{
public string Name { get; set; }
[QuizValue(value: 1)]
public string Quiz1 { get; set; }
[QuizValue(value: 2)]
public string Quiz2 { get; set; }
[QuizValue(value: 3)]
public string Quiz3 { get; set; }
}
public class QuizComparer
{
public QuizComparer(QuizModel correctOne, IComparer<string> comparer, int quizValue = 1)
{
this.CorrectOne = correctOne;
this.Comparer = comparer;
this.QuizDefaultValue = quizValue;
}
public int Compare(QuizModel toCompareOne)
{
Type type = toCompareOne.GetType();
var propertiesInfo = type.GetProperties();
int result = 0;
foreach (var propertyInfo in propertiesInfo)
{
if (propertyInfo.CanRead && propertyInfo.Name != "Name")
{
var toCompareOnePropertyValue = type.GetProperty(propertyInfo.Name).GetValue(toCompareOne).ToString();
var correctOnePropertyValue = type.GetProperty(propertyInfo.Name).GetValue(this.CorrectOne).ToString();
int value = GetQuizValue(propertyInfo);
if (Comparer.Compare(toCompareOnePropertyValue, correctOnePropertyValue) == 0)//equals
{
result += value;
}
}
}
return result;
}
private int GetQuizValue(PropertyInfo propertyInfo)
{
var attributes = propertyInfo.GetCustomAttributes(typeof(QuizValue), false);
int value = this.QuizDefaultValue;
if (attributes != null && attributes.Count() > 0)
{
var quizValueAttribute = attributes[0];
if (quizValueAttribute is QuizValue)
{
var quizValue = quizValueAttribute as QuizValue;
value = quizValue.Value;
}
}
return value;
}
public QuizModel CorrectOne { get; set; }
public IComparer<string> Comparer { get; set; }
public int QuizDefaultValue { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Property)]
public class QuizValue : System.Attribute
{
public QuizValue(int value = 1)
{
this.Value = value;
}
public int Value
{
get;
set;
}
}
Please try like below, May this will leads you to get what you think
It will always compare the Quiz2 with the Quiz1 value.
public string Quiz1 {get; set;}
[CompareAttribute("Quiz1", ErrorMessage = "Quiz2 is mismatch with Quiz1")]
public string Quiz2 { get; set; }
this will give you wrong answers only:
var quiz = new QuizModel() { Name = "Quiz 1", Quiz1 = "Correct answer", Quiz2 = "Correct answer", Quiz3 = "Correct answer" };
var correctQuiz = new QuizModel() { Name = "Quiz 1", Quiz1 = "Correct answer", Quiz2 = "Wrong answer", Quiz3 = "Wrong answer 2" };
Func<QuizModel, List<string>> getListOfAnswers = (currentquiz) =>
(
from property
in typeof(QuizModel).GetProperties()
select property.Name + " " + property.GetValue(currentquiz)
).ToList();
var answers = getListOfAnswers(quiz);
var correctAnswers = getListOfAnswers(correctQuiz);
var wrongAnswers = correctAnswers.Except(answers);
output:
Quiz2 Wrong answer
Quiz3 Wrong answer 2
This solution is using Reflection, LINQ and Anonymous functions at once so it's very cool :)
I have a GroupSummary class that has some properties like this in it:
public class GroupsSummary
{
public bool FooMethod()
{
////
}
public bool UsedRow { get; set; }
public string GroupTin { get; set; }
public string PayToZip_4 { get; set; }
public string PayToName { get; set; }
public string PayToStr1 { get; set; }
public string PayToStr2 { get; set; }
public string PayToCity { get; set; }
public string PayToState { get; set; }
public bool UrgentCare_YN { get; set; }
}
Then I have a Dictionary like <string, List<GroupsSummary>
For each of these dictionary items I want to find all the distinct addresses but the properties of this class that define a distinct address for me are
PayToStr1,PayToStr2,PayToCity,PayToState
I know as far as I can say something like mydictionartItem.select(t => t).Distinct().ToList() but I think that will compare all the properties of this class which is wrong. So how should I solve this?
var newDict = dict.ToDictionary(
x=>x.Key,
v=>v.Value.GroupBy(x=>new{x.PayToStr1, x.PayToStr2, x.PayToCity, x.PayToState})
.Select(x=>x.First())
.ToList());
implement IEquatable<T> interface on the GroupsSummary Class. More information can be found here
IEquatable
defines a method Equals. Remember to overload the GetHashCode method as well
Write your own IEqualityComparer, like so:
public class GroupsSummaryComparer : IEqualityComparer<GroupsSummary>
{
public bool Equals(GroupsSummary x, GroupsSummary y)
{
if (object.ReferenceEquals(x, y))
return true;
else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
return false;
return x.PayToStr1 == y.PayToStr1 && x.PayToStr2 == y.PayToStr2 && x.PayToCity == y.PayToCity && x.PayToState == y.PayToState;
}
public int GetHashCode(GroupsSummary obj)
{
if (obj == null)
return 0;
int code;
if (obj.PayToStr1 != null)
code ^= obj.PayToStr1.GetHashCode();
if (obj.PayToStr2 != null)
code ^= obj.PayToStr2.GetHashCode();
if (obj.PayToCity != null)
code ^= obj.PayToCity.GetHashCode();
if (obj.PayToState != null)
code ^= obj.PayToState.GetHashCode();
return code;
}
}
Then you can pass it to Distinct
This may be safer than implementing IEquatable<GroupsSummary> directly on the class, since, in other situations, you may want to test them for full equality.
The easiest way would be to implement an IEqualityComparer<GroupsSummary>
Then you can say something like
HashSet<GroupSummary> unique = new HashSet<GroupsSummary>(
myDict.Values ,
new MyGroupsSummaryEqualityComparer()
) ;
I have a class like this
public class HistoryEntry
{
DateTime Date{ get; protected set; }
HistoryType EntryType { get; protected set; }
}
public enum HistoryType
{
Cancelled,
Cleared,
Redeemed,
Refunded
}
I have an unordered list of these History Entries, and I do Exists statements to see if an entry exists in the list.
return Entries.Exists(e => e.EntryType == HistoryEntryType.Cancelled);
Now I need to change this so that this method returns whether or not a Cancelled entry exists after the TimeStamp of the last Cleared entry if one exists, otherwise just return whether a Cancelled entry exists at all.
I'm limited to options available in .Net 2.0
How about something like this:
private bool ContainsCanceled(List<HistoryEntry> list)
{
list.Sort();
int index = list.FindLastIndex(delegate(HistoryEntry h) { return h.HistoryType == HistoryType.Cleared; });
for (int i = index >= 0? index : 0; i < list.Count; i++)
{
if (list[i].HistoryType == HistoryType.Cancelled)
{
return true;
}
}
return list.Exists(delegate(HistoryEntry h) { return h.HistoryType == HistoryType.Cancelled; });
}
I'm using C#2.0 syntax...
Oh and one more thing, make sure your HistoryEntry class implements IComparable:
public class HistoryEntry : IComparable<HistoryEntry>
{
public DateTime Date { get; set; }
public HistoryType HistoryType { get; set; }
public int CompareTo(HistoryEntry other)
{
return this.Date.CompareTo(other.Date);
}
}
Search for the index of the matching entries in the list, which requires two searches.
int p = Entries.FindIndex(e => e.EntryType == HistoryEntryType.Cleared);
if (p < 0)
p = 0;
p = Entries.FindIndex(p, e => e.EntryType == HistoryEntryType.Cancelled);
return (p >= 0);
See http://msdn.microsoft.com/en-us/library/efasdh0s.aspx.