how to group data in Linq with date - c#

Im using Linq and having 2 tables which store the record as following
OrderMasterTable
Order_ID |Place_Date |ExpectedDelivery_Date |IsDelivered
==========|=============|=======================|============
1 |1993-02-19 | 1993-02-20 00:01:00 | True
2 |1993-02-20 | 1993-02-20 00:01:00 | True
3 |1993-02-21 | 1993-02-22 00:01:00 | True
4 |1993-02-22 | 1993-02-23 00:01:00 | False
OrderAssignTable
Order_ID |Delivered_By |Delivery_Date
==========|=============|=======================
1 |User123 | 1993-02-20 00:01:00
2 |UserXyz | 1993-02-20 00:01:01
3 |User345 | 1993-02-24 00:01:00
I want to group output data by delivered date in such a way that it appears in a following way:
Date: 1993-02-20,
OnTime: 1,
Delayed:1
Date: 1993-02-22,
OnTime :1,
Delayed: 0
* OnTime if deliveryDateTime is <= ExpectedTime
* Date is deliveryDate
pls,help me out

you can try this.
var query = from assign in OrderAssignTable
join master in OrderMasterTable on assign.Order_ID equals master.Order_ID
where master.IsDelivered == true
group new { assign, master } by assign.Delivery_Date.Date into g
select new
{
Date = g.Key,
OnTime = g.Count(i => i.assign.Delivery_Date <= i.master.ExpectedDelivery_Date),
Delayed = g.Count(i => i.assign.Delivery_Date > i.master.ExpectedDelivery_Date)
};

That should do:
var result = Orders
.Join(Assigns, o => o.OrderId, a => a.OrderId, (o, a) => new {Order = o, Assign = a})
.GroupBy(o => o.Order.ExpectedDeliveryDate.Date)
.Select(g => new
{
Date = g.Key,
OnTime = g.Count(o => o.Assign.DeliveryDate <= o.Order.ExpectedDeliveryDate),
Delayed = g.Count(o => o.Assign.DeliveryDate > o.Order.ExpectedDeliveryDate)
})
.ToArray();

Related

Rewriting this SQL in Lambda - using count and group by

I'm stuck rewriting this SQL in Lambda:
SELECT TOP 1000 COUNT(LoginDateTime)
,[LoginDateTime]
,[Culture]
FROM [LearningApp].[dbo].[UserActivities]
group by LoginDateTime, Culture
Result:
+-----+---------------------------+----------+
| | LoginDateTime | Culture |
+-----+---------------------------+----------+
| 1 | 2016-07-14 12:21:23.307 | de |
| 4 | 2016-07-13 12:21:23.307 | en |
| 2 | 2016-07-14 12:21:23.307 | en |
+-----+---------------------------+----------+
And my code:
public List<UserActivityResponseContract> GetUserActivity()
{
var userActivityResponseContracts = new List<UserActivityResponseContract>();
var userActivitiesList = _baseCommands.GetAll<UserActivity>()
.Select(x => new
{
x.LoginDateTime,
x.Culture
})
.GroupBy(x => new { x.LoginDateTime, x.Culture});
foreach (var userActivity in userActivitiesList)
{
userActivityResponseContracts.Add(new UserActivityResponseContract
{
ActivityDate = userActivity.Key.LoginDateTime.ToShortDateString(),
NumberOfTimesLoggedIn = userActivity.Count(),
Culture = userActivity.Key.Culture
});
}
return userActivityResponseContracts;
}
It doesn't seem very difficult but I am a bit stuck.
Method Syntax:
var result = _baseCommands.GetAll<UserActivity>()
.GroupBy(x => new { x.LoginDateTime, x.Culture})
.Select (x => new UserActivityResponseContract
{
ActivityDate = x.Key.LoginDateTime.ToShortDateString(),
Culture = x.Key.Culture,
NumberOfTimesLoggedIn = x.Count()
})
.Take(1000).ToList();
You can also use an overload of GroupBy that enables you to pass the select function as a second parameter
Query Syntax:
var result = (from x in _baseCommands.GetAll<UserActivity>()
group x by new { x.LoginDateTime, x.Culture} into g
select new UserActivityResponseContract
{
ActivityDate = g.Key.LoginDateTime.ToShortDateString(),
Culture = g.Key.Culture,
NumberOfTimesLoggedIn = g.Count()
}).Take(1000).ToList();
To GroupBy just the Date part of this DateTime do: x.LoginDateTime.Date

How to find the min dates from the list of items?

