I'm currently doing some unit testing of a copy function and I need to compare the elements of the objects between the old list, and the newly copied list.
It works fine, but I was wondering if I can do it in a way that doesn't involve a for loop.
Here is my object:
new NaturePointObject
{
SId = 1,
Name = "Test",
Category = NaturePointCategory.Category1,
CreatorType = CreatorTypeEnum.1,
NaturR = NaturR.Bn,
Description = "Test",
Kumulation = Kumulation.EnEjendom,
Id = 1
}
My old list contains "NaturePointObject" and is called naturPointList, and it will be copied to a list called newNaturePointList.
Here is how I Assert to know if it copied succesfully:
Assert.AreEqual(naturPointList.Count,newNaturePointList.Count);
for (var i = 0; i < newNatureList.Count; i++)
{
Assert.AreEqual(naturPointList[i].Category, newNaturePointList[i].Category);
Assert.AreEqual(naturPointList[i].Description, newNaturePointList[i].Description);
Assert.AreEqual(naturPointList[i].Kumulation, newNaturePointList[i].Kumulation);
Assert.AreEqual(naturPointList[i].Name, newNaturePointList[i].Name);
Assert.AreEqual(naturPointList[i].CreatorType, newNaturePointList[i].CreatorType);
Assert.AreEqual(naturPointList[i].NaturR, newNaturePointList[i].NaturR);
Assert.AreNotEqual(naturPointList[i].SId, newNaturePointList[i].SId);
}
As you can see not all elements of the object must be equal. And I don't care about the "Id" of the object.
Is there a shorter way to do this, than run a for loop?
Probably you want to use CollectionAssert:
CollectionAssert.AreEqual(naturPointList, newNaturePointList, NaturePointObject.CategoryCreatorTypeComparer);
The only thing you need to take in mind is that you need to implement IComparer, to use in the Assert method:
public class NaturePointObject
{
private static readonly Comparer<NaturePointObject> CategoryCreatorTypeComparerInstance = new CategoryCreatorTypeRelationalComparer();
private sealed class CategoryCreatorTypeRelationalComparer : Comparer<NaturePointObject>
{
public override int Compare(NaturePointObject x, NaturePointObject y)
{
// compare fields which makes sense
if (ReferenceEquals(x, y)) return 0;
if (ReferenceEquals(null, y)) return 1;
if (ReferenceEquals(null, x)) return -1;
var categoryComparison = string.Compare(x.Category, y.Category, StringComparison.Ordinal);
if (categoryComparison != 0) return categoryComparison;
return string.Compare(x.CreatorType, y.CreatorType, StringComparison.Ordinal);
}
}
public static Comparer<NaturePointObject> CategoryCreatorTypeComparer
{
get
{
return CategoryCreatorTypeComparerInstance;
}
}
public int SId { get; set; }
public string Category { get; set; }
//other properties
public string CreatorType { get; set; }
}
You can try
Assert.IsTrue(naturPointList.SequenceEqual(newNaturePointList));
If you want to ignore the Id, you can create other classes (without Ids).
Later edit: you could overwrite the Equals method and ignore the Id.
Related
This is my object
public class Totals {
public int Total1 { get; set; }
public int Total2 { get; set; }
public int Total3 { get; set; }
public int Total4 { get; set; }
}
Incrementing the values of Total1 and Total2 using calculateTotals method
private Totals calculateTotals(Totals t) {
if (//condition) {
t.Total1 += 1;
} else {
t.Total2 += 1;
}
return t;
}
**Incrementing value of Total3 and Total4 of the same object with same conditions at a different location using different method calculateOtherTotals, at this point I only need to update Total3 and Total4 **
private Totals calculateOtherTotals(Totals t) {
if (//condition) {
t.Total3 += 1;
} else {
t.Total4 += 1;
}
return t;
}
I am new to c# , I need to increment the values Total1,Total2 and Total3,Total4 separately and the code which I have is working fine
Is there a way to improve my code?, how can I avoid creating two different methods which pretty much does the same logic on different properties? is there a way to create only 1 method to achieve my functionality?
You could do it this way, but essentially the amount of code doesn't change.
This adds a judgment:
Totals calculateTotals(Totals t, bool Flag)
{
//function1:
if (Flag)
{
if (true)
{ //(condition) {
t.Total1++;
}
else
{
t.Total2++;
}
}
//function2:
else
{
if (true)
{ //(condition) {
t.Total3++;
}
else
{
t.Total4++;
}
}
return t;
}
Call it like this:
Totals totals = new Totals();
totals.Total1=0;
totals.Total2=0;
totals.Total3=0;
totals.Total4=0;
calculateTotals(totals,true);//function1:
calculateTotals(totals,false);//function2:
Reflection is one way, though its slow and not a Domain Specific Language:
Type totalsType = typeof(Totals);
var totalToIncrement = condition;
PropertyInfo prop = totalsType.GetProperty("Total" + totalToIncrement);
prop.SetValue(null, 76);
Or perhaps you want to abstract the properties you're incrementing:
private Totals calculateTotals(Totals t)
{
bool condition = true;
AbstractAdds(ref t.Total1, ref t.Total2, condition);
return t;
}
private void AbstractAdds(ref int a, ref int b, bool condition = false)
{
if (condition)
{
a++;
}
else
{
b++;
}
}
}
public class Totals
{
public int Total1;//{ get; set; }
public int Total2;//{ get; set; }
public int Total3;//{ get; set; }
public int Total4;//{ get; set; }
}
I'd personally have a List<int> or int[3] and make the condition calculate the index 0-3:
var index = calcCondition;
Totals[index]++;
This way its extensible for more totals and you get inbuilt functions like LINQ, eg Totals.Sum().
Is there a way to improve my code?, how can I avoid creating two different methods which pretty much does the same logic on different properties? is there a way to create only 1 method to achieve my functionality?
Then it depends on how you want your method (function) to be. (E.g., how you define what your function will do and how your class and properties are characteristic—which, currently, many who want to help you still wonder about.)
Let me give another clear example.
Assume that you answer your additional requirement are:
My object has only 4 properties of "Total"
I want these new function to increment value only 1 when call, no need to add more than 1
This function is called from another class to modify my object value
I want my cool function name calculateOtherTotals being private, because of some unexplained reason such as “I don't like others knowing it exists”.
Then
public OtherClass{
Public Totals ExposeThePrivateCalculateOtherTotals(Totals t, bool IncrementT1 , bool IncrementT2 , bool IncrementT3, bool IncrementT4)
{
calculateOtherTotals(t, IncrementT1 , IncrementT2 , IncrementT3, IncrementT4);
}
Private Totals calculateOtherTotals(Totals t, bool IncrementT1 , bool IncrementT2 , bool IncrementT3, bool IncrementT4) {
if( IncrementT1 ) t.Total1 += 1; //choose your style
if( IncrementT2==true ) ++t.Total2;//choose your style
if( IncrementT3!=false ) t.Total3++; //choose your style
t.Total4 += IncrementT4==true?1:0;//choose your style
return t;
}
}
//In main (how to use)
Totals t= new Totals();
OtherClass doMyFunc = new OtherClass();
t = doMyFunc.ExposeThePrivateCalculateOtherTotals(t, true, false,false,false); // result of operation => t.total1 += 1;
t = doMyFunc.ExposeThePrivateCalculateOtherTotals(t, false, true,false,false); // result of operation => t.total2 += 1;
I am having a bit of a frustrating time finding a simple method to compare and prove that the contents of two lists are equal. I have looked at a number of solutions on stackoverflow but I have not been successful. Some of the solutions look like they will require a large amount of work to implement and do something that on the face of it to my mind should be simpler, but perhaps I am too simple to realize that this cannot be done simply :)
I have created a fiddle with some detail that can be viewed here: https://dotnetfiddle.net/cvQr5d
Alternatively please find the full example below, I am having trouble with the object comparison method (variable finalResult) as it's returning false and if the content were being compared I would expect the value to be true:
using System;
using System.Collections.Generic;
using System.Linq;
public class ResponseExample
{
public Guid Id { get; set; } = Guid.Parse("00000000-0000-0000-0000-000000000000");
public int Value { get; set; } = 0;
public string Initials { get; set; } = "J";
public string FirstName { get; set; } = "Joe";
public string Surname { get; set; } = "Blogs";
public string CellPhone { get; set; } = "0923232199";
public bool EmailVerified { get; set; } = false;
public bool CellPhoneVerified { get; set; } = true;
}
public class Program
{
public static void Main()
{
var responseOne = new ResponseExample();
var responseTwo = new ResponseExample();
var responseThree = new ResponseExample();
var responseFour = new ResponseExample();
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseThree);
objectListTwo.Add(responseFour);
bool result = objectListOne.Count == objectListTwo.Count();
Console.WriteLine($"Count: {result}");
bool finalResult = ScrambledEquals<ResponseExample>(objectListOne, objectListTwo);
Console.WriteLine($"Object compare: {finalResult}");
}
//https://stackoverflow.com/a/3670089/3324415
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T,
int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
}
As people in comments have pointed out this will not work as comparing a complex type by default compares whether the reference is the same. Field by field comparison will not work without implementing equality methods (and then you would need to overload GetHashCode and so on). See https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0
However, if you can use c# 9, which is what you have in the fiddle you can define the type as a record instead of class. Records have built in field by field comparison. See https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records
So public class ResponseExample would become public record ResponseExample and your code works as you expect.
Use Enumerable.All<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) Method which Determines whether all elements of a sequence satisfy a condition.
Once you have initilized your two List
list1.All(x=>list2.Contains(x))
This works by ensuring that all elements in list2 are containted in list1 otherwise returns false
Your method as is will compare if the 2 lists contain the same objects. So it is returning false as there are 4 different objects. If you create your list like this, using the same objects, it will return true:
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseTwo);
objectListTwo.Add(responseOne);
To get a true value when the contents of the objects are the same you could serialize the objects into a json string like this:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
JavaScriptSerializer json = new JavaScriptSerializer();
var cnt = new Dictionary<string,
int>();
foreach (T _s in list1)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T _s in list2)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
If the performance is not a big deal, you can use Newtonsoft.Json. We will be able to compare different types of objects as well as run a deep equals check.
First install the package:
Install-Package Newtonsoft.Json
Here is the code snip:
public static bool DeepEqualsUsingJson<T>(IList<T> l1, IList<T> l2)
{
if (ReferenceEquals(l1, l2))
return true;
if (ReferenceEquals(l2, null))
return false;
if (l1.Count != l2.Count)
return false;
var l1JObject = l1.Select(i => JObject.FromObject(i)).ToList();
var l2JObject = l2.Select(i => JObject.FromObject(i)).ToList();
foreach (var o1 in l1JObject)
{
var index = l2JObject.FindIndex(o2 => JToken.DeepEquals(o1, o2));
if (index == -1)
return false;
l2JObject.RemoveAt(index);
}
return l2JObject.Count == 0;
}
I have a HashSet with errors, <Error>ErrorList.
"Error" has the properties "file" and "block".
So I fill my HashSet with a number of errors, some of which are exactly the same and therefore repeat themselves. The multiple occurrences are completely tolerated by the HashSet. As a last attempt I created a separate list and distincted it: List<Error> noDupes = ErrorList.Distinct().ToList();
But also here my list remains unchanged. Why does neither the hashset nor my noDupes list work? Are there alternative solutions?
Here's the important part of my code:
#region Properties
HashSet<Error> ErrorList { get; set; } = new HashSet<Error>();
private Stopwatch StopWatch { get; set; } = new Stopwatch();
private string CSVFile { get; set; } = null;
int n;
#endregion
ErrorList.Add(new Error
{
File = x,
Block = block
}); ;
n = FileCall.IndexOf(i);
int p = n * 100 / FileCall.Count;
SetConsoleProgress(n.ToString("N0"), p);
}
}
int nx = 0;
List<Error> noDupes = ErrorList.Distinct().ToList();
The Error-Class:
namespace ApplicationNamespace
{
public class Error
{
public string File { set; get; }
public int Block { set; get; }
}
}
Override the default Equals() and GetHashCode() implementations (like the others have mentioned in the comments) for the HashSet<> or Distinct() to work. You can also implement IEquatable<>, which will require you to override the Equals() and GetHashCode() methods.
public class Error : IEquatable<Error>
{
public string File { set; get; }
public int Block { set; get; }
public bool Equals(Error other)
{
// Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
// Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
// Check whether the error's properties are equal.
return File == other.File && Block == other.Block;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
return $"{Block}-{File}".GetHashCode(); // adjust this as you see fit
}
}
Reference: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=netcore-3.1
Remember to handle null values on the File string. (Could replace it with String.Empty for instance.) It's also common to "cache" the hashcode in a private variable, so that once calculated the cached value can be returned on consequent calls to GetHashCode(). For this you will most likely also need to make the class immutable.
(You won't have to do any of this with C# 9's record types.)
Consider objects of the following type:
public class MyObject
{
// "defining" attributes
private string member1;
private string member2;
private string member3;
// other attributes
private string member4;
private string member5;
// ctor
public MyObject(){}
public bool compare(MyObject that)
{
// compare this object with another (that)
}
The compare() method should behave as follows. It only considers "defining" attributes. If they are all different between two objects, it should return false. If they are all the same, return false. In other cases, return true (if only one or two of them differ between the two objects).
The question is, do I have to resort to a huge if statement for this? Is there a "better" solution?
Instead of creating n number of strings, you can create property called List<string> DefiningAttributes and List<string> OtherAttributes.
Now add values to this lists where you want, for now I am doing it in constructor. Use Except() method to get difference from DefiningAttributes and OtherAttributes
Check below implementation
public class MyObject
{
// "defining" attributes
public List<string> DefiningAttributes { get; set; }
// other attributes
public List<string> OtherAttributes { get; set; }
public MyObject()
{
//I used constructor to assign values
DefiningAttributes = new List<string>() { "ABC", "PQR", "XYZ" };
OtherAttributes = new List<string>() { "ABC", "PQR", "Stackoverflow" };
}
public bool compare(MyObject that)
{
var difference = this.DefiningAttributes.Except(that.DefiningAttributes);
//Return false If they are all different between two objects OR if they are all same
if(difference.Count() == this.DefiningAttributes.Count() || !difference.Any())
return false;
//Otherwise return true
return true;
}
}
For more details, read Enumerable.Except method
I think this should do it
var comp1 = this.member1 == that.member1;
var comp2 = this.member2 == that.member2;
var comp3 = this.member3 == that.member3;
var comparisons = new List<string>() { comp2, comp3 };
return comparisons.Any(val => val != comp1 );
comp1, comp2 and comp3 will be bools. If any of those comparisons are not the same as the first comparison*, we know we have different results.
[*] You could use any reference point instead of the first comparison
Edit: Whoops, I thought this was a javascript question, but I then realized it was C#. I just changed my answer to use C# syntax, but the idea is the same. This requires the Linq extension method Any.
The following code should do the trick.
If you want to increase the number of defining properties you just edit the size of the array or swap it to a list.
It should iterate over them and when one does not mach return true.
If at the end none matches returns false.
public class MyObject
{
// "defining" attributes
public string[] definingAttributes = new string[3];
// other attributes
private string member4;
private string member5;
// ctor
public MyObject() { }
public bool compare(MyObject that)
{
bool? previousResult = null;
// compare this object with another (that)
for (int i = 0; i < definingAttributes.Length; i++)
{
if (previousResult == null)
{
previousResult = definingAttributes[i] == that.definingAttributes[i];
}
if (definingAttributes[i] != that.definingAttributes[i])
{
if (previousResult != (definingAttributes[i] == that.definingAttributes[i]))
{
return true;
}
}
}
return false;
}
}
I've got the following code:
public IList<IProductViewModel> ChildProducts { get; set; }
public IList<IProductViewModel> GiftItems { get; set; }
public IList<IProductViewModel> PromoItems { get; set; }
public IList<IProductViewModel> NonGiftItems
{
get
{
return NonPromoItems.Except(GiftItems, new ProductViewModelComparer()).ToList();
}
}
public IList<IProductViewModel> NonPromoItems
{
get
{
return ChildProducts.Where(p => !p.IsPromotion).ToList();
}
}
ProductViewModelComparer:
public class ProductViewModelComparer : IEqualityComparer<IProductViewModel>
{
#region IEqualityComparer<IProductViewModel> Members
public bool Equals(IProductViewModel x, IProductViewModel y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return String.Equals(x.ProductId, y.ProductId);
}
public int GetHashCode(IProductViewModel obj)
{
return obj.ProductId.GetHashCode();
}
#endregion
}
So basically, NonPromoItems is (ChildProducts - PromoItems) and NonGiftItems is (NonPromoItems - GiftItems)
However When:
ChildProducts = IEnumerable<IProductViewModel>[6]
PromoItems = IEnumerable<IProductViewModel>[1] where item matches 1 item in ChildProducts
GiftItems = IEnumerable<IProductViewModel>[0]
My Result is
NonPromoItems = IEnumerable<IProductViewModel>[5] This is Correct
NonGiftItems = IEnumerable<IProductViewModel>[4] This is Incorrect
Somehow an Except(...) is removing an item when given an empty list to subtract.
Any ideas anyone?
Is the removed item a duplicate of another in the list? If so, this is standard behavior for the Except method call:
The Except<T> is a little tricky in that instead of returning the difference as you might expect, it instead returns the set difference. A mathematical set does not contain duplicates (e.g. HashSet).
Here's an example of what I mean:
int[] ints = { 3, 3, 3 };
var result = ints.Except(5); // result contains one element only: { 3 }
You can read more on this CodeProject thread in which I described the problem.
To get "Except"-like functionality while leaving duplicates, you can write the following line:
var ints = new [] { 3, 42, 3 };
var excluded = new [] { 42 };
var results = ints.Where(i => !excluded.Contains(i)); // returns { 3, 3 }