How to compare two generic objects with nested collections - c#

I have been working on a generic method to compare two of the same objects to test wether the values in each one are equal.
I am stuck on trying to compare System.Collection.Generic objects, such as Dictionary, HashSet, LinkedList, Stack, ect...
The one i can compare on is the List collection.
Below is what I have so far. if you can provide a means to compare the generics I'd be greatly appreciative.
~Starts the recursive compare
public static bool FullCompare<T>(this T source, T compareTo, bool ignoreCase)
{
return recursiveCompare(source, compareTo, ignoreCase, true);
}
~The recursive search
private static bool recursiveCompare<T>(T source, T compareTo, bool ignoreCase, bool isEqual)
{
if (source == null || compareTo == null) return isEqual;
if(!isEqual) return isEqual;
Type sourceType = source.GetType();
Type compareToType = compareTo.GetType();
if (sourceType.IsPrimitive || source is string) return Test(source, compareTo, ignoreCase);
var sourceProperties = sourceType.GetProperties();
var compareToProperties = compareToType.GetProperties();
for (var property = 0; property < sourceProperties.Count(); property++ )
{
var sourceProperty = sourceProperties[property];
var compareToProperty = compareToProperties[property];
object sourceValue = sourceProperty.GetValue(source, null);
object compareToValue = compareToProperty.GetValue(compareTo, null);
var sourceElements = sourceValue as IList;
var compareToElements = compareToValue as IList;
if (sourceElements != null)
{
for (var element = 0; element < sourceElements.Count; element++)
{
isEqual = recursiveCompare(sourceElements[element], compareToElements[element], ignoreCase, isEqual);
}
}
else if (typeof(IEnumerable).IsAssignableFrom(sourceProperty.PropertyType))
{
//compare System.Collection.Generic objects
}
else
{
if (sourceProperty.PropertyType.Assembly == sourceType.Assembly)
{
isEqual = recursiveCompare(sourceValue, compareToValue, ignoreCase, isEqual);
}
else
{
isEqual = Test(sourceValue, compareToValue, ignoreCase);
}
}
}
return isEqual;
}
~ The comparison test
private static bool Test<T>(T a, T b, bool ignoreCase)
{
if (a == null && b == null)
{
return true;
}
else if (a == null)
{
return false;
}
else if (b == null)
{
return false;
}
else if (a is string)
{
if (ignoreCase)
{
var aAsString = Convert.ToString(a);
var bAsString = Convert.ToString(b);
return aAsString.EqualsIgnoreCaseAndWhitespace(bAsString);
}
}
return EqualityComparer<T>.Default.Equals(a, b);
}
These are the test cases I am using to test the function
public class A
{
public string TestA { get; set; }
public List<String> ListA { get; set; }
}
public class test
{
public byte? byte1 { get; set; }
//public Dictionary<int, byte> byteDictionary { get; set; }
public HashSet<byte> byteHashSet { get; set; }
public LinkedList<byte> byteLinkedList { get; set; }
public List<byte> byteList { get; set; }
public Queue<byte> byteQueue { get; set; }
public Stack<byte> byteStack { get; set; }
public sbyte? sbyte1 { get; set; }
public short? short1 { get; set; }
public ushort? ushort1 { get; set; }
public int? int1 { get; set; }
public uint? uint1 { get; set; }
public long? long1 { get; set; }
public ulong? ulong1 { get; set; }
public float? float1 { get; set; }
public double? double1 { get; set; }
public char? char1 { get; set; }
public string string1 { get; set; }
public decimal? decimal1 { get; set; }
public bool? bool1 { get; set; }
public DateTime datetime1 { get; set; }
public TimeSpan timespan1 { get; set; }
public string string1Var;
public A objectA { get; set; }
}
var byteAList = new List<byte>();
byteAList.Add(1);
byteAList.Add(2);
var byteBList = new List<byte>();
byteBList.Add(1);
byteBList.Add(2);
//var byteADictionary = new Dictionary<int,byte>();
//byteADictionary.Add(1,1);
//var byteBDictionary = new Dictionary<int,byte>();
//byteBDictionary.Add(1,1);
var dateTimeA = new DateTime(1,1,1,1,1,1);
var dateTimeB = new DateTime(1,1,1,1,1,1);
var timeSpanA = new TimeSpan(1,1,1,1,1);
var timeSpanB = new TimeSpan(1,1,1,1,1);
var aAListAList = new List<string>();
aAListAList.Add("Nel");
aAListAList.Add("Hello");
var aA = new A()
{
TestA = "Jar"
, ListA = aAListAList
};
var aAListBList = new List<string>();
aAListBList.Add("Nel");
aAListBList.Add("Hello");
var aB = new A()
{
TestA = "Jar"
,
ListA = aAListBList
};
var byteHashSetA = new HashSet<byte>();
byteHashSetA.Add(1);
byteHashSetA.Add(2);
var byteHashSetB = new HashSet<byte>();
byteHashSetB.Add(1);
byteHashSetB.Add(2);
var a = new test()
{
byte1 = 1
, byteList = byteAList
//, byteDictionary = byteADictionary
, byteHashSet = byteHashSetA
, sbyte1 = -1
, short1 = -11
, ushort1 = 11
, int1 = -1
, uint1 = 1
, long1 = 1
, ulong1 = 1
, float1 = 1.1F
, double1 = 1.1
//, char1 = 't'
, string1 = "test"
, decimal1 = 1.1M
, bool1 = true
, datetime1 = dateTimeA
, timespan1 = timeSpanA
, string1Var = null
, objectA = aA
};
var a2 = new test()
{
byte1 = 1
,
byteList = byteBList
//,
//byteDictionary = byteBDictionary
,
byteHashSet = byteHashSetB
,
sbyte1 = -1
,
short1 = -11
,
ushort1 = 11
,
int1 = -1
,
uint1 = 1
,
long1 = 1
,
ulong1 = 1
,
float1 = 1.1F
,
double1 = 1.1
,
char1 = 't'
,
string1 = "test"
,
decimal1 = 1.1M
,
bool1 = true
,
datetime1 = dateTimeB
,
timespan1 = timeSpanB
,
string1Var = null
,
objectA = aB
};
var equal = a.FullCompare(a2, true);

