Uppercase a List of object with LINQ - c#

I have the code below. I'd like to convert all items in this list to uppercase.
Is there a way to do this in Linq ?
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public class MyClass
{
List<Person> myList = new List<Person>{
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
}
Update
I don't want to loop or go field by field. Is there a way by reflection to uppercase the value for each property?

Why would you like to use LINQ?
Use List<T>.ForEach:
myList.ForEach(z =>
{
z.FirstName = z.FirstName.ToUpper();
z.LastName = z.LastName.ToUpper();
});
EDIT: no idea why you want to do this by reflection (I wouldn't do this personally...), but here's some code that'll uppercase all properties that return a string. Do note that it's far from being perfect, but it's a base for you in case you really want to use reflection...:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public static class MyHelper
{
public static void UppercaseClassFields<T>(T theInstance)
{
if (theInstance == null)
{
throw new ArgumentNullException();
}
foreach (var property in theInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var theValue = property.GetValue(theInstance, null);
if (theValue is string)
{
property.SetValue(theInstance, ((string)theValue).ToUpper(), null);
}
}
}
public static void UppercaseClassFields<T>(IEnumerable<T> theInstance)
{
if (theInstance == null)
{
throw new ArgumentNullException();
}
foreach (var theItem in theInstance)
{
UppercaseClassFields(theItem);
}
}
}
public class Program
{
private static void Main(string[] args)
{
List<Person> myList = new List<Person>{
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
MyHelper.UppercaseClassFields<Person>(myList);
Console.ReadLine();
}
}

LINQ does not provide any facilities to update underlying data. Using LINQ, you can create a new list from an existing one:
// I would say this is overkill since creates a new object instances and
// does ToList()
var updatedItems = myList.Select(p => new Person
{
FirstName = p.FirstName.ToUpper(),
LastName = p.LastName.ToUpper(),
Age = p.Age
})
.ToList();
If using LINQ is not principal, I would suggest using a foreach loop.
UPDATE:
Why you need such solution? Only one way of doing this in generic manner - reflection.

the Easiest approach will be to use ConvertAll:
myList = myList.ConvertAll(d => d.ToUpper());
Not too much different than ForEach loops the original list whereas ConvertAll creates a new one which you need to reassign.
var people = new List<Person> {
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
people = people.ConvertAll(m => new Person
{
FirstName = m.FirstName?.ToUpper(),
LastName = m.LastName?.ToUpper(),
Age = m.Age
});
to answer your update
I don't want to loop or go field by field. Is there a way by
reflection to uppercase the value for each property?
if you don't want to loop or go field by field.
you could use property on the class to give you the Uppercase like so
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string FirstNameUpperCase => FirstName.ToUpper();
public string LastNameUpperCase => LastName.ToUpper();
}
or you could use back field like so
public class Person
{
private string _firstName;
public string FirstName {
get => _firstName.ToUpper();
set => _firstName = value;
}
private string _lastName;
public string LastName {
get => _lastName.ToUpper();
set => _lastName = value;
}
public int Age { get; set; }
}

You can only really use linq to provide a list of new objects
var upperList = myList.Select(p=> new Person {
FirstName = (p.FirstName == null) ? null : p.FirstName.ToUpper(),
LastName = (p.LastName == null) ? null : p.LastName.ToUpper(),
Age = p.Age
}).ToList();

p.lastname.ToString().ToUpper().Contains(TextString)

Related

XUnit Mocking Passing and Asserting a Collection

New to XUnit and attempting to test the following method but test is failing.
I believe the reason is that a new instance of the data is created when the assert is called.
So even thought the results are the same the object reference is not.
When a new record is added to the collection, it is supposed to returned the same record added.
Employee Data Class:
public class EmployeeData
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Title { get; set; }
}
Method to test:
public List<EmployeeData> Add(List<EmployeeData> employees)
{
foreach(var employee in employees)
{
data.Add(new EmployeeData() { Id = newId(),
FirstName = employee.FirstName, LastName = employee.LastName,
Title = employee.Title });
}
return data;
}
Test Method:
[Fact]
public void Add()
{
// Arrange
var data = new List<EmployeeData>();
data.Add(new EmployeeData() { Id = 1, FirstName = "Adams", LastName = "John", Title = "Fireman" });
// Mocking the employee class constructor arguments.
var mockLogging = new Mock<ILogger<DataService>>();
var mockConfig = new Mock<IConfiguration>();
var sut = new Employee(mockLogging.Object, mockConfig.Object);
var result = sut.Add(data);
// Assert should return the exact data inputted
Assert.Equal(result, data);
}
Test Result:
Message:
Assert.Equal() Failure
Expected: List<EmployeeData> [EmployeeData { FirstName = "Adams", Id = 1, LastName = "John", Title = "Fireman" }]
Actual: List<EmployeeData> [EmployeeData { FirstName = "Adams", Id = 1, LastName = "John", Title = "Fireman" }]
What does it mean that EmployeeData equals EmployeeData?
You can override Equals methods in the EmployeeData class.
public override bool Equals(object obj)
{
return this.FirstName == ((EmployeeData)obj).FirstName;
}
The second option is to pass IEqualityComparer<T> to the Equal method.
public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
private Func<T, object> _expr { get; set; }
public GenericCompare(Func<T, object> expr)
{
this._expr = expr;
}
public bool Equals(T x, T y)
{
var first = _expr.Invoke(x);
var sec = _expr.Invoke(y);
if (first != null && first.Equals(sec))
return true;
else
return false;
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
}
And use it:
Assert.Equal<EmployeeData>(result, data, new GenericCompare<EmployeeData>(x => x.Id));
Copy pasted GenericCompare from https://stackoverflow.com/a/23623976/6629020

Expression-tree to build sub-select results

I'm trying to build a sub-query by using expression-trees. In linq I would write something like:
var single = MyTable
.AsExpandable()
.Select(c => new
{
Childs = Enumerable.Select(
MyTable.VisibleChilds.Invoke(c, dbContext),
cc => Convert(cfg.ChildsConfig).Invoke(dbContext, cc))
});
where the Convert is building an expression like
p => new MyTableSelect {
Id = p.Id,
Name = p.Name
}
depending on the given values from the config (to only read needed data from database).
but I'm struggeling with the second parameter to be passed to the Select call as I need cc to be passed to the Convert-call.
I guess I need something like Expression.Lambda<Func<>> but I don't see it.
Expression.Lambda>(Expression.Invoke(Instance.Convert(cfg.ChildOrganizersFilterConfig), ContextParameter, theEntity));
I am not familiar with your use of Invoke but if you just want to run a 'Converter' in a fluent syntax for use in a Linq Expression I could show you an example of that. Say I have three POCO classes, one parent container, a child container, and a container I want to convert to.
public class POC
{
public int Id { get; set; }
public string Name { get; set; }
public POC(int id, string name)
{
Id = id;
Name = name;
}
}
public class ChildPOC
{
public int ParentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ChildPOC(int parentId, string firstName, string lastName)
{
ParentId = parentId;
FirstName = firstName;
LastName = lastName;
}
}
public class ChildPOCAlter
{
public int ParentId { get; set; }
public string Name { get; set; }
public ChildPOCAlter(string first, string last, int parentId)
{
ParentId = parentId;
Name = $"{first} {last}";
}
}
I could write a converter method to take ChildPOC to ChildPOCAlter like so:
public static Converter<ChildPOC, ChildPOCAlter> ChildPOCOAlter()
{
return new Converter<ChildPOC, ChildPOCAlter>((x) => { return new ChildPOCAlter(x.FirstName, x.LastName, x.ParentId); });
}
I could then populate some data:
var someParents = new List<POC> { new POC(1, "A"), new POC(2, "B") };
var somechildren = new List<ChildPOC> { new ChildPOC(1, "Brett", "x"), new ChildPOC(1, "Emily", "X"), new ChildPOC(2, "John", "Y") };
And then I may want to take these relationships and apply a converter directly on it:
var relationships = someParents.Select(x => new
{
Id = x.Id,
Name = x.Name,
Children = somechildren.Where(y => y.ParentId == x.Id).ToList().ConvertAll(ChildPOCOAlter())
});

How to copy all fields from one object to another, but only when destination value is NULL

I'd like to merge one object into another in a generic way that can be used repeatedly for other more complex objects. I only want the NULLs to change.
ie. merge sourcePerson and detinationPerson to get resultingPerson (below)
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int? Age { get; set; }
public string Address { get; set; }
}
var sourcePerson = new Person
{
FirstName = "Bill",
LastName = "Smith",
Age = 43,
Address = "123 Some Street"
};
var destinationPerson = new Person
{
FirstName = "Barbara",
LastName = null,
Age = 41,
Address = null
};
var resultingPerson = new Person
{
FirstName = "Barbara",
LastName = "Smith",
Age = 41,
Address = "123 Some Street"
};
I've tried Automapper, but can't seem to configure it properly. I feel like the following should work. Help?
Mapper.CreateMap<Person, Person>().ForAllMembers(opt => opt.UseDestinationValue());
Mapper.Map(sourcePerson, destinationPerson);
Thanks IronGeek and tutok.
Yes, the following works:
Mapper.CreateMap<Person, Person>()
.ForAllMembers(opt => opt.Condition(person => person.DestinationValue == null));
Your can use reflection like this:
public static T Merge<T>(T source, T destination)
{
var returnvalue = (T) Activator.CreateInstance(typeof (T));
foreach (var field in destination.GetType().GetProperties())
{
field.SetValue(returnvalue,
field.GetValue(destination, null) == null ? field.GetValue(source) : field.GetValue(destination));
}
return returnvalue;
}
I haven't tested this for other than simple DTOs.

How to access a property of a class by its name?

I find it hard to clearly describe the case in a one-sentence title. Here is the example:
public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
public enum PersonProperties
{
FirstName = 1,
MiddleName = 2,
LastName = 3
}
I am hoping to do this:
foreach (var p in Persons) {
var nameCollection=new List<string>();
foreach (var s in (SectionsEnum[]) Enum.GetValues(typeof (SectionsEnum)))
{
nameCollection.Add(p.GetPropertyByName(s);
}
}
Now, how can we implement the GetPropertyByName() part?
You could do this directly using reflection:
public string GetPropertyByName(SectionsEnum s)
{
var property = typeof(Person).GetProperty(s.ToString());
return (string)property.GetValue(this);
}
Or maybe with a switch.
public string GetPropertyByName(SectionsEnum s)
{
switch (s)
{
case SectionsEnum.FirstName:
return this.FirstName;
case SectionsEnum.MiddleName:
return this.MiddleName;
case SectionsEnum.LastName:
return this.LastName;
default:
throw new Exception();
}
}
But I'd ask if you wouldn't be better served by a wholly different approach, e.g. a list:
public IList<string> NameProperties
{
get
{
return new[] { FirstName, MiddleName, LastName };
}
}
Or instead of having SectionsEnum, use Funcs:
//was
SectionsEnum s = SectionsEnum.FirstName;
//instead
Func<Person, string> nameFunc = p => p.FirstName;
string name = nameFunc(myPerson);
this should be a good starting point for you
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Person p = new Person() { FirstName ="a", MiddleName = "b", LastName = "c" };
List<string> result = new List<string>();
string[] enums = Enum.GetNames(typeof(PersonProperties));
foreach(string e in enums)
{
result.Add(p.GetType().GetProperty(e).GetValue(p, null).ToString());
}
int i = 0;
foreach (string e in enums)
{
Console.WriteLine(string.Format("{0} : {1}", e, result[i++]));
}
Console.ReadKey(false);
}
}
public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
public enum PersonProperties
{
FirstName = 1,
MiddleName = 2,
LastName = 3
}
}

