Linq expression to fill model containing other List<T> - c#

I have a model (sample below), and sample data with desired output..
I have also given two ways to populate model which will give desired output but for somereason,
looks like I am missing something....
Not able to figure out what is the issue here...
need inputs in correcting approach 1 or approach 2 or you can also suggest any other approach which will help populate model in below response..
models
class Emp
{
public int id {get;set;}
public int Name {get;set;}
public List<cardType> cardTypes {get;set;}
}
class cardType
{
public int name {get;set;}
public DateTime Expiry {get;set;}
}
sample data (Data is returned in 1 table only)
id Name cardTypeName exp
1 a Amex 1010
1 a City 2010
desired output
<Emp>
<ID>1</id>
<name>1</name>
<cardTypes>
<cardType>
<Name> Amex </Name>
<exp> 1010 </exp>
</cardType>
<cardType>
<Name> City </Name>
<exp> 2010 </exp>
</cardType>
<cardTypes>
</Emp>
approach 1
(dataTable.AsEnumerable()
.GroupBy(r => r.ItemArray[1])
.Select(grp => new Emp()
{
id = r.itemarray[1],
name = r.itemarray[1],
cardTypes = grp.Select(t => new cardType()
{
field 1,
field 2
}).ToList()
}));
approach 2
return (from DataRow dr in dataTable.Rows
select new Emp
{
id = "",
name= "",
cardTypes = dataTable.AsEnumerable()
.Select(x => new cardType
{
name = ""
exp = ""
}).ToList(),
});

try this
return dataTable.AsEnumerable()
.GroupBy(x => x.Id)
.Select(x => new
{
id = x.Key,
name = x.Key,
cardTypes = x.Select(t => new
{
name = t.Name,
exp = t.Exp
})
});

This should work for you considering you will change the Expiry field in cardType to int
var results =
dt.AsEnumerable()
.GroupBy(row => row.ItemArray[0])
.Select(rows => new Emp
{
id = rows.Key,
name = rows.Key,
cardTypes = rows.Select(row => new cardType {Name = row.ItemArray[2], Expiry = row.ItemArray[3]})
});
If you desire to have DateTime, then decide how to convert. For example:
cardTypes = rows.Select(row => new cardType {Name = row.ItemArray[2], Expiry = new DateTime(row.ItemArray[3],1,1)})

If you want to serialize to xml your models, you should mark your models with [Serializable] attribute and just use XmlSerializer.
[Serializable]
class Emp
{
public int id {get;set;}
public int Name {get;set;}
public List<cardType> cardTypes {get;set;}
}
[Serializable]
class cardType
{
public int name {get;set;}
public DateTime Expiry {get;set;}
}
After that you can use following code:
static Dictionary<int, Emp> ConvertToDict(DataTable dt)
{
Dictionary<int, Emp> emps = new Dictionary<int, Emp>();
foreach (var dr in dt.AsEnumerable())
{
var id = (int)dr["ID"];
var name = (string)dr["Name"];
var cardTypeName = (string)dr["CardTypeName"];
var exp = (int)dr["Exp"];
Emp emp;
var cardType = new CardType { Name = cardTypeName, Exp = exp };
if (emps.TryGetValue(id, out emp))
{
emp.CardTypes.Add(cardType);
}
else
{
emps.Add(id, new Emp { ID = id, Name = name, CardTypes = new List<CardType> { cardType } });
}
}
return emps;
}
static List<string> Serialize<T>(IEnumerable<T> entities) where T:new()
{
var ser = new XmlSerializer(typeof(T));
var serializedEntities = entities.Select(entity =>
{
using (var sw = new StringWriter())
{
ser.Serialize(sw, entity);
return sw.ToString();
}
}).ToList();
return serializedEntities;
}

Related

group by and merge some field result in linq

