Linq DataTable reuse concatenated variable - c#

Here is what I am doing in my LINQ on a datatable.
var result = resTable.Rows.Where(r => Map.ContainsKey(string.Concat(r[HeaderCol].ToString().Trim(),dot,r[FooterCol].ToString().Trim(),dot,r[TypeCol].ToString().Trim())))
.GroupBy(r => string.Concat(r[HeaderCol].ToString().Trim(), dot, r[FooterCol].ToString().Trim(), dot, r[TypeCol].ToString().Trim()))
.ToDictionary(g => g.Key,
g => g.GroupBy(r => DateTime.FromOADate((double)r[DateCol]))
.ToDictionary(c => c.Key,
c => c.Select(r => new ResultObj(DateTime.FromOADate((double)r[ResultDateCol]), new Decimal((double)r[PriceCol])))
.ToList()));
I am creating a key from column values and need to use it in group by as well.
string.Concat(r[HeaderCol].ToString().Trim(), dot, r[FooterCol].ToString().Trim(), dot, r[TypeCol].ToString().Trim())
Any way I can do string concat only once and use it twice in LINQ ?

I don't know why it is necessary, but here t you want.
var result = resTable.Rows.Select(r => new {r, res = string.Concat(r[HeaderCol].ToString().Trim(),dot,r[FooterCol].ToString().Trim(),dot,r[TypeCol].ToString().Trim())})
.Where(r => Map.ContainsKey(r.res))
.GroupBy(r => r.res)
.ToDictionary(g => g.Key,
g => g.GroupBy(r => DateTime.FromOADate((double)r.r[DateCol]))
.ToDictionary(c => c.Key,
c => c.Select(r => new ResultObj(DateTime.FromOADate((double)r.r[ResultDateCol]), new Decimal((double)r.r[PriceCol])))
.ToList()));

Related

Mysql to Linq query

MySQL Query
select MAX(os.aggregate_date) as lastMonthDay,os.totalYTD
from (SELECT aggregate_date,Sum(YTD) AS totalYTD
FROM tbl_aggregated_tables
WHERE subscription_type = 'Subcription Income'
GROUP BY aggregate_date) as os
GROUP by MONTH(os.aggregate_date),YEAR(os.aggregate_date)
ORDER BY lastMonthDay;
converted to this LINQ query
var income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income")
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { month = s.date.Month, year = s.date.Year })
.Select(
// select max data and take its ytdsum value
).ToList();
The purpose of second grouping is to find the max day of each month with a year.
Now, How to select the max date of each month and its ytdsum after the second Grouping?
update
income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income")
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { s.date.Month, s.date.Year })
.Select(
x => x.Max(s => s.date)
).ToList()
this way it's only return the dates and i could not return the full object of the list including ytdSum.
This should work:
var income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income")
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { month = s.date.Month, year = s.date.Year })
.Select(
x => x.OrderByDescending(k => k.date).First()
).ToList();
income now is a list of objects, each of them have date and ytdsum
this worked good but it takes much more time than the original mysql query.
var income = context.tbl_aggregated_tables
.Where(s => s.subscription_type == "Subcription Income"
)
.GroupBy(s => s.aggregate_date)
.Select(result => new
{
date = result.Key,
ytdsum = result.Select(x => x.YTD).Sum()
})
.GroupBy(s => new { s.date.Month, s.date.Year })
.Select(
x => x.OrderByDescending(s => s.date)
)
.ToList()
.Select(el => el.FirstOrDefault())
.OrderBy(s=>s.date);

Return additional column in Linq Query?

I have this Linq query:
var area = db.MyDbSet
.Where(s => s.langid == langid)
.GroupBy(s => s.Title)
.Select(g => new { Title = g.Key })
.Select(s => s.Title);
I want to return another column from the same table, its called: CodeId.
I am noob Linq programmer and quite lost in all those mish-mash things in C# so I can't really understand what should I do.
Can somebody help me how to return the another column from the same table with the same query call?
This may help you :
var area = db.MyDbSet
.Where(s => s.langid == langid)
.GroupBy(s => s.Title)
.Select(g => new { Title = g.Key, CodeId = g.FirstOrDefault().CodeId });
var area = db.MyDbSet
.Where(s => s.langid == langid)
.GroupBy(s => s.Title)
.Select(g => new {
Title = g.Key,
CodeId = g.Id,
OtherField = "Field"
})

