How to access a property of a class by its name? - c#

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
}
}

Related

Set property value while selecting from list of class objects

Am trying to set value(day value to "Sunday") while selecting from list, like below in method 'getData', is there a way I can set it without really changing class object property 'day' value? I just want it set to 'Sunday' while reading, like 'Sunday' as 'day'.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleAppForChecking
{
public class test
{
public string firstName { get; set; }
public string lastName { get; set; }
public int age { get; set; }
public string day { get; set; }
}
public class TestMain
{
static void Main(string[] args)
{
var tests = new List<test>()
{
new test { firstName = "Mike", lastName = "Toss", age = 20, day = "Monday" },
new test { firstName = "Peter", lastName = "Page", age = 30, day = "Tuesday" },
new test { firstName = "Stacy", lastName = "Page", age = 27, day = "Wednesday" }
};
getData(tests);
GetDate(tests);
}
public static void getData(List<test> _data)
{
var _data1 = _data.Where(w => w.firstName == "Stacy")
.Select(o => new
{
o.firstName,
o.lastName,
day = o.day = "Sunday",
o.age
})
.ToList();
foreach(var d in _data1)
{
Console.WriteLine(d);
}
}
public static void GetDate(List<test> _data1)
{
foreach (var d in _data1)
{
Console.WriteLine(d);
}
}
}
}
_data.Where(w => w.firstName == "Stacy")
.Select(o=> {o.day = "Sunday"; return c;})
.ToList();

Search a hierarchy of class and return the path to get there

