How to compare two lists in .NET - c#

List<Employee> emplist = new List<Employee>();
emplist.Add(new Employee { Name = "Emp_1", BasicSalary = 1000, Id = Guid.NewGuid(), HRA = 100, DA = 10, TotalSalary = 1110 });
emplist.Add(new Employee { Name = "Emp_2", BasicSalary = 1000 * 2, Id = Guid.NewGuid(), HRA = 200, DA = 20, TotalSalary = 2220 });
emplist.Add(new Employee { Name = "Emp_3", BasicSalary = 1000 * 3, Id = Guid.NewGuid(), HRA = 300, DA = 30, TotalSalary = 3330 });
var result = empRep.CallSupportFindAll();
// CollectionAssert.AreEqual(emplist, result);
Assert.AreEqual(emplist, result);
var r1 = result[0];
Assert.AreEqual(r1.Name, emplist[0].Name);
Assert.AreEqual(r1.TotalSalary, emplist[0].TotalSalary);
Assert.AreEqual(r1.BasicSalary, emplist[0].BasicSalary);
I want to compare two lists emplist and result. Assert.AreEqual(r1.Name, emplist[0].Name); worked but if we have thousands of records then I need to write thousands of lines.
so please answer-- for one line code for compare two list...
thanks in advance

If I understand correctly, you simply have two instances of List<Employee> and you want to assert that they are equal. I.e. they have the same number of Employee instances, in the same order, with equal properties.
If so, this has nothing to do with FakeItEasy. You simply need a method of performing the assertion. Personally I would do this using Fluent Assertions.
result.Should().BeEquivalentTo(emplist);

You can use HashSet<> to compare the two lists.
First, define an equaliser like this
public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
//You can implement the equal method as you like. For instance, you may compare by name
if (x.Id == y.Id)
return true;
return false;
}
public int GetHashCode(Employee employee)
{
return employee.Id.GetHashCode();
}
}
Next, create 2 hash sets based on the input and the result of the method
var equaliser = new EmployeeComparer();
HashSet<Employee> inputHashset = new HashSet<Employee>(emplist ,equaliser );
HashSet<Employee> resultHashset = new HashSet<Employee>(result,equaliser);
Finally, assert the equality of the two sets. It means, instead of
Assert.AreEqual(emplist, result);
Do
Assert.IsTrue(inputHashset.SetEquals(resultHashset));

public bool compareTwolist<T>(List<T> lst1,List<T> lst2)
{
bool bresult = false;
if (lst1.GetType() != lst2.GetType())
{
return false;
}
//if any of the list is null, return false
if ((lst1 == null && lst2 != null) || (lst2 == null && lst1 != null))
return false;
//if count don't match between 2 lists, then return false
if(lst1.Count != lst2.Count)
return false;
foreach (T item in lst1)
{
T obj1 = item;
T obj2 = lst2.ElementAt(lst1.IndexOf(item));
Type type = typeof(T);
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
string obj1Value = string.Empty;
string obj2Value = string.Empty;
if (type.GetProperty(property.Name).GetValue(obj1) != null)
obj1Value = type.GetProperty(property.Name).GetValue(obj1).ToString();
if (type.GetProperty(property.Name).GetValue(obj2) != null)
obj2Value = type.GetProperty(property.Name).GetValue(obj2).ToString();
//if any of the property value inside an object in the list didnt match, return false
if (obj1Value.Trim() != obj2Value.Trim())
{
bresult = false;
break;
}
}
}
return bresult;
}
yes, I got a solution by creating user define function and pass two lists which we want to compare..
and just check is var a is true or not
var a = compareTwolist(empwithoutid, resultwithoutid);
Assert.IsTrue(a);

The comparison methods suggested in the other answers require you to implement equals on all your objects. This doesn't scale well from a maintenance perspective. Also in some tests only subset of fields are to be compared. This means multiple compare or equals methods to be implemented. An alternative is stateprinter which dumps state to a string to compare against. It can even write and rewrite your asserts for you. See https://github.com/kbilsted/StatePrinter/blob/master/doc/AutomatingUnitTesting.md for more info on automation and https://github.com/kbilsted/StatePrinter/blob/master/doc/TheProblemsWithTraditionalUnitTesting.md for an in depth discussion on problems with unit testing as you are facing now.

