Union of Two Objects Based on Equality of Their Members - c#

Let us start with a class definition for the sake of example:
public class Person
{
public string FirstName;
public string LastName;
public int Age;
public int Grade;
}
Now let's assume I have a List<Person> called people containing 3 objects:
{"Robby", "Goki", 12, 8}
{"Bobby", "Goki", 10, 8}
{"Sobby", "Goki", 10, 8}
What I am looking for is some way to retrieve the following single Person object:
{null, "Goki", -1, 8}
where fields which are the same in all objects retain their value while fields which have multiple values are replaced with some invalid value.
My first thought consisted of:
Person unionMan = new Person();
if (people.Select(p => p.FirstName).Distinct().Count() == 1)
unionMan.FirstName = people[0].FirstName;
if (people.Select(p => p.LastName).Distinct().Count() == 1)
unionMan.LastName = people[0].LastName;
if (people.Select(p => p.Age).Distinct().Count() == 1)
unionMan.Age = people[0].Age;
if (people.Select(p => p.Grade).Distinct().Count() == 1)
unionMan.Grade = people[0].Grade;
Unfortunately, the real business object has many more members than four and this is both tedious to write and overwhelming for someone else to see for the first time.
I also considered somehow making use of reflection to put these repetitive checks and assignments in a loop:
string[] members = new string[] { "FirstName", "LastName", "Age", "Grade" };
foreach (string member in members)
{
if (people.Select(p => p.**member**).Distinct().Count() == 1)
unionMan.**member** = people[0].**member**;
}
where **member** would be however reflection would allow the retrieval and storage of that particular member (assuming it is possible).
While the first solution would work, and the second I am assuming would work, does anyone have a better alternative solution to this problem? If not, would using reflection as described above be feasible?

It's inefficient to do a distinct of all the values just to count the distinct members. You have a shortcut scenario wherein finding one value in any of the subsequent items that does not have the same value as the first item's member means that you have an invalid state for that column.
Something like this should work, though more work would need to be done if any of the members are arrays, need recursive evaluation or other more complex logic (note I have not tested this):
public static T UnionCombine<T>(this IEnumerable<T> values) where T : new() {
var newItem = new T();
var properties = typeof(T).GetProperties();
for (var prop in properties) {
var pValueFirst = prop.GetValue(values.First(), null);
var useDefaultValue = values.Skip(1).Any(v=>!(Object.Equals(pValueFirst, prop.GetValue(v, null))));
if (!useDefaultValue) prop.SetValue(newItem, pValueFirst, null);
}
return newItem;
}

Your last idea seems good to me, something like this:
List<Person> persons = new List<Person>()
{
new Person(){ FirstName="Robby", LastName="Goki", Age=12, Grade=8},
new Person(){ FirstName="Bobby", LastName="Goki", Age=10, Grade=8},
new Person(){ FirstName="Sobby", LastName="Goki", Age=10, Grade=8},
};
var properties = typeof(Person).GetProperties();
var unionMan = new Person();
foreach (var propertyInfo in properties)
{
var values = persons.Select(x => propertyInfo.GetValue(x, null)).Distinct();
if (values.Count() == 1)
propertyInfo.SetValue(unionMan, propertyInfo.GetValue(persons.First(), null), null);
}
A couple of observations:
your class members should be defined as properties and not public members
and both get and set accessor must be public
the default constructor should define the "invalid" values (as correctly suggested by #RaphaƫlAlthaus)
so, the class Person would look like this:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int Grade { get; set; }
public Person()
{
this.FirstName = null;
this.LastName = null;
this.Age = -1;
this.Grade = -1;
}
}

Update: Since you don't have control over the Person class, and state is defined in public fields, rather than properties, I have updated the solution to address this.
I would recommend using reflection. You would want to get the FieldInfo (or PropertyInfo) object ahead of time, rather than getting it for each entry in in your LINQ query. You can get them by using Type.GetField and Type.GetProperty. Once you have those, you can simply use FieldInfo/PropertyInfo.GetValue and FieldInfo/PropertyInfo.SetValue.
For example:
Type personType = typeof(Person);
foreach(string member in members)
{ // Get Fields via Reflection
FieldInfo field = peopleType.GetField(member);
if(field != null)
{
if (people.Select(p => field.GetValue(p, null) ).Distinct().Count() == 1)
{
field.SetValue(unionMan, field.GetValue(people[0], null), null);
}
}
else // If member is not a field, check if it's a property instead
{ // Get Properties via Reflection
PropertyInfo prop = peopleType.GetProperty(member);
if(prop != null)
{
if (people.Select(p => prop.GetValue(p, null) ).Distinct().Count() == 1)
{
prop.SetValue(unionMan, prop.GetValue(people[0], null), null);
}
}
}
}
As you pointed out, you are already setting the "invalid" vlaues in the default constructor, so you shouldn't have to worry about them inside this loop.
Note: In my example, I used the versions of GetField and GetProperties that do not take a BindingFlags parameter. These will only return public members.

