I have data currently parsed in to a .Net System.Data.DataTable in this format:
ID | Header | Value
1 Name Jim
1 Age 34
1 Gender M
2 Name Gibby
2 Age 32
2 Gender F
3 Name Bob
3 Age 100
3 Gender U
and I need to get it into another DataTable that has those fields:
ID | Name | Age | Gender
1 Jim 34 M
2 Gill 32 F
3 Bob 100 U
This is achievable in SQL using:
select id,
max(case when header = 'Name' then Value end) as name,
max(case when header = 'Age' then Value end) as Age,
max(case when header = 'Gender' then Value end) as Gender
from pivottest
group by id;
but this means I'd need to update a sql table with the values and then run this query, where I'd prefer to do it in a c# method before pushing it to the DB.
Is this possible to do in C#, probably using Linq? Where would I even start?!
Assuming you don't mind that it's not returned in a DataTable, you could actually create a list of persons like this:
Group the data in the data table by Id:
var groups = <yourDataTable>.AsEnumerable().GroupBy(x => x.Field<int>("Id"));
Create a class called Person and Enum for Gender:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
}
public enum Gender
{
M,
F,
U
}
Create a list of persons called "people" and loop through the groupings and map them to a Person like this:
var people = new List<Person>();
foreach (var group in groups)
{
people.Add(new Person
{
Id = group.Key,
Name = group.FirstOrDefault(x => x.Field<string>("Header") == "Name").Field<string>("Value"),
Age = Convert.ToInt32(group.FirstOrDefault(x => x.Field<string>("Header") == "Age").Field<string>("Value")),
Gender = (Gender)Enum.Parse(typeof(Gender), group.FirstOrDefault(x => x.Field<string>("Header") == "Gender").Field<string>("Value"))
});
}
I put this into a console app to demonstrate:
class Program
{
static void Main(string[] args)
{
var data = new DataTable();
data.Columns.Add("Id", typeof(int));
data.Columns.Add("Header", typeof(string));
data.Columns.Add("Value", typeof(string));
data.Rows.Add(1, "Name", "Jim");
data.Rows.Add(1, "Age", "34");
data.Rows.Add(1, "Gender", "M");
data.Rows.Add(2, "Name", "Gibby");
data.Rows.Add(2, "Age", "32");
data.Rows.Add(2, "Gender", "F");
data.Rows.Add(3, "Name", "Bob");
data.Rows.Add(3, "Age", "100");
data.Rows.Add(3, "Gender", "U");
Console.WriteLine("Current data:");
foreach (DataRow row in data.Rows)
{
Console.WriteLine("id: {0}, header: {1}, value: {2}", row[0], row[1], row[2]);
}
var groups = data.AsEnumerable().GroupBy(x => x.Field<int>("Id"));
var people = new List<Person>();
foreach (var group in groups)
{
people.Add(new Person
{
Id = group.Key,
Name = group.FirstOrDefault(x => x.Field<string>("Header") == "Name").Field<string>("Value"),
Age = Convert.ToInt32(group.FirstOrDefault(x => x.Field<string>("Header") == "Age").Field<string>("Value")),
Gender = (Gender)Enum.Parse(typeof(Gender), group.FirstOrDefault(x => x.Field<string>("Header") == "Gender").Field<string>("Value"))
});
}
Console.WriteLine("People list data:");
foreach (var person in people)
{
Console.WriteLine("Id: {0}, Name: {1}, Age: {2}, Gender: {3}", person.Id, person.Name, person.Age, person.Gender.ToString());
}
Console.ReadLine();
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
}
public enum Gender
{
M,
F,
U
}
Results:
Related
Consider the following grid view:
Its fairly empty at the moment but will be more populated.
peronID column will always be in the same place. How can I iterate over the first column, counting all personID's equal to a given amount? Something like this (pseudocode) :
foreach element in personID
if element == 25
count += 1
Code that populates the GridView:
private void DisplayTable()
{
LibraryEntities1 myDB = new LibraryEntities1();
var people = myDB.People;
gridViewPeople.DataSource = people.ToList();
gridViewPeople.DataBind();
}
I suggest you operate on the data, not on the grid, so assuming the List below is what you might have bound to your grid:
Given class Person:
class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
You can group by and sum just like you might do in SQL:
List<Person> persons = new List<Person>{
new Person{ PersonID = 13, PersonName = "Foo" },
new Person{ PersonID = 13, PersonName = "Foo" },
new Person{ PersonID = 15, PersonName = "Foo" }
};
var grp = persons.GroupBy(p => p.PersonID)
.Select(p => new {pid = p.Key, count = p.Count()});
foreach (var element in grp)
{
Console.WriteLine($"PersonID = {element.pid}, Count of Person = {element.count}");
}
Output:
PersonID = 13, Count of Person = 2
PersonID = 15, Count of Person = 1
This question already has answers here:
LINQ with groupby and count
(3 answers)
Closed 6 years ago.
I have two class .
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
I have data for above class , please check below .
List<Employee> lstEmployee = new List<Employee>();
lstEmployee.Add(new Employee() { FirstName = "Ronak", LastName = "Patel" });
lstEmployee.Add(new Employee() { FirstName = "Ronak", LastName = "Patel" });
lstEmployee.Add(new Employee() { FirstName = "Sanjay", LastName = "Patel" });
lstEmployee.Add(new Employee() { FirstName = "Ronak", LastName = "Patel" });
lstEmployee.Add(new Employee() { FirstName = "Sanjay", LastName = "Patel" });
lstEmployee.Add(new Employee() { FirstName = "Ronak", LastName = "Patel" });
lstEmployee.Add(new Employee() { FirstName = "Mohan", LastName = "Patel" });
I want filter above generic list and bound into below structure class .
class CountEmployeeName
{
public string FirstName { get; set; }
public int CountFirstName { get; set; }
}
I want bind CountEmployeeName class object as per above data like below sample .
FirstName CountFirstName
Ronak 4 (Ronak is 4 time in above Employee class list)
Sanjay 2 (Sanjay is 4 time in above Employee class list)
Mohan 1 (Mohan is 4 time in above Employee class list)
I want to bind Generic List of CountEmployeeName class like above output as per given data for Employee List .
Use linq group by and then in the select project to your second type:
from item in lstEmployee
group item by item.FirstName into grouping
select new CountEmployeeName
{
FirstName = grouping.Key,
CountFirstName = grouping.Count()
}
In method syntax it will look like this:
lstEmployee.GroupBy(item => item.FirstName)
.Select(grouping => new CountEmployeeName {
FirstName = grouping.Key,
CountFirstName = grouping.Count()
});
Note that I'd recommend changing the CountFirstName to an int rather than string
I got this application that reads the employee data from the database, only problem the boss of an employee is set in employee_id and not in name, which is what I am looking for.
I got a List filled with all the employees. So I am looking for a way to query it with LINQ through the list to link the employee_id to the Firstname/Lastname.
Example: The employee with the ID 1 is Nancy and her boss is Andrew, but it doesn't say Andrew it says 2. But it should say Andrew
I also added 2 images below to add to my explanation.
Listview of the employees
Reading out the list of employees into the ListView
First, load the employees into some local variable (you'll need it later):
List<Employee> employees = Database.getEmployees();
Then edit your foreach cycle:
// 'employee' is better because here it is really just one specific employee
foreach (Employee employee in employees)
Now you can get the name of the boss like this (in foreach cycle):
string boss = employees.FirstOrDefault(x => x.ID == employee.ReportsTo)?.FirstName;
(You need at least C# 6.0 for the ? operator.)
So you need to Left Join ID with Boss and get the Boss Info if found:
var employees = Database.getEmployees();
var employeesWithBoss = (from e in employees
join b in employees
on e.ID equals b.Boss into leftJoin
from boss in leftJoin.DefaultIfEmpty()
select new
{
Employee = e,
BossFirstName = boss == null ? null : boss.FirstName,
BossLastName = boss == null ? null : boss.LastName
}).ToList();
foreach (var employee in employeesWithBoss)
{
// do your normal work here, you now
// have employee.BossFirstName and employee.BossLastName
}
you can use lambda to achieve this.
class Program
{
static void Main(string[] args)
{
List<Employee> employees = new List<Employee>();
employees.Add(new Employee() { EmployeeName = "Nancy", EmployeeId = 1, BossId = 2 });
employees.Add(new Employee() { EmployeeName = "Andrew", EmployeeId = 2, BossId = 0 });
employees.Add(new Employee() { EmployeeName = "Janet", EmployeeId = 1, BossId = 2 });
var employeesWithBossName = employees.Join(employees,
emp1 => emp1.BossId,
emp2 => emp2.EmployeeId,
(emp1, emp2) => new { EmployeeName = emp1.EmployeeName, BossName = emp2.EmployeeName });
foreach (var item in employeesWithBossName)
{
Console.WriteLine("{0} {1}", item.EmployeeName, item.BossName);
}
Console.Read();
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int BossId { get; set; }
}
Hope this will help.
I have a list of orders and need to create groups of similar orders in an efficient manner (preferably without pulling back all orders and comparing manually).
Each order has a UserId and Email Address with additional fields of Postcode and Country.
So I need to create groups on the following rules:
If an order has the same (UserId or Email Address) and (postcode and country) place in a group
Given the following entity and data
public class Order
{
public int UserId { get; set; }
public int OrderId { get; set; }
public string Email { get; set; }
public int PostCode { get; set; }
public string Country { get; set; }
}
Example data
OrderId UserId Email PostCode Country
1 1 blah1 111 au
2 1 blah2 111 au
3 2 blah1 111 au
4 2 blah2 111 au
5 3 blah3 111 nz
6 3 blah3 111 nz
Example Results
Group 1
1 1 blah1 111 au
2 1 blah2 111 au
3 2 blah1 111 au
4 2 blah2 111 au
Group 2
5 3 blah3 111 nz
6 3 blah3 111 nz
The only way I can seem to think of doing this through manual iteration in memory
Is this possible to do with Linq to entities cleanly?
Update
After researching this for a while, I think I've come to the conclusion the only way to achieve what I want is to do two groupbys and manually combine them in memory.
Update 2
Thinking about this logically there is seemingly no elegant solution for this problem in linq and may need to be done with SQL and CTEs or some other recursive solution.
Here I have done Group by on more than 1 column,
OrderViewModel OrderList =from ord in order
group ord by new
{
ord.PostCode,
ord.Country,
ord.UserID,
ord.Email,
ord.OrderID
} into gr
select new OrderViewModel
{
OrderID = gr.Key.OrderID,
UserID = gr.Key.UserID,
Email = gr.Key.Email,
PostCode=gr.key.PostCode,
Country=gr.key.Country,
OrderList= gr.ToList()
};
where OrderViewModel is something ::
public class Order
{
public int UserId { get; set; }
public int OrderId { get; set; }
public string Email { get; set; }
public int PostCode { get; set; }
public string Country { get; set; }
public List<Order> OrderList { get; set; }
}
Where you should have to decide Priority of Grouping data,
&Data which you don't want to Group will be taken as a list.
Try this:-
var query = from ord in orders
group ord by new { ord.Country, ord.PostCode } into g
select new
{
Country = g.Key.Country,
PostCode = g.Key.PostCode,
Orders = g.GroupBy(x => x.OrderId)
.GroupBy(i => i.Count() > 1 ?
new { OrderID = i.Key, Email = default(string) }
: new { OrderID = default(int), i.First().Email })
.Select(o => o.Count() == 1 ?
new { o.Key, Orders = o.First().ToList() }
: new { o.Key, Orders = o.Select(z => z.First()).ToList() })
};
Here is the complete Working Fiddle.
Assuming your Order Class will be like below:
public class Order
{
public int UserId { get; set; }
public int OrderId { get; set; }
public string Email { get; set; }
public int PostCode { get; set; }
public string Country { get; set; }
}
Assuming and Grouping the List of Orders Like below:
public List<List<Order>> grouping()
{
// List of Orders to Group
List<Order> orderList = new List<Order>();
orderList.Add(new Order { UserId = 1, OrderId = 2007, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 2, OrderId = 2007, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 3, OrderId = 2007, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 4, OrderId = 2008, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 5, OrderId = 2008, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 6, OrderId = 2001, Email = "blah1#test.com", PostCode = 111, Country = "India" });
// Grouping
var groupedOrderList = orderList
.GroupBy(u => u.OrderId)
.Select(grp => grp.ToList()).ToList(); // Groping the Records based on the OrderId
return groupedOrderList; // The Result will be List<List<Order>>
}
Your group statement will group by OrderId. The Result will be come as you expected like above you showed.
Update:
You can Add additional fields as well to be grouped based on the multiple fields.
Like below :
.GroupBy(u => u.OrderId & u.UserId)
I want to orderby on a List like:
public List<Person> personList { get; set; };
class Person
{
public string Name{ get; set; };
public List<Car> carList{ get; set; };
}
class Car
{
public string Name { get; set; };
public int PriceInMillions { get; set; };
}
I want to sort CarList of each PersonList such that CarList has BMW object(can be one or more) at top of list?
Input:
Person: Name: 'ABC'
List<Car>:{{"x",25},{"y",25},{"BMW",26},{"x",25},{"BMW",25}}
Person: Name: 'DEF'
List<Car>:{{"BMW",21},{"y",25},{"BMW",26},{"x",25},{"BMW",20}}
Required Output:
Person: Name: 'ABC'
List<Car>:{{"BMW",25},{"BMW",26},{"x",25},{"y",25},{"x",25}}
Person: Name: 'DEF'
List<Car>:{{"BMW",20},{"BMW",21},{"BMW",26},{"y",25},{"x",25}}
I tried to find the index of "BMW" and swap it with first index but it is not working.
List<Car> orderedCarList = person.carList.OrderBy(s => (s.Name == "BMW") ? 0 : 1).ToList();
person.carList = orderedCarList;
If you want to sort in-place, without creating a new list or cars for each person, you can do
foreach(Person p in personList)
{
p.carList.Sort((car1, car2) =>
-(car1.Name == "BMW").CompareTo(car2.name == "BMW"));
}
Here is working solution:
public List<Person> GetSortedList(List<Person> personList)
{
foreach (var person in personList)
{
person.CarList=person.CarList.OrderByDescending(x => x.Name== "BMW").ToList();
}
return personList;
}