Related

Compare two lists on one property and dont add duplicate

Hello so for some reason using various examples i havent been able to solve this.
So i have two lists one containing global values and one vlues that are set on a specific property. What i want to achieve is compare the two lists and keep the specific ones and then add the global ones that are not in the specific list based on its name.
i have tried this
var pidConfigValues = await _database.GetConfigurationValuesForPid(productGroup);
var globalConfigValues = await _database.GetGlobalConfigurationValues();
var allConfigs = pidConfigValues.Where(c => globalConfigValues.All(d => c.Name != d.Name)).ToList();
I guess something is wrong with the Where condition because the allConfigs ends up as empty. The both variables that gets compared are lists of same type of object
Example data
pidConfigValues would consist of objects like
Name: config.myConfig,
Pid: 2,
Value: 1
and globalConfigValues would be like
Name: config.myConfig,
Pid: Null,
Value: 0
Name: config.someOtherConfig,
Pid: Null,
Value: 1
So in the example above i would want allConfigs to be
Name: config.myConfig,
Pid: 2,
Value: 1
Name: config.someOtherConfig,
Pid: Null,
Value: 1
So in allConfigs only the config.myConfig with pid would be shown and from global only add the ones that does not exist in the specific one
Here is one way of doing it:
var pidConfigValues = new List<Config>()
{
new Config() { Name = "config.myConfig", Pid = 2, Value = 1}
};
var globalConfigValues = new List<Config>()
{
new Config() { Name = "config.myConfig", Pid = null, Value = 0},
new Config() { Name = "config.someOtherConfig", Pid = null, Value = 1}
};
var result = pidConfigValues.Concat(globalConfigValues)
.GroupBy(x => x.Name)
.Select(x => x.First()) //if multiple entities have the same name pick the first one which will be the one from pidConfigValues
One solution would be to use Union in combination with a custom EqualityComparer that compares the configs based on their Name-property:
// in your code:
var allConfigs = pidConfigValues.Union(globalConfigValues, new MyConfigComparer()).ToList();
// sample for the comparer:
public class MyConfigComparer : IEqualityComparer<MyConfig>
{
public bool Equals(MyConfig c1, MyConfig c2)
{
if (object.ReferenceEquals(c1, c2))
return true;
if (c1 == null || c2 == null)
return false;
return c1.Name.Equals(c2.Name, StringComparison.Ordinal);
}
public int GetHashCode(MyConfig x)
{
return x.Name.GetHashCode();
}
}
Ciao, you can use Distinct (by rewriting EqualityComparer). Here working example:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var pidConfigValues = new List<Configuration>();
var globalConfigValues = new List<Configuration>();
Configuration pidConfigValue = new Configuration("config.myConfig", 2, 1);
Configuration globalConfigValue1 = new Configuration("config.myConfig", null, 0);
Configuration globalConfigValue2 = new Configuration("config.someOtherConfig", null, 1);
globalConfigValues.Add(globalConfigValue1);
pidConfigValues.Add(pidConfigValue);
globalConfigValues.Add(globalConfigValue2);
List<Configuration> result = pidConfigValues.Concat(globalConfigValues)
.Distinct(new ConfigurationEqualityComparer()).ToList();
Console.WriteLine(String.Join(",", result));
Console.ReadLine();
}
}
public class Configuration
{
public string _name = "";
public Nullable<int> _pid = null;
public int _value = -1;
public Configuration(string name, Nullable<int> pid, int value)
{
this._name = name;
this._pid = pid;
this._value = value;
}
public override string ToString()
{
return "Name: " + this._name + " PID:" + this._pid + " Value:" + this._value + Environment.NewLine;
}
}
public class ConfigurationEqualityComparer
: EqualityComparer<Configuration>
{
public override bool Equals(Configuration c1, Configuration c2)
{
if (c1 == null && c2 == null)
return true;
else if (c1 == null || c2 == null)
return false;
else if (c1._name.Equals(c2._name))
{
if (c1._pid == null || c2._pid == null) return true;
else return false;
}
else
return false;
}
public override int GetHashCode(Configuration cnf)
{
int hCode = cnf._value ^ cnf._value;
return hCode.GetHashCode();
}
}
}
Explanation: Concat two lists and get only Distinct values. Equality comparer must be rewrited because we are using objects so we have to define which object is equal to another.
So your case is complicated to be solved by simple union or join operation. But simple enough to be solved by some simple select and concat operations.
What you need is to loop all loaded pidConfigValues and override a specific property with the global configuration, and then create a collection containing all unique configurations. Is that correct?
If so the solution could be like this:
var pidConfigValues = await _database.GetConfigurationValuesForPid(productGroup);
var globalConfigValues = await _database.GetGlobalConfigurationValues();
// loop through all pidConfigs and override their Pid value if matching global config exists
var allConfigs = pidConfigValues.Select(c =>
{
var matchingGlobalConfig = globalConfigValues.FirstOrDefault(g => g.Name == c.Name);
if (matchingGlobalConfig != null)
{
c.Pid = matchingGlobalConfig.Pid;
}
return c;
}).ToList();
// Find all global configs that are not matching any pidConfigValues
var productNames = pidConfigValues.Select(p => p.Name).ToArray();
var nonMatchingGlobalConfigs = globalConfigValues.Where(g => !productNames.Contains(g.Name)).ToArray();
// add non-matching global-configs to all-configs collection
allConfigs = allConfigs.Concat(nonMatchingGlobalConfigs).ToArray();