Convert to Dictionary and Fill missing items

I have a query as follows:
IDictionary<ClassificationLevel, Int32> stats = context.Exams
.GroupBy(x => x.Classification)
.Select(x => new { Key = x.Key, Count = x.Count() })
// ...
The dictionary ClassificationLevel is has follows:
public enum ClassificationLevel { L1 = 1, L2 = 2, L3 = 3, L4 = 4 }
My problems are:
How to convert the result of the query to IDictionary
The items with Count 0 will not appear in the dictionary.
How to make sure those items appear with value 0.
UPDATED
To get the best performance I think the following should be made:
IDictionary<ClassificationLevel, Int32> stats = context.Exams
.GroupBy(x => x.Classification)
.ToDictionary(x => new { Key = x.Key, Count = x.Count() });
This would close the EF query ...
Then I would find which keys are missing, e.g. which ClassificationLevel items are missing, and add those keys with value 0.
How should I do this?
With a single linq expression.
var stats = context.Exams
.GroupBy(x => x.Classification)
.ToDictionary(x => x.Key, g => g.Count()) // execute the query
.Union(Enum.GetValues(typeof(ClassificationLevel))
.OfType<ClassificationLevel>()
.ToDictionary(x => x, x => 0)) // default empty count
.GroupBy(x => x.Key) // group both
.ToDictionary(x => x.Key, x => x.Sum(y => y.Value)); // and sum
use Enumerable.ToDictionary() and then Enum.GetValues() to fill in the missing values:
IDictionary<ClassificationLevel, Int32> dict = context.Exams
.GroupBy(x => x.Classification)
.ToDictionary(g => g.Key, g => g.Count());
foreach (ClassificationLevel level in Enum.GetValues(typeof(ClassificationLevel)))
if (!dict.ContainsKey(level))
dict[level] = 0;
Or, if Entity Framework balks at the ToDictionary(), I believe you can do the following:
IDictionary<ClassificationLevel, Int32> dict = context.Exams
.GroupBy(x => x.Classification)
.Select(x => new { Key = x.Key, Count = x.Count() })
.AsEnumerable()
.ToDictionary(g => g.Key, g => g.Count);
foreach (ClassificationLevel level in Enum.GetValues(typeof(ClassificationLevel)))
if (!dict.ContainsKey(level))
dict[level] = 0;
You could solve it like this
var enumValues = Enum.GetValues(typeof (EnumType)).Cast<EnumType>().ToArray();
Enumerable.Range((int) enumValues.Min(), (int) enumValues.Max()).ToDictionary(
x => x.Key,
x => context.Exams.Count(e => e.Classification == x)
);
You could use a "Left Outer Join" in LINQ, after that you can use GroupBy + ToDictionary:
var query = from classification in Enum.GetValues(typeof(ClassificationLevel)).Cast<ClassificationLevel>()
join exam in context.Exams on classification equals exam.Classification into gj
from subExam in gj.DefaultIfEmpty()
select new { classification, exam = subExam };
IDictionary<ClassificationLevel, Int32> stats = query
.GroupBy(x => x.classification)
.ToDictionary(g => g.Key, g => g.Count());
This code allows you to loop around your enum.
foreach (ClassificationLevel level in (ClassificationLevel[]) Enum.GetValues(typeof(ClassificationLevel)))
{
}
You could then put something like the following in the middle of the loop:
if(!stats.KeyExists(level))
{
stats.Add(level, 0);
}

Adding more parameters to a Linq query