Related

Add text to all properties in all items of a list without manual multiple loops

I have a List of MyClass
public class MyClass
{
public string Title { get; set; }
public string Name { get; set; }
}
var records = new List<MyClass>
{
new MyClass { Title = "Mr", Name = "Bob" },
new MyClass { Title = "Dr", Name = "Jon" },
};
I need to add "" around each value in each property of the class, and each item in the list. So the actual content of each property contains a " at the start and end.
Is there a way I can add this without having to loop through each property & item and do it all manually. A way using Linq perhaps?
I can't find anything on Google, I'm not even really sure what to search for.
Thanks
Yes, simply map the transformation:
var transformedRecords =
records.Select(c =>
new MyClass
{
Title = $"\"{c.Title}\"",
Name = $"\"{c.Name}\""
});
Now, if what you want is a way to edit all string properties without having to manually change them one by one, then the only way is with reflection:
static void Transform<T>(T t)
where T: class
{
if (ReferenceEquals(t, null))
throw new ArgumentNullException(nameof(t));
var propertiesToEdit =
typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(string)
p.CanWrite &&
p.CanRead);
foreach (var p in propertiesToEdit)
{
p.SetValue(t, $"\"{p.GetValue(t)}\"");
}
}
But now you've got a method that mutates the object that is passed into it. You should never use mutating methods with LINQ, unexpected behaviors are to be expected.
So you are better of just simply iterating manually:
foreach (var r in records)
Transform(r);
And now your records list will contain the mutated items.
Of course, you don't need to stop at string, you can make this quite a bit more general with very little additional cost:
static void Transform<T, Q>(T t, Func<Q, Q> transform)
where T: class
{
if (ReferenceEquals(t, null))
throw new ArgumentNullException(nameof(t));
var propertiesToEdit =
typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(Q)
p.CanWrite &&
p.CanRead);
foreach (var p in propertiesToEdit)
{
p.SetValue(t, transform((Q)p.GetValue(t)));
}
}
And now you'd use it like this:
foreach (var r in records)
Transform(r, (string s) => $"\"{s}\"");
The class constraint is important here because this will fail with value types; c# defaults to pass by value semantics so the method will mutate copies, not the original instances in the list.
Without using a loop I believe this is what you seek:
records.ForEach(e =>
{
e.Name = $"\"{e.Name}\"";
e.Title = $"\"{e.Title}\"";
});

Convert List to typeof UnderlyingSystemType