Clean way to check if all properties, except for two, matches between two objects? [duplicate]

This question already has answers here:
Comparing object properties in c# [closed]
(20 answers)
Closed 4 years ago.
I have a database containing components with about 20 properties. To find out if an update is needed I want to check if all properties for the two objects, except DateCreated and Id, matches.
If all matches no update, if not, update db.
Component comp_InApp = new Component()
{
Id = null,
Description = "Commponent",
Price = 100,
DateCreated = "2019-01-30",
// Twenty more prop
};
Component comp_InDb = new Component()
{
Id = 1,
Description = "Component",
Price = 100,
DateCreated = "2019-01-01",
// Twenty more prop
};
// Check if all properties match, except DateCreated and Id.
if (comp_InApp.Description == comp_InDb.Description &&
comp_InApp.Price == comp_InDb.Price
// Twenty more prop
)
{
// Everything up to date.
}
else
{
// Update db.
}
This works, but it's not a very clean way with 20 properties. Is there a better way of achieiving the same result in a cleaner way?
I am using DeepEqual when I don't want/don't have the time to write myself Equals and GetHashCode methods.
You can install it simply from NuGet with:
Install-Package DeepEqual
and use it like:
if (comp_InApp.IsDeepEqual(comp_InDb))
{
// Everything up to date.
}
else
{
// Update db.
}
But keep in mind that this will only work for your case when you want to explicitly compare objects, but not for the case when you want to remove an object form a List or cases like this, when Equals and GetHashCode are invoked.
One way, create a class that implements IEqualityComparer<Component> to encapsulate this logic and to avoid that you have modify the class Comparer itself(if you don't want this Equals logic all time). Then you can use it for a simple Equals of two instances of Component and even for all LINQ methods that accepts it as additional argument.
class ComponentComparer : IEqualityComparer<Component>
{
public bool Equals(Component x, Component y)
{
if (object.ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Price == y.Price && x.Description == y.Description;
}
public int GetHashCode(Component obj)
{
unchecked
{
int hash = 17;
hash = hash * 23 + obj.Price.GetHashCode();
hash = hash * 23 + obj.Description?.GetHashCode() ?? 0;
// ...
return hash;
}
}
}
Your simple use-case:
var comparer = new ComponentComparer();
bool equal = comparer.Equals(comp_InApp, comp_InDb);
It works also if you have two collections and want to know the difference, for example:
IEnumerable<Component> missingInDb = inAppList.Except( inDbList, comparer );
Here is a solution with Reflection:
static bool AreTwoEqual(Component inApp, Component inDb)
{
string[] propertiesToExclude = new string[] { "DateCreated", "Id" };
PropertyInfo[] propertyInfos = typeof(Component).GetProperties()
.Where(x => !propertiesToExclude.Contains(x.Name))
.ToArray();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
bool areSame = inApp.GetType().GetProperty(propertyInfo.Name).GetValue(inApp, null).Equals(inDb.GetType().GetProperty(propertyInfo.Name).GetValue(inDb, null));
if (!areSame)
{
return false;
}
}
return true;
}
You can use a Reflection but it may slow your application. An alternative way of creating that comparator is to generate it with Linq Expressions. Try this code:
public static Expression<Func<T, T, bool>> CreateAreEqualExpression<T>(params string[] toExclude)
{
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !toExclude.Contains(p.Name))
.ToArray();
var p1 = Expression.Parameter(type, "p1");
var p2 = Expression.Parameter(type, "p2");
Expression body = null;
foreach (var property in props)
{
var pare = Expression.Equal(
Expression.PropertyOrField(p1, property.Name),
Expression.PropertyOrField(p2, property.Name)
);
body = body == null ? pare : Expression.AndAlso(body, pare);
}
if (body == null) // all properties are excluded
body = Expression.Constant(true);
var lambda = Expression.Lambda<Func<T, T, bool>>(body, p1, p2);
return lambda;
}
it will generate an expression that looks like
(Component p1, Component p2) => ((p1.Description == p2.Description) && (p1.Price == p2.Price))
Usage is simple
var comporator = CreateAreEqualExpression<Component>("Id", "DateCreated")
.Compile(); // save compiled comparator somewhere to use it again later
var areEqual = comporator(comp_InApp, comp_InDb);
EDIT: to make it more type safe you can exclude properties using lambdas
public static Expression<Func<T, T, bool>> CreateAreEqualExpression<T>(
params Expression<Func<T, object>>[] toExclude)
{
var exclude = toExclude
.Select(e =>
{
// for properties that is value types (int, DateTime and so on)
var name = ((e.Body as UnaryExpression)?.Operand as MemberExpression)?.Member.Name;
if (name != null)
return name;
// for properties that is reference type
return (e.Body as MemberExpression)?.Member.Name;
})
.Where(n => n != null)
.Distinct()
.ToArray();
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !exclude.Contains(p.Name))
.ToArray();
/* rest of code is unchanged */
}
Now when using it we have an IntelliSense support:
var comparator = CreateAreEqualExpression<Component>(
c => c.Id,
c => c.DateCreated)
.Compile();