I have this class, a hierarchy of categories.
class Categories
{
public long Id { get; set; }
public long ParentId { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public List<Categories> ChildrenData { get; set; }
}
How can I recursively iterate through this class of unknown depth and return the path to get there?
All "Id" values are unique. Say I want to find Id = 23 and get the path to get there by concatenating "Name".
For example, in the image below searching for ID = 23 would return: Default Category/Books/Nonfiction/Best-sellers
Example Hierarchy
My suggestion is that you first build an index:
public static Dictionary<long, Category> IndexBuilder(Category c)
{
var index = new Dictionary<long, Category>();
IndexBuilder(c, index);
return index;
}
private static void IndexBuilder(Category c, Dictionary<long, Category> index)
{
if (index.ContainsKey(c.Id))
return;
index[c.Id] = c;
foreach(var child in c.ChildrenData)
IndexBuilder(child, index);
}
Now you have a lookup, and your path is then easy to produce:
static IEnumerable<Category> PathToRoot(long id, Dictionary<long, Category> index)
{
// Presumably the parent id of the top category is a sentinel.
long current = id
while (current != 0)
{
var category = index[current];
yield return category;
current = category.ParentId;
}
}
Or maybe we just go until we run out of index:
static IEnumerable<Category> PathToRoot(long id, Dictionary<long, Category> index)
{
long current = id
while (index.ContainsKey(current))
{
var category = index[current];
yield return category;
current = category.ParentId;
}
}
Now you have a tool you can use to make your string:
static string Slash<T>(this IEnumerable<T> items) =>
string.Join("/", items);
var s = PathToRoot(23, index)
.Reverse()
.Select(c => c.Name)
.Slash();
See what I am doing here? Make a bunch of helper methods each of which is about five lines long, that can be composed together to make powerful solutions.
I have provided 2 ways, the first way is recursive and the last is not.
Recursive way, add a reference to your parent. This way when you find a match you can easily traverse your way back up the chain to create your path.
class Categories
{
public Categories Parent { get; set; }
public long Id { get; set; }
public long ParentId { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public List<Categories> ChildrenData { get; set; }
}
Then add a Find() method:
public string Find(long id)
{
if( Id == id )
{
return GetPath(); //<-- we need to code this next.
}
else
{
foreach( var entry in Categories)
{
string path = entry.Find(id);
if( path != null )
{
return path;
}
}
return null;
}
}
And finally the GetPath(), the assumption here is that the highest level instances of Categories do not have a Parent:
public string GetPath()
{
System.Text.StringBuilder sb = new StringBuilder();
Categories current = this;
while( current != null)
{
sb.Insert(0,current.Name);
if( current != this)
{
sb.Insert(0,"/");
}
current = Parent;
}
return sb.ToString();
}
Now if recursion isn't what you want, then pass in the current path to the Find() method.
public string Find(long id, string pathSoFar)
{
if (pathSoFar == null)
{
pathSoFar = Name;
}
else
{
pathSoFar = pathSoFar + Name;
}
if ( Id == id)
{
return pathSoFar;
}
else
{
foreach( var entry in Categories)
{
string path = entry.Find(id, pathSoFar + "/");
if( path != null )
{
return path;
}
}
return null;
}
}
Usage:
var nonRecusive = cats.Find(23, null);
This will get what you are looking for using recursion:
void Main()
{
var data = GetData();
Console.WriteLine(GetPath(data, 23, ""));
}
public String GetPath(Categories c, Int32 id, String path)
{
if (c.Id == id)
{
return path + "/" + c.Name;
}
foreach (var cd in c.ChildrenData)
{
var p = GetPath(cd, id, path + "/" + c.Name);
if (!String.IsNullOrWhiteSpace(p))
{
return p;
}
}
return "";
}
public class Categories
{
public long Id { get; set; }
public long ParentId { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public List<Categories> ChildrenData { get; set; }
}
public Categories GetData()
{
return
new Categories
{
Id = 1,
Name = "Default Category",
ChildrenData = new List<Categories>
{
new Categories
{
Id = 2,
Name = "Magazines",
ChildrenData = new List<Categories> {}
},
new Categories
{
Id = 2,
Name = "Books",
ChildrenData = new List<Categories>
{
new Categories
{
Id = 20,
Name = "Fiction",
ChildrenData = new List<Categories> {}
},
new Categories
{
Id = 21,
Name = "Nonfiction",
ChildrenData = new List<Categories>
{
new Categories
{
Id = 22,
Name = "New",
ChildrenData = new List<Categories> {}
},
new Categories
{
Id = 23,
Name = "Best-Sellers",
ChildrenData = new List<Categories> {}
},
}
}
}
}
}
};
}

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)

Get initials of column c#

I'm reding a .txt file that contains a list of several names in one single column, they are listed like this:
Lastname Middlename Name:
Lastname Middlename Name:
Lastname Middlename Name:
Lastname Middlename Name:
Lastname Middlename Name:
I only want to keep the initials of every name and add them to another list, for example:
Lastname Middlename Name: => LAMN
Do I have to separate by spaces? or use several removes? or use RegEX?
Thanks!
var result = File.ReadAllLines("text.txt")
.Select(line => new string (line.Split(' ')
.Select(s => s.First())
.ToArray())
).ToList();
Edit:
To get two letters from last name:
var result = File.ReadAllLines("text.txt").Select(line =>
{
var words = line.Split(' ');
var la = words.First().Take(2);
var mn = words.Skip(1).Select(s => s.First());
return new string(la.Concat(mn).ToArray()).ToUpper();
}
).ToList();
The result with be: LAMN
public List<Person> ParseFile(string filePath)
{
List<Person> lp = new List<Person>();
using (StreamReader sr = new StreamReader(filePath))
{
while (!sr.EndOfStream)
{
lp.Add(new Person(sr.ReadLine()));
}
}
return lp;
}
With
class Person
{
public Person(string fullName)
{
this.fullName = fullName;
}
private string fullName;
public string FullName
{
get { return fullName; }
set { fullName = value; }
}
private string initials;
public string Initials
{
get { return String.Join("",new string[]{
String.Join("",fullName.Split(new char[] { ' ' }).Take(1).Select(i => i.Substring(0, 2))),
String.Join("",fullName.Split(new char[] { ' ' }).Skip(1).Select(i => i.Substring(0, 1)))
});
set { initials = value; }
}
}
First I would make a Person class:
public class Person
{
string _initials = "";
public String FirstName { get; set; }
public String LastName { get; private set; }
public String MiddleName { get; private set; }
public String Initials { get { return _initials; } }
public String FullName { get { return FirstName + MiddleName + LastName; } }
public Person(String name)
{
string[] names = name.Split(' ');
if (names.Length != 3)
{
throw new ArgumentException("Incorrect format for a person.");
}
FirstName = names[2];
MiddleName= names[1];
LastName = names[0];
_initials =
String.Concat(LastName[0],LastName[1],MiddleName[0],FirstName[0]);
}
}
Then populate the Person class with the file:
List<Person> personsList = new List<Person>();
using (StreamReader reader = new StreamReader(filePath))
{
while (!reader.EndOfStream)
{
Person p = new Person(reader.ReadLine());
personsList.Add(p);
}
}
Then you can access the Person's first,last,and middle name as well as their initials:
foreach(Person p in personsList)
{
Console.WriteLine(p.Initials);
}
May be it's not a complete solution for your question, but I think is the better way to get initials without using LINQ.
Regex to extract initials from Name

Uppercase a List of object with LINQ

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)

Categories

Resources