I have a class like
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now I have a list of this class: List<Person> persons;
var persons = new List<Person> {
new Person { Id = 1, LastName = "Reza", FirstName="Jenabi" },
new Person { Id = 1, LastName = "Amin", FirstName="Golmahalle"},
new Person { Id = 2, LastName = "Hamed", FirstName="Naeemaei"}
};
Is there a way I can group by Id and get the list of all the full Name (Combine first and last names)?
So after grouping:
var Id = results[0].Id; // Output : 1
List<string> fullNames = results[0].FullNames; // Output : "Reza Jenabi","Amin Golmahalle"
I believe this is what you need:
var results = persons.GroupBy(x => x.Id)
.Select(x => new { Id = x.Key, FullNames = x.Select(p => $"{p.FirstName} {p.LastName}").ToList() })
.ToList();
I think bellow code can help you:
var fff = from p in persons
group $"{p.FirstName} {p.LastName}" by p.Id into g
select new { PersonId = g.Key, FullNames = g.ToList() };
yeah, you can use GroupBy and Join those items:
var grouped = persons.GroupBy(p => p.Id)
.Select(s => string.Join(", ", s.Select(a=> $"{a.FirstName} {a.LastName}")));

OrderBy Collection in Dictionary Value based on Property of the Collection

I'm having a List<Department>, in that it has Department Name, List of Employees Name and OrderBy Dicrection for List. Now I need to construct a Dictionary<string, ObservableCollection<Employee>>, the KEY is a Department Name and the Value is an ObservableCollection by using List<Department>.
Expectation: I need to Sort the EmpName within the ObservableCollection<Employee> which is present in Dictionary<string, ObservableCollection<Employee>> EmpList based on sortEmpName Property which is in List<Department> DepList using
LINQ
I written the LINQ for this in the below Main() Function.
void Main()
{
List<Department> DepList = new List<Department>()
{
new Department() {
DepName = "HR",
sortEmpName = FilterListSortDirection.SortDirection.Ascending,
EmpName = new List<string>() {"Raj", "Baba"}
},
new Department() {
DepName = "iLab",
sortEmpName = FilterListSortDirection.SortDirection.Descending,
EmpName = new List<string>() {"Emma", "Kaliya"}
},
new Department() {
DepName = "Testing",
sortEmpName = FilterListSortDirection.SortDirection.None,
EmpName = new List<string>() {"Supriya", "Billa"}
}
};
Dictionary<string, ObservableCollection<Employee>> EmpList = DepList.Select(m =>
new {
Dep = m.DepName,
EmpCollection = new ObservableCollection<Employee>(
m.EmpName.Select(k =>
new Employee() { EmpName = k, IsChecked = true }).ToList())
}
).ToDictionary(x => x.Dep, x => x.EmpCollection);
}
The Model Classes are
public class Employee
{
public string EmpName { get; set; }
public bool IsChecked { get; set; }
}
public class Department
{
public string DepName { get; set; }
public FilterListSortDirection.SortDirection sortEmpName { get; set; }
public List<string> EmpName { get; set; }
}
public class FilterListSortDirection
{
public enum SortDirection
{
None,
Ascending,
Descending
}
}
The Output Screen Shot of List<Department> DepList
The Output Screen Shot of Dictionary<string, ObservableCollection<Employee>> EmpList
Expectation: I need to Sort the EmpName within the ObservableCollection<Employee> which is present in Dictionary<string, ObservableCollection<Employee>> EmpList based on sortEmpName Property which is in List<Department> DepList using
LINQ
This is a small part of a complex LINQ query in my project, so, I need to achieve this in LINQ. Kindly assist me.
HR => List of Employee Names should be in Ascending
iLab => List of Employee Names should be in Descending
Testing => List of Employee Names should be in original order - None (i.e., No Change - Don't Sort)
I guess you need something like this:
var EmpList = DepList.ToDictionary(p => p.DepName, p =>
{
var empList = p.EmpName.Select(k => new Employee() { EmpName = k, IsChecked = true });
if (p.sortEmpName == FilterListSortDirection.SortDirection.Ascending)
{
empList = empList.OrderBy(q => q.EmpName);
}
else if (p.sortEmpName == FilterListSortDirection.SortDirection.Descending)
{
empList = empList.OrderByDescending(q => q.EmpName);
}
return new ObservableCollection<Employee>(empList.ToList());
});
use OrderBy
Dictionary<string, ObservableCollection<Employee>> EmpList = DepList.Select(m => new
{
Dep = m.DepName,
EmpCollection =
m.sortEmpName == SortDirection.Ascending ?
new ObservableCollection<Employee>(m.EmpName.Select(k => new Employee {EmpName = k, IsChecked = true}).ToList().OrderBy(emp => emp.EmpName)) :
m.sortEmpName == SortDirection.Descending ?
new ObservableCollection<Employee>(m.EmpName.Select(k => new Employee { EmpName = k, IsChecked = true }).ToList().OrderByDescending(emp => emp.EmpName)) :
new ObservableCollection<Employee>(m.EmpName.Select(k => new Employee { EmpName = k, IsChecked = true }).ToList())
}).ToDictionary(x => x.Dep, x => x.EmpCollection);

c# append new items to list item

The code below has a list with two rows in it, I want to be able to amalgamate the two lines and sum the income field based on the Country.
public class City
{
public string Name {get;set;}
public string Country {get;set;}
public double income {get;set;}
}
public class myClass
{
void Main()
{
var c = GetCities();
var d = c.GroupBy(s => new {s.Country, s.Flag, s.Name}).Select(s => new City { Country = s.Key.Country, Flag = s.Key.Flag, Name = s.Key.Name});
d.Dump(); //LINQPAD output to screen
}
public List<City> GetCities()
{
List<City> cities = new List<City>();
cities.Add(new City() { Name = "Istanbul", income= 22.00, Country = "Turkey" });
cities.Add(new City() { Name = "", income= 44.88, Country = "Turkey" });
return cities;
}
}
in my real application the list is being generated in two places, but the data needs to show on one single line.
found my answer
var result = c.GroupBy(x => x.Country)
.Select(g => {
var item = g.First();
return new{
Country = item.Country,
Name = item.Name,
income = g.Sum(x => x.income) };
}).ToList();

How to make select query by string property name in lambda expression?

I would like to make a query by using lambda select,
Like below:
public class Foo{
public int Id {get;set;}
public string Name {get;set;}
public string Surname {get;set;}
}
var list = new List<Foo>();
var temp = list.Select(x=> x("Name"),("Surname"));
The property name needs to be sent as a string,
I dont know how to use, I have given it for being a example.
is it possible?
Edit:
Foo list :
1 A B
2 C D
3 E F
4 G H
I don't know type of generic list, I have property name such as "Name", "Surname"
I want to be like below:
Result :
A B
C D
E F
G H
The following code snippet shows 2 cases. One filtering on the list, and another creating a new list of anonymous objects, having just Name and Surname.
List<Foo> list = new List<Foo>();
var newList = list.Select(x=> new {
AnyName1 = x.Name,
AnyName2 = x.Surname
});
var filteredList = list.Select(x => x.Name == "FilteredName" && x.Surname == "FilteredSurname");
var filteredListByLinq = from cust in list
where cust.Name == "Name" && cust.Surname == "Surname"
select cust;
var filteredByUsingReflection = list.Select(c => c.GetType().GetProperty("Name").GetValue(c, null));
Interface
If you have access to the types in question, and if you always want to access the same properties, the best option is to make the types implement the same interface:
public interface INamable
{
string Name { get; }
string Surname { get; }
}
public class Foo : INamable
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
This will preserve type safety and enable queries like this one:
public void ExtractUsingInterface<T>(IEnumerable<T> list) where T : INamable
{
var names = list.Select(o => new { Name = o.Name, Surname = o.Surname });
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
If, for some reason, you can't alter the original type, here are two more options.
Reflection
The first one is reflection. This is Mez's answer, i'll just rephrase it with an anonymous type like in the previous solution (not sure what you need exactly):
public void ExtractUsingReflection<T>(IEnumerable<T> list)
{
var names = list.Select(o => new
{
Name = GetStringValue(o, "Name"),
Surname = GetStringValue(o, "Surname")
});
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
private static string GetStringValue<T>(T obj, string propName)
{
return obj.GetType().GetProperty(propName).GetValue(obj, null) as string;
}
Dynamic
The second uses dynamic:
public void ExtractUsingDynamic(IEnumerable list)
{
var dynamicList = list.Cast<dynamic>();
var names = dynamicList.Select(d => new
{
Name = d.Name,
Surname = d.Surname
});
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
With that in place, the following code:
IEnumerable<INamable> list = new List<Foo>
{
new Foo() {Id = 1, Name = "FooName1", Surname = "FooSurname1"},
new Foo() {Id = 2, Name = "FooName2", Surname = "FooSurname2"}
};
ExtractUsingInterface(list);
// IEnumerable<object> list... will be fine for both solutions below
ExtractUsingReflection(list);
ExtractUsingDynamic(list);
will produce the expected output:
FooName1 FooSurname1
FooName2 FooSurname2
FooName1 FooSurname1
FooName2 FooSurname2
FooName1 FooSurname1
FooName2 FooSurname2
I'm sure you can fiddle with that and get to what you are trying to achieve.
var temp = list.Select(x => x.Name == "Name" && x.Surname == "Surname");
var temp = list.Select(x => new {Name = x.Name, Surname = x.Surname});

Convert DataTable(dynamic columns) into List<T>

I want to convert DataTable with below sample Data
Employee subject1 subject2 subject3 .......
1 100 80 60......
2 90 70 70...
into
List.
Where Employee Object is as follows..
public class Employee
{
public int EmpId { get; set;}
public Dictionary<string,decimal> SubjectMarks;
}
Can anyone help me in converting this Datatable to List in c sharp or using linq.
So the dynamic subject-columns start at index 1 and end at table.Columns-Count-1. Then i would create an array of these columns first. Then you can use Select + ToDictionary + ToList:
DataColumn[] subjectColumns = table.Columns.Cast<DataColumn>().Skip(1).ToArray();
List<Employee> employee = table.AsEnumerable()
.Select(r => new Employee
{
EmpId = r.Field<int>("Employee"),
SubjectMarks = subjectColumns.Select(c => new
{
Subject = c.ColumnName,
Marks = r.Field<decimal>(c)
})
.ToDictionary(x => x.Subject, x => x.Marks)
}).ToList();
Assuming that the type of the columns are already int for the ID and decimal for the marks. Otherwise use int.Parse and decimal.Parse to convert them.
var list = new List<Employee>();
var id = table.Columns[0];
var marks = table.Columns.Cast<DataColumn>().Skip(1).ToArray();
foreach (DataRow row in table.Rows)
{
var obj = new Employee { EmpId = (int) row[id] };
var dict = new Dictionary<string,decimal>();
foreach (var mark in marks)
{
dict[mark.ColumnName] = (decimal)row[mark];
}
obj.SubjectMarks = dict;
list.Add(obj);
}
Instead of using a Dictionary, then you can you List to add subject grades
Try this:
public class Employee
{
public int EmpId { get; set;}
public List<string> SubjectMarks { get; set;}
}
var empList = (from emp in dtEmployeeList.AsEnumerable()
select new Employee()
{
EmpId = (int) emp["Employee"],
SubjectMarks = List<string>()
{
emp["subject1"].ToString(),
emp["subject2"].ToString(),
emp["subject3"].ToString()
}
}).ToList()
You can do like this:
System.Data.DataTable table = // your table
List<Employee> result =
table.AsEnumerable().Select(i => new Employee()
{
EmpId = i.Field<int>("Employee"),
SubjectMarks = { { "subject1", i.Field<decimal>("subject1") } ,
{ "subject2", i.Field<decimal>("subject2") } ,
{ "subject3", i.Field<decimal>("subject3") } }
}).ToList();
You'll need to make sure you have a reference to System.Data.DataSetExtensions. And don't forget to add the using System.Data.

Categories