Different group by parameters according to different conditions in c#

I have a class Person having properties as PersonId, PersonAdress, PersonSalary
I have three different cases when I have to apply group by which is
1. When only PersonAdress is null
2. When only PersonSalary is null
3. When both PersonAdress and PersonSalary are null.
PersonId cannot be null.
All these info is in the list List personList.
Can someone help me here?
You have to define an anonymous lambda to define the grouping. But you can also use an existing method.
List<Person> personList = ...;
var agg = personList.GroupBy(Aggregator);
foreach (var gr in agg) {
Console.WriteLine("Group {0} has {1} persons", gr.Key, gr.Count());
}
private static int Aggregator(Person p) {
if (p.PersonAddress == null && p.PersonSalary == null) {
return 3;
} else if (p.PersonSalary == null) {
return 2;
}
return 1;
}

Get new and existing items between lists

First we pull in new alerts and deserialize them. Now I only care about 2 properties that need to be compared: CommandID and AlertID, all others can be ignored so I create a new object which I assumed would have been easier to compare the results. All other properties become null.
List<AlertModel> alerts = JsonConvert.DeserializeObject<List<AlertModel>>(json)
.Select(x => new AlertModel() { CommandID = x.CommandID, AlertID = x.AlertID }).ToList();
Now I want to find new alerts that don't already exist
List<AlertModel> newAlerts = alerts.Except(currentAlerts).ToList();
Next we pull what alerts already exist.
List<AlertModel> existingAlerts = currentAlerts.Intersect(alerts).ToList();
Now we store new and existing alerts.
currentAlerts.Clear();
currentAlerts.AddRange(newAlerts);
currentAlerts.AddRange(existingAlerts);
1st run alerts contains 1 item newAlerts contains 1 item and existingAlerts contains 0 as they should.
2nd run through isn't what I was expecting.
alerts contains 1 as it should.
newAlerts contains 1 and this should be 0. currentAlerts contains the exact same CommandID and AlertID as in alerts
existingAlerts contains 0 and this should be 1 since the same CommandID and AlertID exists in currentAlerts and alerts.
Not sure what i'm missing here and maybe there is a better way to do this.
Replace this code:
List<AlertModel> newAlerts = alerts.Except(currentAlerts).ToList();
Whit this:
List<AlertModel> newAlerts = alerts.Where(x => !currentAlerts.Any(y => y.CommandID == x.CommandID && y.AlertID == x.AlertID)).ToList();
The issue is that your alerts list contains new elements (new AlertModel() { CommandID = x.CommandID, AlertID = x.AlertID }). This is a reference problem.
Animal a = new Animal { Color = "Red" };
Animal b = new Animal { Color = "Red" };
a == b; // This returns false
Alternatively you can override Equals method in you class. To do this in your class:
public class AlertModel {
// Some things
public override bool Equals(object model) {
return model != null && CommandID == model.CommandId && AlertID == model.AlertID;
}
}
Override Equals and GetHashCode in your AlertModel class. Return a constant value in GetHashCode() (e.g. -1) if you want to force to call your Equals method.
public override bool Equals(object obj)
{
var that = obj as AlertModel;
return that != null && that.AlertId == this.AlertId && that.CommandId == this.CommandId;
}
public override int GetHashCode()
{
int hash = 13;
return (this.AlertId.GetHashCode() * this.CommandID.GetHashCode()) ^ hash;
}
var uniqueAlerts = alerts.Where(a=> !currentAlerts.Any(c=> c.CommandID == a.CommandID && c.AlertID== a.AlertID));