I am currently working on code that is using dynamic-linq, I ran into a problem when using a List<BaseClass>, where the list actually contains a list of the Person Class.
When I execute the following code I get a ParseException:
var list = new List<BaseClass>();
list.Add(new Person
{
FirstName = "Joe",
Surname = "Bloggs"
});
list.Where("FirstName == #0", "Joe");
And the Exception:
Please see BaseClass below:
public class BaseClass
{
public int Id { get; set; }
}
And the Person class:
public class Person : BaseClass
{
public string FirstName { get; set; }
public string Surname { get; set; }
}
I can overcome the error by implementing the following code:
var list = new List<BaseClass>();
list.Add(new Person
{
FirstName = "Joe",
Surname = "Bloggs"
});
var newList = CreateListOfCorrectType<BaseClass>(list);
newList.Where("FirstName == #0", "Joe");
Please see CreateListOfCorrectType<T> method below:
private IList CreateListOfCorrectType<T>(
List<T> list)
{
if (list.Count == 0)
{
return list;
}
var typeInfo = list.FirstOrDefault().GetType();
var correctListType = typeof(List<>).MakeGenericType(typeInfo.UnderlyingSystemType);
var listOfCorrectType = (Activator.CreateInstance(correctListType)) as IList;
list.ForEach(x => listOfCorrectType.Add(x));
return listOfCorrectType;
}
My Question is if using the CreateListOfCorrectType is the best way of overcoming the issue? and if not what alternatives do I have in getting the List<BaseClass> to the correct Type.
I am looking to use this with existing code, and changing the existing List<>types are not possible. And the CreateListOfCorrectType method is not aware of the Person class.
Please note that class names and variables are for demonstrative purposes only.
UPDATE
Optimist's answer below leaded me to the solution for my issue, please see extension method used below:
public static IList ToDerivedListType(this IList list)
{
if (list == null || list.Count == 0)
{
return list;
}
var type = list.Cast<object>().FirstOrDefault().GetType();
var castedList = typeof(Enumerable).GetMethod("Cast", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
.MakeGenericMethod(type)
.Invoke(null, new[] { list });
return typeof(Enumerable).GetMethod("ToList", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
.MakeGenericMethod(type)
.Invoke(null, new[] { castedList }) as IList;
}
System.Linq.Enumerable.Cast and MakeGenericMethod was the key.
How about using the OfType linq method:
list.OfType<Person>().Where("FirstName == #0", "Joe");
See https://msdn.microsoft.com/en-us/library/vstudio/bb360913(v=vs.100).aspx
depending on the actual use cases, some things can be improved:
runtime expenses: the CreateListOfCorrectType function copies all elements into a new collection which results in unnecessary expense in case only a subset is taken out of the returned collection .
scope of applicability of the mapping function: this could be widened to work with IEnumerable.
finally, both the mapping and the filtering function can be combined into one to reduce the amount of code in consuming functions.
System.Linq.Enumerable.Cast and MakeGenericMethod can be employed to achieve that:
static public class Extension
{
static public IEnumerable CastDynamic(this IEnumerable Source, Type Type)
{
return
(IEnumerable)
typeof(Enumerable)
.GetMethod("Cast", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
.MakeGenericMethod(Type)
.Invoke(null, new[] { Source });
}
static public IEnumerable CastToFirstType(this IEnumerable Source)
{
if (0 == Source.Take(1).Count())
{
return Source;
}
return CastDynamic(Source, Source.Cast<object>().FirstOrDefault().GetType());
}
static public IEnumerable WhereCastToFirstType(this IEnumerable Source, string Predicate, params object[] values)
{
return Source.CastToFirstType().Where(Predicate, values);
}
}
If tried to mimic the function you showed in terms of exceptions thrown. There is a difference because the casting is done after the mapping function returns.

Filtering IEnumerable by collection (Linq)

I want to filter an IEnumerable object by a specific property of whatever object it is collecting. I want the option to filter by one or more property value but how many values (and what values) to filter by is only known at runtime.
Ok, so to give an example, the collected objects could be the following struct:
public struct Person
{
public string Name { get; set; }
public string Profession{ get; set; }
}
This struct could then be used by the following list, which I have populated with some arbitrary values:
List<Person> people= new List<Person>;
people.Add(new Person(){Name = "Mickey", Profession="Tinker"};
people.Add(new Person(){Name = "Donald", Profession="Tailor"};
people.Add(new Person(){Name = "Goofy", Profession="Soldier"};
people.Add(new Person(){Name = "Pluto", Profession="Spy"};
This is then put into an IEnumerable (all of them are transferred to it first)
var wantedPeople = from n in this.people select n;
So say a user was only interested in the "Tailor" and "Spy" professions, and via some sort of gui trickery the following collection was created:
List<string> wantedProfessions = new List<string>();
wantedProfessions.Add("Tailor");
wantedProfessions.Add("Spy");
Now what Linq statement can I use to filer my wantedPeople so it only includes the tailor and spy entries?
I know I could use a where clause but I don't know how to tailor it to get what I want (and doing the following is not what I want as it only works with the wantedProfessions collection above (e.g. this collection will change at runtime):
wantedPeople = from n in wantedPeople
where n.Profession == wantedProffessions[0] || n.Profession == wantedProffessions[1]
select n;
If you want to check any wanted profession from given list:
wantedPeople = from n in wantedPeople
where wantedProffessions.Contains(n.Profession)
select n;
Or you can build query with lambda syntax by applying filters one by one:
var query = people.AsEnumerable();
if (!String.IsNullOrEmpty(name))
query = query.Where(p => p.Name == name);
if (wantedProfessions.Any())
query = query.Where(p => wantedProfessions.Contains(p.Profession));
If you wanted to create more complex filters, like some name, and several professions, you can use Specification pattern. Specification can be defined by this simple interface:
public interface ISpecification<T>
{
bool Satisfied(T entity);
}
It just checks whether given entity (person) satisfies specification. Specification also look very simple:
public class PersonNameSpecification : ISpecification<Person>
{
private string _name;
public PersonNameSpecification(string name)
{
_name = name;
}
public bool Satisfied(Person person)
{
return person.Name == _name;
}
}
Profession specification:
public class PersonProfessionSpecification : ISpecification<Person>
{
private string[] _professions;
public PersonProfessionSpecification(params string[] professions)
{
_professions = professions;
}
public bool Satisfied(Person person)
{
return _professions.Contains(person.Profession);
}
}
You can create specifications which implement boolean logic, like OrSpecification or AndSpecification:
public class AndSpecification<T> : ISpecification<T>
{
private ISpecification<T> _specA;
private ISpecification<T> _specB;
public AndSpecification(ISpecification<T> specA, ISpecification<T> specB)
{
_specA = specA;
_specB = specB;
}
public bool Satisfied(T entity)
{
return _specA.Satisfied(entity) && _specB.Satisfied(entity);
}
}
public static class SpecificationExtensions
{
public static ISpecification<T> And<T>(
this ISpecification<T> specA, ISpecification<T> specB)
{
return new AndSpecification<T>(specA, specB);
}
}
Now you can create complex specification which describes people you want to get:
var professionSpec = new PersonProfessionSpecification("Tailor", "Spy");
var nameSpec = new PersonNameSpecification("Pluto");
var spec = professionSpec.And(nameSpec);
And get required people:
var result = people.Where(spec.Satisfied);
Sergey B's Solution is the correct one for your example.
Assuming that you weren't using a collection that had the Contains() method, you could also do the following:
var wantedPeople = from n in people
from p in wantedProffessions
where n.Profession.Equals(p)
select n;

How to access properties by name?

This is a simple example on how I update a value in the database:
var context = new dbEntities();
var car = context.CarTable.Where(p => p.id == id).FirstOrDefault();
car.Make = "Volvo";
context.SaveChanges();
However, what I need to do now is to get the property by name instead. So this is what I in theory would like to do:
var context = new dbEntities();
var car = context.CarTable.Where(p => p.id == id).FirstOrDefault();
**car["Make"] = "Volvo";**
context.SaveChanges();
Is this possible in EF?
I wouldn't use reflection since that would be slow.
You can use expression trees, especially if you cache the expressions. Check this link for an article about it. I would write a wrapper around the code in the article which takes an object and a propertyname (string), creates/caches the func using the code in the article (or retrieves it from the cache), and executes the func.
The main question is really why do you need this?
The best way is still car.Make = "Volvo";.
If string-name is strongly needed, you can use Reflection:
var property = typeof (Car).GetProperty("Make");
property.SetValue(car, "BMW", null);
Here are 2 drawbacks:
Slow.
Compiler cannot check the string.
The other way - you can use indexer and switch:
public class Car
{
public string Make { get; set; }
public string this[String name]
{
set
{
switch (name)
{
case "Make":
Make = value;
break;
...
}
}
}
}
And then just car["Make"] = "Volvo";
It's faster, but a typ-problem occurs: you have to parse strings or operate with objects.
public class Car
{
public string Make { get; set; }
public object this[string name]
{
get
{
var property = this.GetType().GetProperties().FirstOrDefault(p => p.Name.Equals(name));
if (property != null)
{
return property.GetValue(this, null);
}
return null;
}
set
{
var property = this.GetType().GetProperties().FirstOrDefault(p => p.Name.Equals(name));
if (property != null)
{
property.SetValue(this, value, null);
}
}
}
}

Merging two objects in C#

I have an object model MyObject with various properties. At one point, I have two instances of these MyObject: instance A and instance B. I'd like to copy and replace the properties in instance A with those of instance B if instance B has non-null values.
If I only had 1 class with 3 properties, no problem, I could easily hard code it (which is what I started doing). But I actually have 12 different object models with about 10 properties each.
What's good way to do this?
Update
Use AutoMapper instead if you need to invoke this method a lot. Automapper builds dynamic methods using Reflection.Emit and will be much faster than reflection.'
You could copy the values of the properties using reflection:
public void CopyValues<T>(T target, T source)
{
Type t = typeof(T);
var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value != null)
prop.SetValue(target, value, null);
}
}
I've made it generic to ensure type safety. If you want to include private properties you should use an override of Type.GetProperties(), specifying binding flags.
I have tried what's described by Merge Two Objects into an Anonymous Type by Kyle Finley and it is working perfectly.
With the TypeMerger, the merging is as simple as
var obj1 = new {foo = "foo"};
var obj2 = new {bar = "bar"};
var mergedObject = TypeMerger.MergeTypes(obj1 , obj2 );
That's it! You've got the merged object. Apart from that, there is a provision to ignore specific properties too. You can use the same thing for MVC3 as well.
You can do it using reflection, but as someone stated, it'll have a performance penalty.
Since you're working with an expected class design, you can achieve the same goal by using an extension method like so:
public static class MyClassExtensions
{
public static void Merge(this MyClass instanceA, MyClass instanceB)
{
if(instanceA != null && instanceB != null)
{
if(instanceB.Prop1 != null)
{
instanceA.Prop1 = instanceB.Prop1;
}
if(instanceB.PropN != null)
{
instanceA.PropN = instanceB.PropN;
}
}
}
And later, somewhere in your code:
someInstanceOfMyClass.Merge(someOtherInstanceOfMyClass);
At the end of the day you've centralized this operation in an extension method and if you add or remove a property of your class, you only need to modify extension method's implementation and you'll get everything done.
you can use this package:XASoft
use foo.Merger(bar) method
to combine 2 object to a dynamic object
just like this
var objA = new { a = 1, b = 2, c = 3 };
var newObj = objA.Merger(new { a = "Hey", d = 4, e = 5 });
newObj.e = "There";
newObj.f = 6;
Console.WriteLine(JsonConvert.SerializeObject(newObj));
even if object list works fine too!
private class TestClass
{
public int X { get; set; }
public int Y { get; set; }
}
static void Main(string[] args)
{
var list = new List<TestClass> {
new TestClass{ X=1,Y=2},
new TestClass{ X=3,Y=4}
};
Console.WriteLine(JsonConvert.SerializeObject(list.ListMerger(i => new
{ X = "null value", Z = i.X == 1 ? 0 : 1 })));
}
oops,looks like javascript language...
btw,source code click here
I've created a method similar to some answers above, but it returns a new object merged between 2 objects, and will only merge the second objects value if the first objects value is null.
public T MergeObjects<T>(T primary, T secondary)
{
T obj = (T)Activator.CreateInstance(typeof(T));
Type t = typeof(T);
var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(primary, null);
if (value != null)
prop.SetValue(obj, value, null);
else
{
value = prop.GetValue(secondary, null);
if (value != null)
prop.SetValue(obj, value, null);
}
}
return obj;
}
I've written my own class for this purpose: ObjectMerger.
Basically it uses reflections (and may be slow because of that). It also contains more features e.g. parsing objects for cyclic references and merge them too. My ObjectMerger also contains mechanism to handle more complex classes like Delegate or MemberInfo. Those will be copied completely, other objects in the class are recursively merged.
The Syntax is like:
var initialInstance = new MyObjectBase(); // Initialize first object
var properInstance = new MyObjectWithAlgorithms(); // Initialize second object
var result = ObjectMerger.MergeObjects(properInstance, initialInstance); // Merge Objects into type of "properInstance"
I'm sorry to say that it is NOT FOR USE AS IS, because some external libraries are missing in the repository at the moment due to limitations in my company, but they can easily be rewritten. I hope a can add them in future.
This is the same as #Bas Answer but for Merging 2 Object lists
public class Copycontents
{
public static void Work<T>(IList<T> targetList, IList<T> sourceList, Func<T, int> selector)
{
var matchingPrimaryKey = targetList.Select(x => selector(x)).ToList();
foreach (var thismatchingPrimaryKey in matchingPrimaryKey)
{
CopyValues<T>(targetList.Single(x => selector(x) == thismatchingPrimaryKey),
sourceList.Single(x => selector(x) == thismatchingPrimaryKey));
}
}
private static void CopyValues<T>(T target, T source)
{
Type t = typeof(T);
var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value != null)
prop.SetValue(target, value, null);
}
}
}

Categories

Resources