I have a table structure like this
id | itemId | date |
1 | a1 | 6/14/2015
2 | a1 | 3/14/2015
3 | a1 | 2/14/2015
4 | b1 | 6/14/2015
5 | c1 | 6/14/2015
From this table structure I am trying to get all the distinct items that has min date. for e.g. I am trying to get id = 3,4 and 5.
I have tried following code but I couldn't
var items = (from i in _db.Items
//where min(i.date) // doesn't seem right
group i by i.itemID
into d select new
{
iId = d.Key,
}).Distinct();
Given your sample data, I would do this:
var query =
from i in _db.Items
group i by i.itemId into gis
let lookup = gis.ToLookup(x => x.date, x => x.id)
from x in lookup[gis.Min(y => y.date)]
select x;
var items = from i in _db.Items
group i.date by i.itemID
into d select new {
iId = d.Key, iDate = d.Min()
};

GROUP BY in datatable on two columns with sum of third column using LINQ

I am searching for a LINQ query for which input and output datatables are as below -
Name Code count
-------------------------
User1 q1 2
user1 q2 2
user2 q2 1
user2 q3 3
user1 q1 2
Name Code Count
-----------------------
User1 | q1 | 4
User1 | q2 | 2
User1 | q3 | 0
User2 | q1 | 0
User2 | q2 | 1
User2 | q3 | 3
i.e. I want sum of count for distinct set of codes for each Name, in result table, it showing (user1, q3,0) because there is no record of q3 for user1 in input datatable, Please help in this regard, Thanks again
This should work:
var query = table.AsEnumerable()
.GroupBy(row => new { Name = row.Field<string>("Name"), Code = row.Field<string>("Code") });
var table2 = table.Clone(); // empty table with same schema
foreach (var x in query)
{
string name = x.Key.Name;
string code = x.Key.Code;
int count = x.Sum(r => r.Field<int>("Count"));
table2.Rows.Add(x.Key.Name, x.Key.Code, count);
}
Edit: if you instead want to modify the original table and sum the count per each name-code group, use this approach which uses a Dictionary as lookup:
var nameCodeCountLookup = table.AsEnumerable()
.GroupBy(row => new { Name = row.Field<string>("Name"), Code = row.Field<string>("Code") })
.ToDictionary(ncGrp => ncGrp.Key, ncGrp => ncGrp.Sum(r => r.Field<int>("Count")));
foreach (DataRow row in table.Rows)
{
string Name = row.Field<string>("Name");
string Code = row.Field<string>("Code");
row.SetField("Count", nameCodeCountLookup[new { Name, Code }]);
}
However, i don't know why your result table contains this row:
User1 | q3 | 0
There is no name-code combination User1+q3 in the original table.
I think here is what you looking for:
var result =
new DataTable().Rows.Cast<DataRow>()
.Select(p => new {User = p[0], Q = p[1], Value = (double) p[2]})
.GroupBy(p => new {p.User, p.Q})
.Select(p => new {User = p.Key.User, Q = p.Key.Q, Total = p.Sum(x => x.Value)})
.ToList();
Or almost same, but you can do it too:
var result =
new DataTable().Rows.Cast<DataRow>()
.Select(p => new
{
User = p.Field<string>("column1Name"),
Q = p.Field<string>("column2Name"),
Value = p.Field<double>("column3Name")
})
.GroupBy(p => new {p.User, p.Q})
.Select(p => new {User = p.Key.User, Q = p.Key.Q, Total = p.Sum(x => x.Value)})
.ToList();

Roll up totals of an ObservableCollection<MyType> with a Linq expression