I solved the issue by comparing the lengths of the two objects first. then if they are the same then i do a nested loop to compare one list against the other. now this is probably not efficient, but it works.
foreach (var sourceElement in sourceElements) { numOfSourceElements++; }
foreach (var compareToElement in compareToElements) { numOfCompareToElements++; }
if (numOfSourceElements != numOfCompareToElements) isEqual = false;
if (isEqual)
{
foreach (var sourceElement in sourceElements)
{
found = false;
foreach (var compareToElement in compareToElements)
{
if (IsSameAsRecursive(sourceElement, compareToElement, sameAsOptions, isEqual))
{
found = true;
break;
}
}
if (!found) break;
}
isEqual = found;
}

Related

Mapping classes with reflection

I have 2 classes User and UserDto both have a Phones property, all the other properties I can map perfectly.
But the problem occurs with the Phones list of the User class, since being null, I cannot identify what type of list it is.
What I need is to be able to and create a list based on its type using reflection.
var list = new List<PhoneDto>();
list.Add(new PhoneDto { Code = 1, Number = 11111111 });
list.Add(new PhoneDto { Code = 2, Number = 11111112 });
list.Add(new PhoneDto { Code = 3, Number = 11111113 });
list.Add(new PhoneDto { Code = 4, Number = 11111114 });
list.Add(new PhoneDto { Code = 5, Number = 11111115 });
var userDto=new UserDto
{
Age=18,
IsData=true,
CreationDate=DateTime.Now,
Phones =list
};
var myMapper = new MyMapper();
var user= myMapper.Map<User>(userDto);
public class User
{
public string Name { get; set; }
public DateTime CreationDate { get; set; }
public int Age { get; set; }
public bool IsData { get; set; }
public ICollection.Phone. Phones { get; set; }
}
public class UserDto
{
public string Name { get; set; }
public DateTime CreationDate { get; set; }
public int Age { get; set; }
public bool IsData { get; set; }
public List.PhoneDto. Phones { get; set; }
}
namespace CustomMapping;
public class MyMapper
{
public T Map<T>(object sourceObject)
{
if (sourceObject == null) throw new Exception("Source object is null");
var instance = Activator.CreateInstance(typeof(T));
if (instance == null) throw new Exception("Can not create de typeof(T) Instance");
var sourceType = sourceObject.GetType();
var targetType = instance.GetType();
PropertyInfo[] sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] targetProperties = targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var sourceProperty in sourceProperties)
{
if (sourceProperty == null) continue;
var targetProperty = targetProperties.FirstOrDefault(p => p.Name.ToLower() == sourceProperty.Name.ToLower() && p.CanWrite);
if (targetProperty == null) continue;
var sourceValue = sourceProperty?.GetValue(sourceObject, null);
if (sourceProperty?.PropertyType == targetProperty.PropertyType)
{
instance?.GetType()?.GetProperty(sourceProperty.Name)?.SetValue(instance, sourceValue);
continue;
}
var isEnumerable = IsEnumerable(targetProperty);
if (isEnumerable)
{
//---------Here I need to map the lists---------
}
}
return (T)instance;
}
private bool IsEnumerable(PropertyInfo propertyInfo)
{
return (propertyInfo.PropertyType.GetInterfaces().Any(p => p.Name == "IEnumerable`1") && propertyInfo.PropertyType.FullName.IndexOf("System.String") < 0);
}
}
I would appreciate any help.