LINQ, SelectMany with multiple possible outcomes

I have a situation where I have lists of objects that have to be merged. Each object in the list will have a property that explains how it should be treated in the merger. So assume the following..
enum Cascade {
Full,
Unique,
Right,
Left
}
class Note {
int Id { get; set; }
Cascade Cascade { get; set; }
// lots of other data.
}
var list1 = new List<Note>{
new Note {
Id = 1,
Cascade.Full,
// data
},
new Note {
Id = 2,
Cascade.Right,
// data
}
};
var list2 = new List<Note>{
new Note {
Id = 1,
Cascade.Left,
// data
}
};
var list3 = new List<Note>{
new Note {
Id = 1,
Cascade.Unique,
// data similar to list1.Note[0]
}
}
So then, I'll have a method ...
Composite(this IList<IList<Note>> notes){
return new List<Note> {
notes.SelectMany(g => g).Where(g => g.Cascade == Cascade.All).ToList()
// Here is the problem...
.SelectMany(g => g).Where(g => g.Cascade == Cascade.Right)
.Select( // I want to do a _LastOrDefault_ )
// continuing for the other cascades.
}
}
This is where I get lost. I need to do multiple SelectMany statements, but I don't know how to. But this is the expected behavior.
Cascade.Full
The Note will be in the final collection no matter what.
Cascade.Unique
The Note will be in the final collection one time, ignoring any duplicates.
Cascade.Left
The Note will be in the final collection, First instances superseding subsequent instances. (So then, Notes 1, 2, 3 are identical. Note 1 gets pushed through)
Cascade.Right
The Note will be in the final collection, Last instance superseding duplicates. (So Notes 1, 2, 3 are identical. Note 3 gets pushed trough)
I think you should decompose the problem in smaller parts. For example, you can implement the cascade rules for an individual list in a seperate extension method. Here's my untested take at it:
public static IEnumerable<Note> ApplyCascades(this IEnumerable<Note> notes)
{
var uniques = new HashSet<Note>();
Note rightToYield = null;
foreach (var n in notes)
{
bool leftYielded = false;
if (n.Cascade == Cascade.All) yield return n;
if (n.Cascade == Cascade.Left && !leftYielded)
{
yield return n;
leftYielded = true;
}
if (n.Cascade == Cascade.Right)
{
rightToYield = n;
}
if (n.Cascade == Cascade.Unique && !uniques.Contains(n))
{
yield return n;
uniques.Add(n);
}
}
if (rightToYield != null) yield return rightToYield;
}
}
This method would allow to implement the original extension method something like this:
List<Note> Composite(IList<IList<Note>> notes)
{
var result = from list in notes
from note in list.ApplyCascades()
select note;
return result.ToList();
}

Categories