I have a Silverlight delegate which gets an ObservableCollection in the EventArgs Result. The DTO myType has fields Order, StartDate, Status, PlannedAmount, ActualAmount, and few others. The query on the WCF service side gets several rows per Order, varying only by PlannedAmount and ActualAmount.
Order # | StartDate | PlannedAmount| ActualAmount | Order Comments ....
Order A | March 15 | 20.00 | 0.00 | Comment 1 ...
Order A | March 15 | 30.00 | 0.00 | Comment 1 ...
Order A | March 15 | 10.00 | 0.00 | Comment 1 ...
Order A | March 15 | 0.00 | 30.00 | Comment 1 ...
Order B | March 25 | 10.00 | 0 | Comment 2 ...
Order B | March 25 | 0.00 | 5.00 | Comment 2 ...
I would like to show only one row per Order and sum all the PlannedAmount and ActualAmount values. I prefer to change this in the presentation layer as I don't know what other consumers of the WCF Operation require. So I want to roll it up to...
Order # | StartDate | PlannedAmount| ActualAmount | Order Comments ....
Order A | March 15 | 60.00 | 30.00 | Comment 1 ...
Order B | March 25 | 10.00 | 5.00 | Comment 2 ...
And then make this an ObservableCollection of the same type that I had before.
I have tried this but I cannot seem to get anything but the Key and the Sum values.
var Orders =
from wo in OrdersPerOperation
group wo by wo.OrderNo
into g
select new
{
OrderNo = g.Key,
Planned = g.Sum(wo => wo.Planned),
Actual = g.Sum(wo => wo.Actual),
OrderComments = g.Select(wo => wo.Equipment),
StartDate = g.Select(wo => wo.StartDate),
Status = g.Select(wo => wo.Status),
OrderType = g.Select(wo => wo.OrderType) //,...
};
Edit
Getting just the key and two sums is straight-forward:
var Orders =
from wo in OrdersPerOperation
group wo by wo.OrderNo
into g
select new
{
OrderNo = g.Key,
Planned = g.Sum(wo => wo.Planned),
Actual = g.Sum(wo => wo.Actual)
}
The challenge is getting all of the other fields, which are repetitive to also show in the result.
EDIT
I made this guess that this might work like a self-referencing SQL query. I believe I am on the right track with this, in that each element has data in the correct format. I still cannot set the results to the ItemsSource of the Silverlight grid, which I could bind, before all of this Linq confusion. The IDE warns me that FirstOrDefault is a possible NullReferenceException.
var workOrders = from wo in workOrdersPerOperation
group wo by wo.OrderNo
into g
select new
{
OrderNo = g.Key,
Planned = g.Sum(wo => wo.Planned),
Actual = g.Sum(wo => wo.Actual),
g.FirstOrDefault(wo => wo.OrderNo == g.Key).Location,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).Equipment,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).StartDate,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).Status,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).OrderType,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).AccType,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).WorkCenter,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).Description,
g.FirstOrDefault(wo => wo.OrderNo == g.Key).Priority
};
Can anyone get me past this step? I still need to bind it to the control.
You want g.SelectMany(wo => wo.Equipment) instead.
I'm pretty sure that this
Location = g.FirstOrDefault(wo => wo.OrderNo == g.Key).Location,
Will produce the same result as this
Location = g.FirstOrDefault().Location,
Additionally, you only need to use FirstOrDefault if your collection might be empty. However, the group would not exist if there were no items in it. This allows you to simplify it down to this.
Location = g.First().Location,
You can make the final solution
var orders = from wo in ordersPerOperation
group wo by wo.OrderNo
into g
select new MyType
{
OrderNo = g.Key,
Planned = g.Sum(wo => wo.Planned),
Actual = g.Sum(wo => wo.Actual),
Location = g.First().Location,
Equipment = g.First().Equipment,
StartDate = g.First().StartDate,
Status = g.First().Status,
OrderType = g.First().OrderType,
AccType = g.First().AccType,
WorkCenter = g.First().WorkCenter,
Description = g.First().Description,
Priority = g.First().Priority
};
Try:
OrderComments = g.Select(wo => wo.Equipment).ToList()
I found that I could select into a collection of new instances of MyClass with initializers.
var orders = from wo in ordersPerOperation
group wo by wo.OrderNo
into g
select new MyType()
{
OrderNo = g.Key,
Planned = g.Sum(wo => wo.Planned),
Actual = g.Sum(wo => wo.Actual),
Location = g.FirstOrDefault(wo => wo.OrderNo == g.Key).Location,
Equipment = g.FirstOrDefault(wo => wo.OrderNo == g.Key).Equipment,
StartDate = g.FirstOrDefault(wo => wo.OrderNo == g.Key).StartDate,
Status = g.FirstOrDefault(wo => wo.OrderNo == g.Key).Status,
OrderType = g.FirstOrDefault(wo => wo.OrderNo == g.Key).OrderType,
AccType = g.FirstOrDefault(wo => wo.OrderNo == g.Key).AccType,
WorkCenter = g.FirstOrDefault(wo => wo.OrderNo == g.Key).WorkCenter,
Description = g.FirstOrDefault(wo => wo.OrderNo == g.Key).Description,
Priority = g.FirstOrDefault(wo => wo.OrderNo == g.Key).Priority
};
This is not an ObservableCollection, but it turns out that I don't need it be after all.

C# Group with conditional