Map one class data to another class with iteration

I have a C# project and looking for simple solution for map one class object data to list of another class object.
This is my input class
public class RatesInput
{
public string Type1 { get; set; }
public string Break1 { get; set; }
public string Basic1 { get; set; }
public string Rate1 { get; set; }
public string Type2 { get; set; }
public string Break2 { get; set; }
public string Basic2 { get; set; }
public string Rate2 { get; set; }
public string Type3 { get; set; }
public string Break3 { get; set; }
public string Basic3 { get; set; }
public string Rate3 { get; set; }
}
This is my another class structure
public class RateDetail
{
public string RateType { get; set; }
public decimal Break { get; set; }
public decimal Basic { get; set; }
public decimal Rate { get; set; }
}
it has a object like below. (For easiering the understanding, I use hardcoded values and actually values assign from a csv file)
RatesInput objInput = new RatesInput();
objInput.Type1 = "T";
objInput.Break1 = 100;
objInput.Basic1 = 50;
objInput.Rate1 = 0.08;
objInput.Type2 = "T";
objInput.Break2 = 200;
objInput.Basic2 = 50;
objInput.Rate2 = 0.07;
objInput.Type3 = "T";
objInput.Break3 = 500;
objInput.Basic3 = 50;
objInput.Rate3 = 0.06;
Then I need to assign values to "RateDetail" list object like below.
List<RateDetail> lstDetails = new List<RateDetail>();
//START Looping using foreach or any looping mechanism
RateDetail obj = new RateDetail();
obj.RateType = //first iteration this should be assigned objInput.Type1, 2nd iteration objInput.Type2 etc....
obj.Break = //first iteration this should be assigned objInput.Break1 , 2nd iteration objInput.Break2 etc....
obj.Basic = //first iteration this should be assigned objInput.Basic1 , 2nd iteration objInput.Basic2 etc....
obj.Rate = //first iteration this should be assigned objInput.Rate1, 2nd iteration objInput.Rate2 etc....
lstDetails.Add(obj); //Add obj to the list
//END looping
Is there any way to convert "RatesInput" class data to "RateDetail" class like above method in C#? If yes, how to iterate data set?
Try this:
public class RatesList : IEnumerable<RateDetail>
{
public RatesList(IEnumerable<RatesInput> ratesInputList)
{
RatesInputList = ratesInputList;
}
private readonly IEnumerable<RatesInput> RatesInputList;
public IEnumerator<RateDetail> GetEnumerator()
{
foreach (var ratesInput in RatesInputList)
{
yield return new RateDetail
{
RateType = ratesInput.Type1,
Break = Convert.ToDecimal(ratesInput.Break1, new CultureInfo("en-US")),
Basic = Convert.ToDecimal(ratesInput.Basic1, new CultureInfo("en-US")),
Rate = Convert.ToDecimal(ratesInput.Rate1, new CultureInfo("en-US"))
};
yield return new RateDetail
{
RateType = ratesInput.Type2,
Break = Convert.ToDecimal(ratesInput.Break2),
Basic = Convert.ToDecimal(ratesInput.Basic2),
Rate = Convert.ToDecimal(ratesInput.Rate2, new CultureInfo("en-US"))
};
yield return new RateDetail
{
RateType = ratesInput.Type3,
Break = Convert.ToDecimal(ratesInput.Break3),
Basic = Convert.ToDecimal(ratesInput.Basic3),
Rate = Convert.ToDecimal(ratesInput.Rate3, new CultureInfo("en-US"))
};
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
And use:
var list = new RatesList(new List<RatesInput>() { objInput });
foreach (var item in list)
{
Console.WriteLine(item.Basic);
}
You can use Reflection to get the properties info like this:
var props = objInput.GetType().GetProperties();
var types = props.Where(x => x.Name.StartsWith("Type"))
.Select(x => x.GetValue(objInput)).ToList();
var breaks = props.Where(x => x.Name.StartsWith("Break"))
.Select(x => x.GetValue(objInput)).ToList();
var basics = props.Where(x => x.Name.StartsWith("Basic"))
.Select(x => x.GetValue(objInput)).ToList();
var rates = props.Where(x => x.Name.StartsWith("Rate"))
.Select(x => x.GetValue(objInput)).ToList();
List<RateDetail> lstDetails = new List<RateDetail>();
for (int i = 0; i < types.Count; i++)
{
lstDetails.Add(new RateDetail
{
RateType = types[i].ToString(),
Break = Convert.ToDecimal(breaks[i]),
Basic = Convert.ToDecimal(basics[i]),
Rate = Convert.ToDecimal(rates[i])
});
}

return multiple reader.cast<>

All I want to do is return multiple reader.cast<> so that i can use 2 sqlcommands.
var first =reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names = x.GetString(0),
Values = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(1)
}).ToList();
reader.NextResult();
var second=reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names2 = x.GetString(0),
Values2 = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble2 = x.GetDouble(1)
}).ToList();
All I want to do is return var first and var second. Please help :(
I'm using this Location.cs for parameters:
namespace MVCRealtime
{
public class LocationInfo
{
public string Names { get; set; }
public string Values { get; set; }
public double ValuesDouble { get; set; }
public string Names2 { get; set; }
public string Values2 { get; set; }
public double ValuesDouble2 { get; set; }
}
}
public static class ReaderHelper
{
public static IEnumerable<TElem> GetData<TElem>(this IDataReader reader, Func<IDataRecord, TElem> buildObjectDelegat)
{
while (reader.Read())
{
yield return buildObjectDelegat(reader);
}
}
}
// ...
var result = reader.GetData(x => new LocationInfo()
{
Names = x.GetString(0),
Values = Math.Round(x.GetDouble(1), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(1)
}).Take(2);
So you get 1st var in 1st element of the result and 2nd var in 2nd element.

Compare each class attribute value using LINQ

I have a below class. I will get two objects List<Client> data1 and List<Client> data2. I want to compare data1 and data2 with each of the attribute value.
For example, if data1 object has the LastName=a and ClientId=1,..etc and if data2 list has the same set of data i want to push that into one more list.
Any idea, how can we achieve this using LINQ/Minimal code?
public class Client
{
public int ClientId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
}
using Intersect
List<Client> data1 = new List<Client>();
List<Client> data2 = new List<Client>();
List<Client> newlst = new List<Client>();
Client obj = new Client();
obj.ClientId = 1;
obj.LastName = "a";
obj.FirstName = "n";
obj.Email = "e";
data1.Add(obj);
data2.Add(obj);
obj = new Client();
obj.ClientId = 2;
obj.LastName = "a";
obj.FirstName = "f";
obj.Email = "e";
data1.Add(obj);
newlst = data1.Intersect(data2).ToList();
I have used IEqualityComparer which is used to compare both the collection and Intersect will give the common value.I have tested the code for few scenario. You can check for all the scenario.
Hope this code will be helpful.
namespace UnitTestProject
{
[TestClass]
public class CompareTwoGenericList
{
[TestMethod]
public void TestMethod1()
{
var coll = GetCollectionOne();
var col2 = GetCollectionTwo();
//Gives the equal value
var commonValue = coll.Intersect(col2, new DemoComparer()).ToList();
//Difference
var except=coll.Except(col2, new DemoComparer()).ToList();
}
public List<Demo> GetCollectionOne()
{
List<Demo> demoTest = new List<Demo>()
{
new Demo
{
id=1,
color="blue",
},
new Demo
{
id=2,
color="green",
},
new Demo
{
id=3,
color="red",
},
};
return demoTest;
}
public List<Demo> GetCollectionTwo()
{
List<Demo> demoTest = new List<Demo>()
{
new Demo
{
id=1,
color="blue",
},
new Demo
{
id=2,
color="green",
},
new Demo
{
id=4,
color="red",
},
};
return demoTest;
}
}
// Custom comparer for the Demo class
public class DemoComparer : IEqualityComparer<Demo>
{
// Products are equal if their color and id are equal.
public bool Equals(Demo x, Demo y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the demo properties are equal.
return x.color == y.color && x.id == y.id;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Demo demo)
{
//Check whether the object is null
if (Object.ReferenceEquals(demo, null)) return 0;
//Get hash code for the color field if it is not null.
int hashColor = demo.color == null ? 0 : demo.color.GetHashCode();
//Get hash code for the id field.
int hashId = demo.id.GetHashCode();
//Calculate the hash code for the product.
return hashColor ^ hashId;
}
}
}
Create Your new class ClientView
public class ClientView{
public int ClientId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
}
List lst = new List();
var data = from n in db.client
select new ClientView()
{
ClientId = n.ClientId ,
LastName = n.LastName ,
FirstName = n.FirstName,
};
var data1 = from n in db.client
select new ClientView()
{
ClientId = n.ClientId ,
LastName = n.LastName ,
FirstName = n.FirstName,
};
lst.AddRange(data);
lst.AddRange(data1);
List<ClientView> lst1 = new List<ClientView>();
foreach (var singlelst in lst)
{
ClientView newClient = new ClientView ();
newClient.Id = singlelst.Id;
newClient.આપેલ = singlelst.LastName;
newClient.આપેલતારીખ = singlelst.FirstName;
lst1.Add(newClient);
}
Try this:
public IEnumerable<PropertyInfo> GetVariance(Client user)
{
foreach (PropertyInfo pi in user.GetType().GetProperties()) {
object valueUser = typeof(Client).GetProperty (pi.Name).GetValue (user);
object valueThis = typeof(Client).GetProperty (pi.Name).GetValue (this);
if (valueUser != null && !valueUser.Equals(valueThis))
yield return pi;
}
}
IEnumerable<PropertyInfo> variances = data1.GetVariance (data2);
foreach (PropertyInfo pi in variances)
Console.WriteLine (pi.Name);

How store From-To value and result in an array

I want to store some items with fields "fromValue", "ToValue" , "Info" in an array and write a routine to search an input "value" between "FromValue" & "ToValue" and return "Info" field. I need fast searchable container.
FromValue,ToValue,Info
10,20,TX
24,56,NY
input =34 returns NY
Thanks
ok simple, this class defines your generic range.
public class Range<TValue, TInfo>
{
private readonly IComparer<TValue> comparer;
public Range(IComparer<TValue> comparer)
{
this.comparer = comparer;
}
public Range(IComparer<TValue> comparer)
: this(Comparer<TValue>.Default)
{
}
public TValue From { get; set; }
public TValue To { get; set; }
public TInfo Info { get; set; }
public bool InRange(T value, bool inclusive = true)
{
var lowerBound = this.comparer.Compare(value, this.From);
if (lowerBound < 0)
{
return false;
}
else if (!inclusive && lowerBound == 0)
{
return false;
}
var upperBound = this.comparer.Compare(value, this.To);
if (upperBound > 0)
{
return false;
}
else if (!inclusive && upperBound == 0)
{
return false;
}
return true;
}
}
So, you can have a sequence of ranges,
IEnumerable<Range<int, string>> ranges = ...
To find all the info values in range you can do,
var rangesInRange = ranges.Where(r => r.InRange(42)).Select(r => r.Info);
You could make a specialised container to improve this operation.
Class:
public class Information
{
public int FromValue { get; set; }
public int ToValue { get; set; }
public string Info { get; set; }
}
Search:
List<Information> Informations = new List<Information>();
Information infoObj = new Information();
infoObj.FromValue = 10;
infoObj.ToValue = 20;
infoObj.Info = "TX";
Informations.Add(infoObj);
Information infoObj2 = new Information();
infoObj2.FromValue = 24;
infoObj2.ToValue = 56;
infoObj2.Info = "NY";
Informations.Add(infoObj);
//passing sample input which lies between fromvalue and tovalue
int sampleInput = 15;
var result = Informations.FirstOrDefault(x => x.FromValue < sampleInput && sampleInput < x.ToValue);
This is pretty straight forward.
In the most simple scenario just create a class Item
public class Item
{
public int Id { get; set; }
public int FromValue { get; set; }
public int ToValue { get; set; }
public string Info { get; set; }
}
With this you can initialize your collection of type List<T>
List<Item> Items = new List<Item>()
{
new Item() {Id = 1, FromValue = 10, ToValue = 20, Info = "TX"}
new Item() {Id = 2, FromValue = 24, ToValue = 56, Info = "NY"}
new Item() {Id = 3, FromValue = 15, ToValue = 34, Info = "FL"}
};
And with this you can query it to your hearts content.
var itemsFromFlorida = Items.Where(it => it.Info == "FL");

Categories

Resources