I'm using this query to count number of orders by date. I'm trying to add one more parameter that counts total products for each order, however I can't get it to work atm.
This is the essential part of a method that is suposed to return a list of 3 parameters (Date, TotalOrders and TotalProducts). Im using a Linq query to get a list with total order for each date, im wondering how to add my third parameter to the list "TotalProducts" and if i can do by adding one more search parameter in the Query. The foreach part below do not work propertly, it will return a list of TotalProducts but CreationDate will be the same for ech item in the list. I also have a feeling putting a foreach inside a foreach dosn't seem optimal for this:
var orders = _orderService.SearchOrderStatistics(startDateValue, endDateValue, orderStatus,
paymentStatus, shippingStatus, model.CustomerEmail, model.OrderGuid);
var result = orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc.Date.ToString("yyyyMMdd"))
.Select(s => new { Date = s.Key, Count = s.Count() });
List<GCOrdersModel> TotalOrdersPaid = new List<GCOrdersModel>();
foreach (var g in result)
{
foreach (var opv in orders)
{
GCOrdersModel _Om = new GCOrdersModel(g.Date, g.Count.ToString(), opv.OrderProductVariants.Count.ToString());
TotalOrdersPaid.Add(_Om);
}
}
return TotalOrdersPaid;
To access total products for every orders I must use OrderProductVariants.Count.ToString()
Can I add this parameter to the query?
Thx
You could try this:
return orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc.Date.ToString("yyyyMMdd"))
.Select(s => new GCOrdersModel()
{
Date = s.Key,
Count = s.Count(),
OpvCount = opv.OrderProductVariants.Count.ToString()
})
.ToList();
or
return orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc.Date.ToString("yyyyMMdd"))
.Select(s => new GCOrdersModel(s.Key, s.Count, opv.OrderProductVariants.Count.ToString()))
.ToList();
That way, you don't have to iterate over your result again. And it automatically creates your list of GCOrdersModel.
Edit
Does this work?
return orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc.Date.ToString("yyyyMMdd"))
.Select(s => new GCOrdersModel()
{
Date = s.Key,
Count = s.Count(),
OpvCount = s.OrderProductVariants.Count.ToString()
})
.ToList();
or
return orders.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc.Date.ToString("yyyyMMdd"))
.Select(s => new GCOrdersModel(s.Key, s.Count(), s.OrderProductVariants.Count.ToString()))
.ToList();
How about:
var opvCount =
opv
.OrderProductVariants
.Count
.ToString();
return
orders
.Where(o => o.PaymentStatus == PaymentStatus.Paid)
.GroupBy(g => g.CreatedOnUtc.Date.ToString("yyyyMMdd"))
.Select(s => new
{
Date = s.Key,
Count = s.Count()
})
.Select(x =>
new GCOrdersModelg(x.Date, g.Count.ToString(), opvCount));

Resultant LinQ Query to a new DataTable

Refer to earlier post/Question:
LINQ to Swap few Columns to Rows of a DataTable using C#
I want the resultant of the query interms of a new dataTable directly instead of defining columns in the new datatable.
Refer to the above post question the LINQ Query newset is:
var newSet = dt.AsEnumerable()
.GroupBy(r => r.Field<string>("Location"))
.Select(g => new
{
Location = g.Key,
ppl_required_Q1 = g.Where(p => p.Field<string>("Quarter") == "Q1").Sum(p => p.Field<int>("ppl_required")),
ppl_required_Q2 = g.Where(p => p.Field<string>("Quarter") == "Q2").Sum(p => p.Field<int>("ppl_required")),
ppl_required_Q3 = g.Where(p => p.Field<string>("Quarter") == "Q3").Sum(p => p.Field<int>("ppl_required")),
ppl_required_Q4 = g.Where(p => p.Field<string>("Quarter") == "Q4").Sum(p => p.Field<int>("ppl_required")),
ppl_available_Q1 = g.Where(p => p.Field<string>("Quarter") == "Q1").Sum(p => p.Field<int>("ppl_available")),
ppl_available_Q2 = g.Where(p => p.Field<string>("Quarter") == "Q2").Sum(p => p.Field<int>("ppl_available")),
ppl_available_Q3 = g.Where(p => p.Field<string>("Quarter") == "Q3").Sum(p => p.Field<int>("ppl_available")),
ppl_available_Q4 = g.Where(p => p.Field<string>("Quarter") == "Q4").Sum(p => p.Field<int>("ppl_available")),
});
How to get the newset resultant collection in a new datatable directly without any loop through? Is it possible in LINQ??
Try this:
newset.CopyToDataTable();
Check this Link

Categories