ForAllMembers, Map based on condition

Need to conditionally map properties based on if they appear in the InvalidProperties list. If the current source property name exists in the list, then it should use the destinations value.
Created a solution, but not sure if it's the "right way" to do it:
public class MyBassClass
{
public List<string> InvalidProperties
{
get;
set;
}
}
public class PersonAllergy : MyBassClass
{
public PersonAllergy()
{
InvalidProperties = new List<string>();
}
public int Id
{
get;
set;
}
public string Allergy
{
get;
set;
}
}
public class Person : MyBassClass
{
public Person()
{
InvalidProperties = new List<string>();
Allergy = new PersonAllergy();
}
public PersonAllergy Allergy
{
get;
set;
}
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public int Age
{
get;
set;
}
}
private static bool IgnoreInvalid( AutoMapper.ResolutionContext context )
{
return ( (MyBassClass)context.InstanceCache.First().Value )
.InvalidProperties.Contains( context.MemberName );
}
Usage:
Person person = new Person();
person.FirstName = "john";
person.LastName = "smith";
person.Age = 45;
person.Allergy.Id = 1;
person.Allergy.Allergy = "Penacilin";
person.Allergy.InvalidProperties.Add( "Id" );
person.InvalidProperties.Add( "Age" );
Person templatePerson = new Person();
templatePerson.FirstName = "sam";
templatePerson.LastName = "rottenburg";
templatePerson.Age = 55;
templatePerson.Allergy.Id = 2;
templatePerson.Allergy.Allergy = "Monkeys";
AutoMapper.Mapper.CreateMap<Person, Person>()
.ForAllMembers( opt => opt.Condition( IgnoreInvalid ) );
AutoMapper.Mapper.CreateMap<PersonAllergy, PersonAllergy>()
.ForAllMembers( opt => opt.Condition( IgnoreInvalid ) );
var mergedPerson = AutoMapper.Mapper
.Map<Person, Person>(templatePerson, person);
mergedPerson.Allergy = AutoMapper.Mapper
.Map<PersonAllergy, PersonAllergy>( templatePerson.Allergy, person.Allergy );
Person Outputs:
Age: 45 (instead of 55)
FirstName: John
LastName: Smith
Allergy Outputs:
Allergy: Penacillin
Id: 2 (instead of 1)

Categories

Resources