how can i grouping a data with conditional if bill < 10 ?
i have table:
meetingId | bill
------------------
a | 6
b | 7
c | 1
a | 5
a | 3
b | 4
g | 2
expected results :
a = 6+5+3 = 14 limit is 10 --> 10 and 4
b = 7+4 = 11 so limit is 10 --> 10 and 1
c and g not over the limit.
meetingId | bill
------------------
a | 10
a | 4
b | 10
b | 1
c | 1
g | 2
i tried in SQL why but i stuck with if condition
my SQL :
SELECT NO_ORDRE
,ORDRE.CODE_CLIENT As CodeCl
,[CODE_DEST]
,ORDRE.RS_NOM As OrdreRS
,ORDRE.ADRESSE As OrdreAdr
,ORDRE.CP As OrdreCP
,ORDRE.VILLE As OrdreVille
,ENLEV_CREMB
,ENLEV_DECL
,MODAL_MODE
,[PAYS]
,[INSEE]
,[SIRET]
,ORDRE.TEL As OrdreTel
,ORDRE.FAX As OrdreFax
,[EMAIL]
,[NBR_COLIS]
,[POID]
,[OBS]
,[DATE_CREE]
,[DATE_MODIF]
,[REF_EXPED]
,[AUTRE_REF]
,[AGENCE]
,[TRANSPORTEUR]
,NOM
,CAPITAL
,LIBELLE
,T_LOGO.IMG As FaImg
,T_LOGO.ADRESSE As FaAdr
,T_LOGO.CP As FaCp
,T_LOGO.VILLE As FaVille
,T_LOGO.TEL As FaTel
,T_LOGO.FAX As FaFax
,FAWEB_CLIENT.RS_NOM As CliRsNom
,FAWEB_CLIENT.ADRESSE As CliAdr
,FAWEB_CLIENT.CP As CliCp
,FAWEB_CLIENT.VILLE As CliVille
FROM [ORDRE]
LEFT JOIN T_LOGO ON ORDRE.TRANSPORTEUR = T_LOGO.NOID
LEFT JOIN FAWEB_CLIENT ON ORDRE.CODE_CLIENT = FAWEB_CLIENT.CODE_CLIENT
WHERE (STATUT_ORDRE = 2) AND (TRANSPORTEUR IN (SELECT ParsedString From dbo.ParseStringList(#Trans)))
and then i use in C#
List<Pers_Ordre> oListOrdre = new List<Pers_Ordre>();
while (readerOne.Read())
{
Pers_Ordre oPerOrdr = new Pers_Ordre();
Pers_Ordre test = (from t in oListOrdre where t.DestId == readerOne["CODE_DEST"].ToString() select t).FirstOrDefault();
oPerOrdr.OrdreId = Convert.ToInt32(readerOne["NO_ORDRE"]);
oPerOrdr.DestId = readerOne["CODE_DEST"].ToString();
if (test == null)
{
oListOrdre.Add(oPerOrdr);
}
else
{
int NbrColis = (from t in oListOrdre where t.DestId == readerOne["CODE_DEST"].ToString() select t.NbrColis).FirstOrDefault();
if (NbrColis < 5)
{
test.NbrColis += NbrColis;
}
}
}
it not work what i expected.
Thanks for your help!
(Not really an answer, but this doesn't fit in a comment.)
Here's a LINQ-to-Objects query that groups items by meetingId and creates new items such that there is one item with bill less than 10 and as many items as needed with bill equalling 10 to keep the sum:
Is this what you're looking for?
Code:
var list = new List<Tuple<char, int>>
{
Tuple.Create('a', 6),
Tuple.Create('b', 7),
Tuple.Create('c', 1),
Tuple.Create('a', 5),
Tuple.Create('a', 3),
Tuple.Create('b', 4),
Tuple.Create('g', 2),
};
var result = list
.GroupBy(x => x.Item1)
.Select(g => new
{
Key = g.Key,
Sum = g.Sum(x => x.Item2)
})
.Select(p => new
{
Key = p.Key,
Items = Enumerable.Repeat(10, p.Sum / 10)
.Concat(Enumerable.Repeat(p.Sum % 10, 1))
})
.SelectMany(p => p.Items.Select(i => Tuple.Create(p.Key, i)))
.ToList();
This SQL query will return the wanted results:
SELECT meetingId, SUM(bill) as bill_total
FROM table
GROUP BY meetingId
HAVING SUM(bill) < 10
You should not do this at the client side because it can get pretty intensive, a simple GROUP BY with a HAVING clause should give you the expected results:
Sample data:
The query you need:
SELECT
MeetingID,
SUM(bill) AS Total
FROM
Table_1
GROUP BY
MeetingID
HAVING
SUM(bill) < 10
The results of the query:
table.GroupBy(p => p.meetingId).Where(p => p.Sum(q => q.bill) < 10)
.Select(p => new
{
meetingId= p.Key,
bill= p.Sum(q => q.bill)
});

Categories

Resources