I have two Table one is Target and another is Table sales
Table Target
+------+--------------+--------+
| ID |Category Name | Target |
+------+--------------+--------+
| 1 | AAAA | 15000 |
| 2 | BBBB | 50000 |
| 3 | CCCC | 20000 |
| 4 | DDDD | 40000 |
| 5 | EEEE | 30000 |
+------+--------------+--------+
Table Sales
+------+---------+--------------+--------+
| ID | Date |Category Name | Sale |
+------+---------+--------------+--------+
| 1 | 01/01/20| AAAA | 5000 |
| 2 | 01/01/20| BBBB | 1000 |
| 3 | 02/01/20| CCCC | 2000 |
| 4 | 03/01/20| DDDD | 4000 |
| 5 | 03/01/20| EEEE | 3000 |
| 6 | 03/01/20| AAAA | 1000 |
| 7 | 05/01/20| EEEE | 3000 |
| 8 | 06/01/20| BBBB | 3000 |
| 9 | 02/01/20| CCCC | 1000 |
| 10 | 02/01/20| DDDD | 2000 |
+------+---------|--------------+--------+
I want to create following report from above table. How to create this report by devexpress.
+----------+--------------+------+
| Target |Category Name | Sale |
+----------+--------------+------+
| 15000 | AAAA | 6000 |
| 50000 | BBBB | 4000 |
| 20000 | CCCC | 3000 |
| 40000 | DDDD | 6000 |
| 30000 | EEEE | 6000 |
+----------+--------------+------+
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable targetTable = new DataTable();
targetTable.Columns.Add("ID", typeof(int));
targetTable.Columns.Add("Category Name", typeof(string));
targetTable.Columns.Add("Target", typeof(int));
targetTable.Rows.Add(new object[] { 1, "AAAA", 150000 });
targetTable.Rows.Add(new object[] { 2, "BBBB", 500000 });
targetTable.Rows.Add(new object[] { 3, "CCCC", 20000 });
targetTable.Rows.Add(new object[] { 4, "DDDD", 40000 });
targetTable.Rows.Add(new object[] { 5, "EEEE", 30000 });
DataTable salesTable = new DataTable();
salesTable.Columns.Add("ID", typeof(int));
salesTable.Columns.Add("Date", typeof(DateTime));
salesTable.Columns.Add("Category Name", typeof(string));
salesTable.Columns.Add("Sale", typeof(int));
salesTable.Rows.Add(new object[] { 1, DateTime.Parse("01/01/20"), "AAAA", 5000 });
salesTable.Rows.Add(new object[] { 2, DateTime.Parse("01/01/20"), "BBBB", 1000 });
salesTable.Rows.Add(new object[] { 3, DateTime.Parse("02/01/20"), "CCCC", 2000 });
salesTable.Rows.Add(new object[] { 4, DateTime.Parse("03/01/20"), "DDDD", 4000 });
salesTable.Rows.Add(new object[] { 5, DateTime.Parse("03/01/20"), "EEEE", 3000 });
salesTable.Rows.Add(new object[] { 6, DateTime.Parse("03/01/20"), "AAAA", 1000 });
salesTable.Rows.Add(new object[] { 7, DateTime.Parse("05/01/20"), "EEEE", 3000 });
salesTable.Rows.Add(new object[] { 8, DateTime.Parse("06/01/20"), "BBBB", 3000 });
salesTable.Rows.Add(new object[] { 9, DateTime.Parse("02/01/20"), "CCCC", 1000 });
salesTable.Rows.Add(new object[] { 10, DateTime.Parse("02/01/20"), "DDDD", 2000 });
DataTable table = new DataTable();
table.Columns.Add("Target", typeof(int));
table.Columns.Add("Category Name", typeof(string));
table.Columns.Add("Sale", typeof(int));
var joins = from t in targetTable.AsEnumerable()
join s in salesTable.AsEnumerable() on t.Field<string>("Category Name") equals s.Field<string>("Category Name")
select new { t = t, s = s};
var groups = joins.GroupBy(x => x.t.Field<string>("Category Name"));
foreach (var group in groups)
{
table.Rows.Add(new object[] { group.First().t.Field<int>("Target"), group.Key, group.Sum(x => x.s.Field<int>("Sale")) });
}
}
}
}
You may follow the tutorial how to create a devexpress report
and when you will create a query for the sql datasource, specify a grouping and aggregate the Target column using Sum function like
select Target.Target, sum(Sales.Sale)
from Target
join Sales ...
group by Target.Target
And you can Also create two queries for the report to produce the master-detail report layout, group & sum your Targets using the reporting summary mechanism so it will give you possibility to add a drill down feature to your report to view detailed reports.
Related
I have DataTable mocked-up below:
+----+------+-------------+--------+
| ID | YEAR | PERSON_NAME | AMOUNT |
+----+------+-------------+--------+
| 1 | 2004 | BARBARA | 500 |
| 2 | 2004 | BOB | 100 |
| 3 | 2004 | JANE | 30 |
| 4 | 2004 | JOHN | 200 |
| 5 | 2005 | BARBARA | 505 |
| 6 | 2005 | BOB | 150 |
| 7 | 2005 | JANE | 15 |
| 8 | 2005 | JOHN | 215 |
| 10 | 2006 | BARBARA | 523 |
| 11 | 2006 | BOB | 185 |
| 12 | 2006 | JANE | 25 |
| 13 | 2006 | JOHN | 207 |
+----+------+-------------+--------+
I am trying to add a new column that will track the year-over-year change of the amounts of each person:
+----+------+-------------+--------+-------+
| ID | YEAR | PERSON_NAME | AMOUNT | Y-O-Y |
+----+------+-------------+--------+-------+
| 1 | 2004 | BARBARA | 500 | |
| 2 | 2004 | BOB | 100 | |
| 3 | 2004 | JANE | 30 | |
| 4 | 2004 | JOHN | 200 | |
| 5 | 2005 | BARBARA | 505 | 5 |
| 6 | 2005 | BOB | 150 | 50 |
| 7 | 2005 | JANE | 15 | -15 |
| 8 | 2005 | JOHN | 215 | 15 |
| 10 | 2006 | BARBARA | 523 | 18 |
| 11 | 2006 | BOB | 185 | 35 |
| 12 | 2006 | JANE | 25 | 10 |
| 13 | 2006 | JOHN | 207 | -8 |
+----+------+-------------+--------+-------+
I've achieved this easily in SQL by joining the table to itself with some ON conditions, and was trying to mimic the same logic to c# DataTable and got it to somehow work in a convoluted way. I was wondering if there is a cleaner way with LINQ or DataViews or just a compact algorithm to achieve the same effect. Thanks!
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("YEAR", typeof(int));
dt.Columns.Add("PERSON_NAME", typeof(string));
dt.Columns.Add("AMOUNT", typeof(int));
dt.Rows.Add(new object[] { 1, 2004, "BARBARA", 500 });
dt.Rows.Add(new object[] { 2, 2004, "BOB", 100 });
dt.Rows.Add(new object[] { 3, 2004, "JANE", 30 });
dt.Rows.Add(new object[] { 4, 2004, "JOHN", 200 });
dt.Rows.Add(new object[] { 5, 2005, "BARBARA", 505 });
dt.Rows.Add(new object[] { 6, 2005, "BOB", 150 });
dt.Rows.Add(new object[] { 7, 2005, "JANE", 15 });
dt.Rows.Add(new object[] { 8, 2005, "JOHN", 215 });
dt.Rows.Add(new object[] { 10, 2006, "BARBARA", 523 });
dt.Rows.Add(new object[] { 11, 2006, "BOB", 185 });
dt.Rows.Add(new object[] { 12, 2006, "JANE", 25 });
dt.Rows.Add(new object[] { 13, 2006, "JOHN", 207 });
dt.Columns.Add("Y-O-Y", typeof(int));
List<List<DataRow>> groups = dt.AsEnumerable()
.OrderBy(x => x.Field<int>("YEAR"))
.GroupBy(x => x.Field<string>("PERSON_NAME"))
.Select(x => x.ToList())
.ToList();
foreach (List<DataRow> person in groups)
{
for (int i = 1; i < person.Count(); i++)
{
person[i]["Y-O-Y"] = person[i].Field<int>("AMOUNT") - person[i - 1].Field<int>("AMOUNT");
//or
//person[i]["Y-O-Y"] = (int)person[i]["AMOUNT"] - (int)person[i - 1]["AMOUNT"];
}
}
}
}
}
This question already has answers here:
Is it possible to Pivot data using LINQ?
(7 answers)
Closed 3 years ago.
I received a request to export data from my asp.net mvc project using linq to an excel spreadsheet. Usually this is an easy task, however, in this scenario the person requesting the data would like the export from example or list A to look like example B
Example A (current export)
Id | CustomerNum | CustomerName | FruitName | Charge
____________________________________________________
1 | 1026 | Bob | Banana | 3.00
2 | 1032 | Jill | Apple | 2.00
3 | 1026 | Bob | Apple | 3.00
4 | 1144 | Marvin | Banana | 1.00
5 | 1753 | Sam | Pear | 4.00
6 | 1026 | Bob | Banana | 3.00
Example B (requested export format)
Id | CustomerNum | CustomerName | Banana | Apple | Pear
_________________________________________________________
1 | 1026 | Bob | 6.00 | 3.00 |
2 | 1032 | Jill | 0 | 2.00 |
3 | 1144 | Marvin | 1.00 | 0 |
5 | 1753 | Sam | 0 | 0 | 4.00
I have never seen where distinct row values were used as columns. How should I go about this?
Create a pivot table :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("CustomerNum", typeof(int));
dt.Columns.Add("CustomerName", typeof(string));
dt.Columns.Add("FruitName", typeof(string));
dt.Columns.Add("Charge", typeof(decimal));
dt.Rows.Add(new object[] {1,1026, "Bob", "Banana", 3.00});
dt.Rows.Add(new object[] {2,1032, "Jill", "Apple", 2.00});
dt.Rows.Add(new object[] {3,1026, "Bob", "Apple", 3.00});
dt.Rows.Add(new object[] {4,1144, "Marvin", "Banana", 1.00});
dt.Rows.Add(new object[] {5,1753, "Sam", "Pear", 4.00});
dt.Rows.Add(new object[] {6,1026, "Bob", "Banana", 3.00});
string[] fruits = dt.AsEnumerable().Select(x => x.Field<string>("FruitName")).Distinct().OrderBy(x => x).ToArray();
DataTable pivot = new DataTable();
pivot.Columns.Add("CustomerNum", typeof(int));
pivot.Columns.Add("CustomerName", typeof(string));
foreach (string fruit in fruits)
{
pivot.Columns.Add(fruit, typeof(decimal));
}
var groups = dt.AsEnumerable().GroupBy(x => x.Field<int>("CustomerNum"));
foreach (var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["CustomerNum"] = group.Key;
newRow["CustomerName"] = group.First().Field<string>("CustomerName");
foreach (DataRow row in group)
{
string fruitName = row.Field<string>("FruitName");
decimal oldvalue = (newRow[fruitName] == DBNull.Value) ? 0 : (decimal)newRow[fruitName];
newRow[fruitName] = oldvalue + row.Field<decimal>("Charge");
}
}
}
}
}
I am having trouble selecting the first item in a list that is unique based on two fields, JOB_ID and EMPLOYEE_ID.
Each job should only be assigned to one employee (the one with the lowest OVERALL_SCORE), then move on and assign the next employee.
The List Objects are as follows:
JobMatch.cs
public int JOB_ID { get; set; }
public int JOB_MATCHES_COUNT { get; set; }
EmployeeMatch.cs
public int EMPLOYEE_ID { get; set; }
public int EMPLOYEE_MATCHES_COUNT { get; set; }
Rankings.cs
public int JOB_ID { get; set; }
public int EMPLOYEE_ID { get; set; }
public int TRAVEL_TIME_MINUTES { get; set; }
public int PRIORITY { get; set; }
public int OVERALL_SCORE { get; set; }
Rankings.cs gets an overall score based on the travel time field and
number of matches an Employee/Job has.
EmployeeMatch.cs
+-------------+-------------------+
| EMPLOYEE_ID | EMP_MATCHES_COUNT |
+-------------+-------------------+
| 3 | 1 |
| 4 | 1 |
| 2 | 3 |
| 1 | 4 |
+-------------+-------------------+
JobMatch.cs
+--------+-------------------+
| JOB_ID | JOB_MATCHES_COUNT |
+--------+-------------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 2 |
| 4 | 4 |
+--------+-------------------+
Ranking.cs (shortened as to not fill the screen)
+--------+-------------+---------------+
| JOB_ID | EMPLOYEE_ID | OVERALL_SCORE |
+--------+-------------+---------------+
| 4 | 3 | 800 |
| 4 | 4 | 800 |
| 3 | 1 | 800 |
| 3 | 2 | 1200 |
| 2 | 1 | 1600 |
| 2 | 2 | 1800 |
| 4 | 1 | 2000 |
| 4 | 2 | 2100 |
| 1 | 1 | 6400 |
+--------+-------------+---------------+
Basically, the idea is to select the first unique Employee and Job in this list and then the best matches will be put into a separate list, something like the following for the above scenario:
+--------+-------------+---------------+
| JOB_ID | EMPLOYEE_ID | OVERALL_SCORE |
+--------+-------------+---------------+
| 4 | 3 | 800 |
| 3 | 1 | 800 |
| 2 | 2 | 1800 |
+--------+-------------+---------------+
I tried the following but it didn't work as intended:
var FirstOrder = (rankings.GroupBy(u => u.JOB_ID)
.Select(g => g.First())).ToList();
var SecondOrder = (FirstOrder.GroupBy(u => u.EMPLOYEE_ID)
.Select(g => g.First())).ToList();
The idea is choosing first element and then removing corresponding elements from list to make sure next choice is unique, as below:
var rankings = new List<Rankings> {
new Rankings{ JOB_ID= 4,EMPLOYEE_ID= 3, OVERALL_SCORE= 800 },
new Rankings{ JOB_ID= 4,EMPLOYEE_ID= 4, OVERALL_SCORE= 800 },
new Rankings{ JOB_ID= 3,EMPLOYEE_ID= 1, OVERALL_SCORE= 800 },
new Rankings{ JOB_ID= 3,EMPLOYEE_ID= 2, OVERALL_SCORE= 1200 },
new Rankings{ JOB_ID= 2,EMPLOYEE_ID= 1, OVERALL_SCORE= 1600 },
new Rankings{ JOB_ID= 2,EMPLOYEE_ID= 2, OVERALL_SCORE= 1800 },
new Rankings{ JOB_ID= 4,EMPLOYEE_ID= 1, OVERALL_SCORE= 2000 },
new Rankings{ JOB_ID= 4,EMPLOYEE_ID= 2, OVERALL_SCORE= 2100 },
new Rankings{ JOB_ID= 1,EMPLOYEE_ID= 1, OVERALL_SCORE= 6400 },
};
var cpy = new List<Rankings>(rankings);
var result = new List<Rankings>();
while (cpy.Count() > 0)
{
var first = cpy.First();
result.Add(first);
cpy.RemoveAll(r => r.EMPLOYEE_ID == first.EMPLOYEE_ID || r.JOB_ID == first.JOB_ID);
}
result:
+--------+-------------+---------------+
| JOB_ID | EMPLOYEE_ID | OVERALL_SCORE |
+--------+-------------+---------------+
| 4 | 3 | 800 |
| 3 | 1 | 800 |
| 2 | 2 | 1800 |
+--------+-------------+---------------+
Really, if you're trying to get the best score for the job, you don't need to select by unique JOB_ID/EMPLOYEE_ID, you need to sort by JOB_ID/OVERALL_SCORE, and pick out the first matching employee per JOB_ID (that's not already in the "assigned list").
You could get the items in order using LINQ:
var sorted = new List<Ranking>
(
rankings
.OrderBy( r => r.JOB_ID )
.ThenBy( r => r.OVERALL_SCORE )
);
...and then peel off the employees you want...
var best = new List<Ranking>( );
sorted.ForEach( r1 =>
{
if ( !best.Any
(
r2 =>
r1.JOB_ID == r2.JOB_ID
||
r1.EMPLOYEE_ID == r2.EMPLOYEE_ID
) )
{
best.Add( r1 );
}
} );
Instead of using Linq to produce a sorted list, you could implement IComparable<Ranking> on Ranking and then just sort your rankings:
public class Ranking : IComparable<Ranking>
{
int IComparable<Ranking>.CompareTo( Ranking other )
{
var jobFirst = this.JOB_ID.CompareTo( other.JOB_ID );
return
jobFirst == 0?
this.OVERALL_SCORE.CompareTo( other.OVERALL_SCORE ):
jobFirst;
}
//--> other stuff...
}
Then, when you Sort() the Rankings, they'll be in JOB_ID/OVERALL_SCORE order. Implementing IComparable<Ranking> is probably faster and uses less memory.
Note that you have issues...maybe an unstated objective. Is it more important to fill the most jobs...or is more important to find work for the most employees? The route I took does what you suggest, and just take the best employee for the job as you go...but, maybe, the only employee for job 2 may be the same as the best employee for job 1...and if you put him/her on job 1, you might not have anybody left for job 2. It could get complicated :-)
Basically you could use System.Linq.Distinct method reinforced with the custom equality comparer IEqualityComparer<Ranking>. The System.Linq provide this method out of the box.
public class Comparer : IEqualityComparer<Ranking>
{
public bool Equals(Ranking l, Ranking r)
{
return l.JOB_ID == r.JOB_ID || l.EMPLOYEE_ID == r.EMPLOYEE_ID;
}
public int GetHashCode(Ranking obj)
{
return 1;
}
}
The trick here is with the GetHashCode method, and then as simple as this
rankings.Distinct(new Comparer())
I have a Branch List, each one has a Number N of employees, I have a Branch object and a NumberEmployees property, now I need to iterate over that list sending the number of employees per block, I explain better with the following table: I order the List by Number of Employees, so far no problem.
+---------+-----------+
| Branch | Employees |
+---------+-----------+
|MEXICO | 800 |
|USA | 700 |
|INDIA | 500 |
|CHINA | 400 |
|AUSTRALIA| 300 |
+---------+-----------+
Now iterate through a list but dividing the number of employees into blocks something like this:
+-----------+------------+-------------+------------+
| Branch | FirstGroup | SecondGroup | ThirdGroup |
+-----------+------------+-------------+------------+
| Mexico | 267 | 267 | 267 |
| USA | 234 | 234 | 234 |
| India | 167 | 167 | 167 |
| China | 134 | 134 | 134 |
| Australia | 100 | 100 | 100 |
+-----------+------------+-------------+------------+
In the end I think the list that should result would be:
+-----------+-----------+
| Branch | Employees |
+-----------+-----------+
| Mexico | 267 |
| USA | 234 |
| India | 167 |
| China | 134 |
| Australia | 100 |
| Mexico | 267 |
| USA | 234 |
| India | 167 |
| China | 134 |
| Australia | 100 |
| Mexico | 267 |
| USA | 234 |
| India | 167 |
| China | 134 |
| Australia | 100 |
+-----------+-----------+
So far I can only order the List.
double TotalEmployees = ListBranch.Sum(item => item.EmployeeNumber);
double blockSize = TotalEmployees / ListBranch.Count();
double sizeQuery = Math.Ceiling(blockSize);
foreach (Branch branch in ListBranch.OrderByDescending(f => f. EmployeeNumber))
{
//to do
}
I appreciate your valuable help for any clues you can give me
This might do the trick for you
List<BranchEmployee> be = new List<BranchEmployee>();
be.Add(new BranchEmployee() { Branch = "MEXICO", Employee = 800 });
be.Add(new BranchEmployee() { Branch = "USA", Employee = 700 });
be.Add(new BranchEmployee() { Branch = "INDIA", Employee = 500 });
be.Add(new BranchEmployee() { Branch = "CHINA", Employee = 400 });
be.Add(new BranchEmployee() { Branch = "AUSTRALIA", Employee = 300 });
List<BranchEmployee> ExpectedBE = new List<BranchEmployee>();
for(int i = 0; i <= 2; i++)
{
foreach(BranchEmployee smbe in be)
{
ExpectedBE.Add(new BranchEmployee()
{
Branch = smbe.Branch,
Employee = smbe.Employee / 3
});
}
}
What I see is that every group has equal number of employees that is the total number of employees divided by 3.
To look the data in the way you have shown I have created a class like this
public class BranchEmployee
{
public string Branch { get; set; }
public int Employee { get; set; }
}
I am working on a small project using C# and EF5.0 and I need to group some data. Let say I have table of columns in a building like shown below.
+----------+--------Columns Table--+------+------+
| ColumnID |ColumnName|Width|Length|Height|number|
+----------+----------+-----+------+------+------+
| 1 | C101 | 50 | 70 | 250 | 1 |
| 2 | C102 | 70 | 70 | 250 | 1 |
| 3 | C103 | 70 | 60 | 250 | 1 |
| 4 | C104 | 90 | 70 | 250 | 1 |
| 5 | C105 | 40 | 50 | 250 | 1 |
| 6 | C106 | 50 | 70 | 250 | 1 |
| 7 | C107 | 50 | 60 | 250 | 1 |
| 8 | C108 | 70 | 70 | 250 | 1 |
+----------+----------+-----+------+------+------+
I need a C# code to see the above data groupped like this:
+----------+---Groupped Columns Table-----+------+
|G_ColumnID|ColumnName|Width|Length|Height|number|
+----------+----------+-----+------+------+------+
| 1 |C(101-106)| 50 | 70 | 250 | 2 |
| 2 |C(102-108)| 70 | 70 | 250 | 2 |
| 3 | C103 | 70 | 60 | 250 | 1 |
| 4 | C104 | 90 | 70 | 250 | 1 |
| 5 | C105 | 40 | 50 | 250 | 1 |
| 6 | C107 | 50 | 60 | 250 | 1 |
+----------+----------+-----+------+------+------+
I prefer clues than the exact solution.
EDIT : Below code shows my current state. I think I can find the columns with the same Height, Width and Length using this code. But I am not sure how to generate a new name for the group.
using (pehlivanEntities context = new pehlivanEntities())
{
foreach (var item in context.table1)
{
int id = item.ColumnID;
foreach (var item2 in context.table1)
{
int id2 = item2.ColumnID;
if (id != id2)
{
if (item.Width == item2.Width)
{
if (item.Length == item2.Length)
{
if (item.Height == item2.Height)
{
//Alter item.ColumnName
//increase item.number by one
//Remove item2
}
}
}
}
}
}
}
Well you'd start with grouping on a composite key:
var groups = myData.GroupBy(d => new{d.Width, d.Length, d.Height})
then
groups
.Select(g => new {
g.Key.Width,
g.Key.Length,
g.Key.Height,
columnNames = g.Select(x => x.ColumnName),
number = g.Count()})
then a bit of string manipulation on the columnNames field