I have three columns in a datatable: string, DateTime, and decimal. I want to group by the string and decimal column, and for the rows grouped I want to sum the decimal values. I know how to do the sum part, but how do you group two different columns in a datatable?
This is my code so far which doesn't work properly:
var newSort = from row in objectTable.AsEnumerable()
group row by new {ID = row.Field<string>("resource_name"), time1 = row.Field<DateTime>("day_date")} into grp
orderby grp.Key
select new
{
resource_name1 = grp.Key.ID,
day_date1 = grp.Key.time1,
Sum = grp.Sum(r => r.Field<Decimal>("actual_hrs"))
};
I don't think you're giving us the full story. Other than orderby not working with anonymous types (the code you gave wouldn't have compiled), your query should work the way you want. I just put this in LINQPad:
var objectTable = new DataTable();
objectTable.Columns.Add("resource_name",typeof(string));
objectTable.Columns.Add("day_date",typeof(DateTime));
objectTable.Columns.Add("actual_hrs",typeof(decimal));
objectTable.Rows.Add(1, DateTime.Today, 1);
objectTable.Rows.Add(2, DateTime.Today, 2);
var newSort = from row in objectTable.AsEnumerable()
group row by new {ID = row.Field<string>("resource_name"), time1 = row.Field<DateTime>("day_date")} into grp
select new
{
resource_name1 = grp.Key.ID,
day_date1 = grp.Key.time1,
Sum = grp.Sum(r => r.Field<Decimal>("actual_hrs"))
};
newSort.Dump();
... and I got these results:
resource_name1 | day_date1 | Sum
1 | 7/1/2011 12:00:00 AM | 1
2 | 7/1/2011 12:00:00 AM | 2
use this code
var newSort = from row in objectTable.AsEnumerable()
group row by new {ID = row.Field<string>("resource_name"), time1 = row.Field<DateTime>("day_date")} into grp
orderby grp.Key
select new
{
resource_name1 = grp.Key.ID,
day_date1 = grp.Key.time1,
Sum = grp.Sum(r => Convert.ToDecimal(r.ItemArray[2]))
};
For those that want a solution in Vb.net, here is an example:
Dim workTable As DataTable = New DataTable("Customers")
Dim workCol As DataColumn = workTable.Columns.Add("ID", Type.GetType("System.Int32"))
workTable.Columns.Add("Total", Type.GetType("System.Decimal"))
workTable.Columns.Add("Compra", Type.GetType("System.Decimal"))
Dim row As DataRow = workTable.NewRow()
row("id") = 2
row("total") = 1.5
row("compra") = 3
workTable.Rows.Add(row)
row = workTable.NewRow()
row("id") = 1
row("total") = 1.5
row("compra") = 3.3999999999999999
workTable.Rows.Add(row)
row = workTable.NewRow()
row("id") = 1
row("total") = 1.5
row("compra") = 5
workTable.Rows.Add(row)
Dim detalles As IEnumerable(Of DataRow) = workTable.AsEnumerable()
Dim query = From detalle In detalles.AsEnumerable() _
Group detalle By grupoClave = New With _
{ _
Key .C2 = detalle("id"), _
Key .C4 = detalle("total")} Into g = Group _
Select New With _
{ _
.Col2 = g(0).Field(Of Integer)("id"), _
.Col3 = g(0).Field(Of Decimal)("total"), _
.Col4 = g.Sum(Function(fact) fact.Field(Of Decimal)("compra")) _
}
For Each p In query
Console.WriteLine((p.Col2 & p.Col3 & p.Col4))
Next
Related
I have this code and it is supposed to fill up the appropriate tables and it is filling the first table (Mu_Reports) one but the second table (MU_By_Machine) keeps coming up blank
using (var db = new ProductionContext())
{
var objct = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)db).ObjectContext;
objct.ExecuteStoreCommand("TRUNCATE TABLE [MU Report]");
objct.ExecuteStoreCommand("TRUNCATE TABLE [MU By Machine]");
db.SaveChanges();
var query = db.Production_Reports
.GroupBy(x => new { x.Date, x.Machine_Number, x.Shift })
.Select(x => new
{
Date = x.Key.Date,
Shift = x.Key.Shift,
MachineNum = x.Key.Machine_Number,
MU = x.Sum(i => i.Partial_MU_ * 100)
}).ToList();
foreach (var item in query)
{
var z = new MU_Report();
z.Date = System.Convert.ToDateTime(item.Date);
z.Shift = item.Shift;
z.Machine_Number = item.MachineNum;
z.MU = item.MU;
db.MU_Reports.Add(z);
db.SaveChanges();
}
var query2 = from b in db.MU_Reports
join downtime in db.Downtime_Reports on b.Shift equals downtime.Shift
where downtime.Downtime_Code.Equals("9185")
group downtime by new { b.Date, b.Shift, b.Machine_Number, b.MU } into g
select new
{
Date = g.Key.Date,
Shift = g.Key.Shift,
Machine = g.Key.Machine_Number,
MU = g.Key.MU,
No_Work_Hours = g.Sum(x => x.Total_DownTime)
};
foreach (var item in query2)
{
var y = new MU_By_Machine();
y.Date = item.Date;
y.Shift = item.Shift;
y.Machine_Number = item.Machine;
y.MU = item.MU;
y.MU_w_o_No_Work = (item.MU * 8) / (8 - item.No_Work_Hours);
db.MU_By_Machines.Add(y);
db.SaveChanges();
}
}
I don't know if it is because I cannot have query and query2 go in the same button or if I am just doing something wrong. No error occurs only MU_By_Machine appears blank. Please keep in mind I'm new to C#.
You will need to invoke .ToList() at the end of your select statement in query2 as well.
I need to sum elements of same type starting from 2 LINQ queries.
Below is my code:
var query1 = from d in _contextProvider.Context.Documents
where d.TransportId == transportId
group d by d.Type
into dg
select new { DocumentType = dg.Key.ToString(), DocumentCount = dg.Count() };
var query2 = from n in _contextProvider.Context.NotificationDocuments
where n.TransportId == transportId
group n by n.TransportId
into nd
select new { DocumentType = "Notification", DocumentCount = nd.Count() };
var query_collapsed = query1.Union(query2)
.GroupBy(p => new { DocumentType = p.DocumentType })
.Select(g => new DocumentCounters() { DocumentType = g.Key.DocumentType, DocumentCount = g.Sum(p => p.DocumentCount) });
Example: below let's analyse values for DocumentType equals to Notification.
Values of query1:
Values of query2:
The collapsed query :
That's correct: 1 + 2 = 3
The problem: I noticed that whenever the count for Notification in query1 is equals to the count for Notification in query2, then the sum is not performed.
Example:
2 + 2 = 2
or
3 + 3 = 3
Any ideas ?
LINQ Union will remove duplicate entries. If you want to merge the two sequences you can use Concat like so:
var query_collapsed = query1.Concat(query2)
.GroupBy(p => new { DocumentType = p.DocumentType })
.Select(g => new DocumentCounters() { DocumentType = g.Key.DocumentType, DocumentCount = g.Sum(p => p.DocumentCount) });
this is my environment.
I have a datatable with this kind of structure
IDstring | Attrib1 | Attrib2 | Attrib3 | Attrib4 | Value
I need to export average, max, min value group by attrib elements, and I use following code:
var queryTable = from rows in dataTable.AsEnumerable()
group rows by new
{
Attrib1 = rows["Attrib1"],
Attrib2 = rows["Attrib2"],
Attrib3 = rows["Attrib3"],
Attrib4 = rows["Attrib4"]
} into grp
select new
{
Attrib1 = grp.Key.Attrib1,
Attrib2 = grp.Key.Attrib2,
Attrib3 = grp.Key.Attrib4,
Attrib4 = grp.Key.Attrib14,
Avg = grp.Average(s => Convert.ToDouble(s["Value"])),
Min = grp.Min(s => Convert.ToDouble(s["Value"])),
Max = grp.Max(s => Convert.ToDouble(s["Value"])),
Count = grp.Count()
};
If I need also to export one IDstring (not necessary all occurrence) that match max and min value, how can I do it?
I have tried with another linq query for every max and min element of upon querytable results to the original datatable but it is too slow for the number of elements I have. Can you help me please?
With the big help of coder of code, this could be the solution:
First I make on order by Value and then i take first and last element.
var queryTable = from rows in resultTable.AsEnumerable()
orderby rows.Field<double>("Value")
group rows by new
{
Attrib1 = rows["Attrib1"],
Attrib2 = rows["Attrib2"],
Attrib3 = rows["Attrib3"],
Attrib4 = rows["Attrib4"]
} into grp
select new
{
Attrib1 = grp.Key.Attrib1,
Attrib2 = grp.Key.Attrib2,
Attrib3 = grp.Key.Attrib3,
Attrib4 = grp.Key.Attrib4,
Avg = grp.Average(s => Convert.ToDouble(s["Value"])),
Min = grp.Min(s => Convert.ToDouble(s["Value"])),
IDStringMin = grp.First().Field<string>("IDstring"),
Max = grp.Max(s => Convert.ToDouble(s["Value"])),
IDStringMax = grp.Last().Field<string>("IDstring"),
Count = grp.Count()
};
I have list of simple objects:
var r = new List
{
new { Id = 1, Value = 2, DateTime = DateTime.Parse("10.10.2014")},
new { Id = 2, Value = 3, DateTime = DateTime.Parse("10.10.2014")},
new { Id = 3, Value = 4, DateTime = DateTime.Parse("10.10.2014")},
new { Id = 1, Value = 5, DateTime = DateTime.Parse("11.10.2014")},
new { Id = 2, Value = 6, DateTime = DateTime.Parse("11.10.2014")}
};
I want to get object like:
DateTime | 1 | 2 | 3 |
10.10.2014 | 2 | 3 | 4 |
11.10.2014 | 5 | 6 | |
Is there any nice linq query to this? Smth like pivot/unpivot in sql maybe?
Try this:
r.ToLookup(t => t.id, t=>t.DateTime)
And if that doesn't work, read through this
You are looking to group the list according to id and then key the resulting list by dictionary. You should be able to do that with some combination of GroupBy, which creates a list of grouped lists and ToDictionary(), which creates allows you to specify a property of the object as a key and creates a dictionary from it.
You're looking for a simple GroupBy:
var result = r.GroupBy(x => x.DateTime)
.Select (grp => new
{
DateTime = grp.Key,
_1 = grp.Where(x => x.Id == 1).Select(x => x.Value).Cast<Int32?>().FirstOrDefault(),
_2 = grp.Where(x => x.Id == 2).Select(x => x.Value).Cast<Int32?>().FirstOrDefault(),
_3 = grp.Where(x => x.Id == 3).Select(x => x.Value).Cast<Int32?>().FirstOrDefault()
});
result is now:
If the number of Ids are not known at compile-time then there is no way to create a link statement to capture those Ids as new fields. Linq just can do that. The best you can do in that case is this:
var ids = r.Select(x => x.Id).Distinct().OrderBy(x => x).ToArray();
var query =
from x in r
group x by x.DateTime into gxs
let lookup = gxs.ToLookup(x => x.Id, x => (int?)x.Value)
select new
{
DateTime = gxs.Key,
Values = ids.Select(i => new
{
Id = i,
Value = lookup[i].FirstOrDefault(),
}).ToArray(),
};
That produces this:
If the Ids are known then the following variation is the best:
var query =
from x in r
group x by x.DateTime into gxs
let lookup = gxs.ToLookup(x => x.Id, x => (int?)x.Value)
select new
{
DateTime = gxs.Key,
_1 = lookup[1].FirstOrDefault(),
_2 = lookup[2].FirstOrDefault(),
_3 = lookup[3].FirstOrDefault(),
};
I'm need to do the equivalent of a t-sql statement like this using LINQ:
SELECT * FROM mytable WHERE idnumber IN('1', '2', '3')
so in LINQ I have:
Dim _Values as String = "1, 2, 3"
Dim _Query = (From m In mytable Where idnumber = _Values Select m).ToList()
Not sure what to do with _Values to make idnumber evaluate each value in the string.
Thanks in advance.
I will go for Inner Join in this context. If I would have used Contains, it would Iterate unnecessarily 6 times despite of the fact that there is just one match.
C# Version
var desiredNames = new[] { "Pankaj", "Garg" };
var people = new[]
{
new { FirstName="Pankaj", Surname="Garg" },
new { FirstName="Marc", Surname="Gravell" },
new { FirstName="Jeff", Surname="Atwood" }
};
var records = (from p in people
join filtered in desiredNames on p.FirstName equals filtered
select p.FirstName
).ToList();
VB.Net Version
Dim desiredNames = New () {"Pankaj", "Garg"}
Dim people = New () {New With { _
.FirstName = "Pankaj", _
.Surname = "Garg" _
}, New With { _
.FirstName = "Marc", _
.Surname = "Gravell" _
}, New With { _
.FirstName = "Jeff", _
.Surname = "Atwood" _
}}
Dim records = ( _
Join filtered In desiredNames On p.FirstName = filtered).ToList()
Disadvantages of Contains
Suppose I have two list objects.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Using Contains, it will search for each List-1 item in List-2 that means iteration will happen 49 times !!!
Dim _Values as String = "1, 2, 3"
Dim _Query = (From m In mytable Where _Values.Split(", ").Contains(m.idnumber) Select m).ToList()