How to get the exact value list result using loop - c#

this is my database which lists 5 results of the menu,
what i want is, if the date not equal to current Date (DateTime.Now), the result will be zero, like this:
Here is my code, on what I'm trying to do,
for (int i = 1; i <= DateTime.Now.ToShortDateString().Length; i++)
if (list.Any(x => x.Date.ToShortDateString() == i.ToString() ))
{
list.ToList().Add(new MenuModel
{
Total = list.First(x => x.Date.ToShortDateString() == i.ToString()).Total,
Location = list.First(x => x.Date.ToShortDateString() == i.ToString()).Location,
});
}
else
{
list.Add(new MenuModel
{
Total = list.First(x => x.Date.ToShortDateString() != i.ToString()).Total=0,
Location = list.First(x => x.Date.ToShortDateString() != i.ToString()).Location,
});
}
but the result that i get is like this,
The location, didnt show the real value, which will be A,B,C,D and E.. How to get the exact value of location?

What you really need is little linq magic. The below linq Get the Total based on the current date to actual value and if not to current date then set to 0.
DateTime date = DateTime.Today;
var menus = list.Select(l => new MenuModel
{
Total = l.Date.Date == date ? l.Total : 0,
Location = l.Location,
});

Related

LINQ - Match value, if not, return NULL element

I have a given date and I want to get the element on a table that is greater than the given date, but if it's not found, I want to return the element with the NULL date.
For example:
Table:
If the given date is, for example, 2019-10-20, I want to return the element with the ID 3.
If the given date is, for example, 2019-11-20, I want to return the element with the ID 4.
I've tried doing this:
var parameter = _db.Parameter
.OrderBy(x => x.EndDate)
.First(givenDate < x.EndDate) || x.EndDate == null);
But it's always returning the last element. What do I need to do?
You can try this:
var query = _db.Parameter
.OrderBy(x => x.EndDate)
.ToList();
var parameter = query.FirstOrDefault(x => givenDate < x.EndDate);
if ( parameter == null )
parameter = query.FirstOrDefault(x => x.EndDate == null);
if ( parameter == null )
...
else
...
We create an initial ordered query on the date, taking a list to evaluate the query.
Next we check if the first givenDate matching the desired result.
Else we try to get the first null row.
Then you can manage the final case.
To avoid parsing twice the query you can use that:
TheTypeOfParameter parameterFound = null;
TheTypeOfParameter parameterHavingDateNotNull = null;
TheTypeOfParameter parameterHavingDateNull = null;
bool foundDateNull = false;
bool foundDateNotNull = false;
foreach ( var item in query )
{
if ( !foundDateNull && item.EndDate == null )
{
parameterHavingDateNull = item;
foundDateNull = true;
}
else
if ( !foundDateNotNull && item.EndDate > givenDate )
foundDateNotNull = true;
if ( !foundDateNotNull )
parameterHavingDateNotNull = item;
if ( foundDateNotNull || foundDateNull )
break;
}
parameterFound = parameterHavingDateNotNull != null
? parameterHavingDateNotNull
: parameterHavingDateNull;
Because I can't test and debug, I hope this loop will work...
I would optimise the code by NOT using LINQ for this:
var parameter = _db.Parameter[0]; // you may need to handle that there's at least 1 item.
for (int i = 1; i < _db.Parameter.Count; i++)
{
var param = _db.Parameter[i];
if (param.EndDate > givenDate)
{ // param is good
if (parameter.EndDate == null || parameter.EndDate > param.EndDate)
parameter = param; // replace parameter with param
}
else if (parameter.EndDate != null && parameter.EndDate < givenDate)
{ // parameter precedes given date, replace it!
parameter = param;
}
}
This will iterate through your list just once, unlike the other solutions provided so far.
If you MUST use LINQ and want to iterate once, maybe you can use the below, which will return a dynamic though, so you need to convert it back to a Parameter. It works by replacing the NULL with DateTime.MaxValue so that when you do an OrderBy, the entries that were NULL would be ordered at the bottom.
var param = _db.Parameter
.Select(x => new
{
ID = x.ID,
EndDate = (x.EndDate.HasValue) ? x.EndDate : DateTime.MaxValue,
Value = x.Value
})
.OrderBy(x => x.EndDate)
.FirstOrDefault();
var parameter = new Parameter()
{
ID = param.ID,
EndDate = (param.EndDate == DateTime.MaxValue) ? null : param.EndDate,
Value = param.Value
};
As mentioned in the comments, NULL will be first after the ordering.
What if you try the following:
var parameter = _db.Parameter
.Where(x => (x.EndDate > givenDate) || (x.EndDate == null))
.OrderBy(x => x.EndDate)
.Last();
After selecting only earlier dates the latest is chosen. If only one element is in the list (the NULL element), this one gets chosen.

Compare when a Variable becomes bigger than a Value in List/IEnumerable

Background/Context
I am currently building a function that will take a value and compare it against a list to find at which point the string values become bigger. The value I take is a date and the String contains a range of dates that are headings to a chart and essentially I am trying to find the column in excel where this date would fit in. The heading dates are all weekly dates. I've tried to get my logic around it but nothing seems to be working so thought I'd ask on here.
Logic Behind Code
The program creates a IEnumerable to find cell of records for which date we are looking up
The program creates a IEnumerable with all the dates in the headings.
The program takes that date and compares it to the IEnumerable in 2. to return a column where the new date will go.
Code up to point 2
List<string> Sheet1Rows = new List<string>();
List<string> Sheet2Rows = new List<string>();
List<DateTime> ColumnDates = new List<DateTime>();
//List<int> NewRowsNumber = new List<int>();
for (int row = 9; row <= LastRowSheet1; row++)
{
Sheet1Rows.Add(ExcelWksht1.Cells[row, 1].Value);
}
for (int row = 9; row <= LastRowSheet2; row++)
{
Sheet2Rows.Add(ExcelWksht2.Cells[row, 1].Value);
}
for (int column = 16; column <= LastColumnSheet2; column++)
{
ColumnDates.Add(ExcelWksht2.Cells[5, column].Value);
}
IEnumerable<string> Names1 = Sheet1Rows;
IEnumerable<string> Names2 = Sheet2Rows;
IEnumerable<DateTime> Dates1 = ColumnDates;
IEnumerable<string> NamesInBoth = Names1.Intersect(Names2);
IEnumerable<string> NamesOnlyInTwo = Names2.Except(Names1);
Debug.Print("NamesInBoth\n==========");
foreach (var n in NamesInBoth)
{
Debug.Print(n.ToString());
//Get Row Values
int rowinsheet1 = (Sheet1Rows.FindIndex(x => String.Compare(x, n) == 0)) + 9;
int rowinsheet2 = (Sheet2Rows.FindIndex(x => String.Compare(x, n) == 0)) + 9;
//Get Date Values
var Sheet1Date = ExcelWksht1.Cells[rowinsheet1, 9].Value.ToString();
var Sheet2Date = ExcelWksht2.Cells[rowinsheet2, 9].Value.ToString();
if (Sheet1Date != "No Planned Date" && Sheet2Date != "No Planned Date")
{
if (Sheet1Date != null && Sheet2Date != null)
{
if (Sheet1Date.ToString() != Sheet2Date.ToString())
{
//THIS IS WHERE IM STRUGGLING
}
}
else
{
((Excel.Range)ExcelWksht2.Cells[rowinsheet2, 1]).EntireRow.Interior.Color = Microsoft.Office.Interop.Excel.XlRgbColor.rgbOrange;
}
}
}
The Problem
As shown in the code I need to figure out a way to construct a loop (?) to check the row date value against all the heading values in the IEnumerable to give me the date value that begins the 7 day range.

MVC - Count in a foreach loop, best practice

I have been working on getting a count records within a foreach loop. I am going to need run many of these counts on a single page. I am looking for the most efficient way to do this.
I have gotten this far, but I am not sure if I headed down the right path. If I am, how do I get this data into my view.
ViewModel
public class AgtLeadStatsListVM
{
public string LoanAgent { get; set; }
public DateTime LeadDate { get; set; }
public int LeadDailyCt { get; set; }
public int LeadWeeklyCt { get; set; }
public int LeadMTDCt { get; set; }
public int LeadYTDCt { get; set; }
public IEnumerable<MWFUser> AgentList { get; set; }
public virtual WebLead Lead { get; set; }
}
Controller
var model = new AgtLeadStatsListVM();
{
// Get Selected Agent's Information
var AgentList = from l in db.MWFUsers
where (l.UserTitle == "Banker"
select l;
foreach (var agent in AgentList)
{
// Daily Lead Count
var LeadDailyCt = db.WebLeads.Count(x => (x.LoanAgent == agent.UserEmail)
&& (x.LeadDate >= todayDate && x.LeadDate <= todayEndDay));
// Weekly Lead Count
var LeadWeeklyCt = db.WebLeads.Count(x => (x.LoanAgent == agent.UserEmail)
&& x.LeadDate >= firstOfWeek
&& x.LeadDate <= todayEndDay);
// Monthly Lead Count
var LeadMTDCount = db.WebLeads.Count(x => (x.LoanAgent == agent.UserEmail)
&& x.LeadDate >= firstOfMonth
&& x.LeadDate <= todayEndDay);
// YTD Lead Count
var LeadYTDCount = db.WebLeads.Count(x => (x.LoanAgent == agent.UserEmail)
&& x.LeadDate >= firstOfMonth
&& x.LeadDate <= todayEndDay);
}
}
View
#model LoanModule.ViewModels.AgtLeadStatsListVM
<div>
#foreach (var item in Model.AgentList)
{
<p>#Model.LoanAgent</p>
<p>#Model.LeadDailyCt</p>
<p>#Model.LeadWeeklyCt</p>
<p>#Model.LeadMTDCt</p>
<p>#Model.LeadYTDCt</p>
}
I am receiving this error on my View: Object reference not set to an instance of an object. (on line: #foreach (var item in Model.AgentList))
What am I missing?
Thank you.
The semicolon at the end of var model = new AgtLeadStatsListVM(); means that you are no longer in an object initializer after that line. The syntax you're probably trying for is something more along these lines:
var agents =
from l in db.MWFUsers
where l.UserTitle == "Banker"
select l;
var model = new AgtLeadStatsListVM
{
// Get Selected Agent's Information
AgentList = agents.ToList(),
// Daily Lead Count
LeadDailyCt = agents.Sum(a => db.WebLeads.Count(
x => (x.LoanAgent == a.UserEmail)
&& (x.LeadDate >= todayDate && x.LeadDate <= todayEndDay)))
// ...
}
By the way, if you want to get all of this information in a single round-trip, you could use this group by-based trick.
var model =
(from agent in agents
let webLeads = db.WebLeads.Where(x => x.LoanAgent == agent.UserEmail)
group new{agent, webLeads} by 0 into g
select new AgtLeadStatsListVM
{
// Get Selected Agent's Information
AgentList = g.Select(e => e.agent).ToList(),
// Daily Lead Count
LeadDailyCt = g.Sum(e => e.webLeads.Count(x => x.LeadDate >= todayDate && x.LeadDate <= todayEndDay)),
// ...
}).FirstOrDefault();
Update
From your comments it sounds like this is more what you're going for:
var model =
(from agent in agents
let webLeads = db.WebLeads.Where(x => x.LoanAgent == agent.UserEmail)
select new AgtLeadStatsListVM
{
// Get Selected Agent's Information
LoanAgent = agent.UserEmail,
// Daily Lead Count
LeadDailyCt = webLeads.Count(x => x.LeadDate >= todayDate && x.LeadDate <= todayEndDay),
// ...
}).ToList();
And your view code:
#model IEnumerable<LoanModule.ViewModels.AgtLeadStatsListVM>
<div>
#foreach (var item in Model)
{
<p>#item.LoanAgent</p>
<p>#item.LeadDailyCt</p>
<p>#item.LeadWeeklyCt</p>
<p>#item.LeadMTDCt</p>
<p>#item.LeadYTDCt</p>
}
The AgentList property should be removed from your model entirely.
I am receiving this error on my View: Object reference not set to an
instance of an object. (on line: #foreach (var item in
Model.AgentList))
The AgentList is null.
Furthermore, you haven't initialized correctly your model.
Specifically, this line of code
var model = new AgtLeadStatsListVM();
creates a new object of type AgtLeadStatsListVM, where
LoanAgent is null
LeadDate 1/1/0001 12:00:00 AM
LeadDailyCt is 0
LeadWeeklyCt is 0
LeadMTDCt is 0
LeadYTDCt is 0
AgentList is null
WebLead is Lead
The default values, since you didn't set any value. Probably, you want to make use of an object initializer, there you don't need ();. We write just this:
var model = new AgtLeadStatsListVM
{
LoadAgent = "Name of the LoadAgent",
LeadDate = DateTime.Now.Utc,
LeadDailyCt = agents.Sum(a => db.WebLeads.Count(
x => (x.LoanAgent == a.UserEmail)
&& (x.LeadDate >= todayDate && x.LeadDate <= todayEndDay)))
// Do the same for the rest of the corresponding properties.
}
I am going to ignore the error that you are getting (see other answers for it) and reference only best practice and a most efficient way for counting part of the question.
The most efficient way (at least in my opinion) would be using some caching technique for the result and updating the cache on daily basis(since the maximum resolution that you use is daily). Clearly, choosing an appropriate caching mechanism depends on your application. It can go from storing some data in static variable on application start, to running a dedicated Redis server (or any other fast data structure store). The bottom line here is: try to minimize the number of queries to DB and cache any suitable data.

LINQ Entity Framework Select A Record

I have a trying to select a record.
In db, i have two records for Month 1 and 4.
I need to get values for both month 1 and 4, but all i get is value for month 1 both times in iteration (Month 1 and Month 4).
EX: In dB Month 1 Value is : 55 and Month 4 value is 22, but i get value 55 for both Months 1 and 4, while iteration in code.
for (var month = 1; month <= 12; month++)
{
var itemMonth = month;
var itemAuditReport = proxy.itemAuditReports.SingleOrDefault(i => i.SKU == itemSku && i.Month == itemMonth && i.Year==itemYear);
//
}
if (itemAuditReport == null)
{
// Do Something
}
Am i missing something ?
why dont you try out
I am guessing that itemAuditReportis the same item get assigned in iteration causing problem so make use of list and add items in it
List<Item> item = null;
for (var month = 1; month <= 12; month++)
{
var itemMonth = month;
var itemAuditReport = proxy.itemAuditReports.SingleOrDefault(i => i.SKU == itemSku &&
i.Month == itemMonth && i.Year==itemYear);
if(itemAuditReport!=null)
item.Add(itemAuditReport);
//
}
This will give you some insight on your error.
Why is it bad to use an iteration variable in a lambda expression
I will let you know if I find something to help you out fix the issue.
Like John Sykor suggested, try this:
for (var month = 1; month <= 12; month++)
{
var itemMonth = month;
var year = itemYear;
var sku = itemSku;
var itemAuditReport = proxy.itemAuditReports.SingleOrDefault(i =>
i.SKU == sku && i.Month == itemMonth && i.Year==year);
}
the following examples explain it more:
this outputs 10 ten times.
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (Action action in actions)
{
action();
}
but the following outputs 0...9 as expected
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
var x = i; //<--important line
actions.Add(() => Console.WriteLine(x));
}
foreach (Action action in actions)
{
action();
}
Yes, the keys in the Entity Framework model wasn't set properly, Sku was set as a primary key with title. in the model, hence bringing the same result set.
I changed the keys to be applied on Sku, Month and Year, and it worked great !!

Separating Records into indvidual months for mvc

I have a collection of records. Which have two boxers, match date, location etc...
I want to separate them by months and group them together. Currently I have what is below. And it works to a degree. That looks for matchdates in the future. that is this year and steps through each month (1-12) and finds any matches in that date range.
Placing it into a nice dictionary of int, enumerable where int is the month and enumberable is the collection of matches in that month
//Build the matches list by Months!!!
var summarysDic = new Dictionary<int, IEnumerable<MatchSummary>>();
for (int i = 1; i <= 12; i++)
{
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value.Year == DateTime.Now.Year &&
x.MatchDate.Value.Month == i &&
!x.HasResult() &&
x.MatchDate.Value > DateTime.Now);
if (MatchesOfMonth.Count() > 0)
{
summarysDic.Add(i, MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x=> new MatchSummary(x)).ToArray());
}
}
Problem is this currently only deals with this year. I would like to instead make it so it works for "the next 6 months" but this would of course have to work over the new year as well!
Whats the best/cleanest way to go about doing this?
thanks in advance!
P.S on a side note i have yet to find how to simply do DateTime.Now.Month.add(1) for example (as i will always be going from current date forwards!)
-----COMPLETED CODE!-----
//Build the matches list by Months!!!
var summarysDic = new Dictionary<string, IEnumerable<MatchSummary>>();
for (int i = 1; i <= 12; i++)
{
var checkDate = DateTime.Now.AddMonths(i);
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value.Month == checkDate.Month &&
x.MatchDate.Value.Year == checkDate.Year &&
!x.HasResult() &&
x.MatchDate.Value > DateTime.Now);
if (MatchesOfMonth.Count() > 0)
{
var firstMatchDate = MatchesOfMonth.First().MatchDate.Value;
if (firstMatchDate.Year != DateTime.Now.Year)
{
summarysDic.Add(firstMatchDate.ToString("MMMM yyyy"), MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x => new MatchSummary(x)).ToArray());
}
else
{
summarysDic.Add(firstMatchDate.ToString("MMMM"), MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x => new MatchSummary(x)).ToArray());
}
}
}
I believe you can get what you want without modifying your algorithm significantly:
//Build the matches list by Months!!!
var summarysDic = new Dictionary<int, IEnumerable<MatchSummary>>();
for (int i = 0; i <= 6; i++)
{
var checkDate = DateTime.Now.AddMonths(i);
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value.Year == checkDate.Year &&
x.MatchDate.Value.Month == checkDate.Month &&
!x.HasResult() &&
x.MatchDate.Value > DateTime.Now);
if (MatchesOfMonth.Count() > 0)
{
summarysDic.Add(i, MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x=> new MatchSummary(x)).ToArray());
}
}
What's wrong with DateTime.Now.AddMonth(1)?
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value <= DateTime.Now.AddMonth(i)
&& !x.HasResult()
&& x.MatchDate.Value > DateTime.Now);
I haven't compiled that, but it should run with only fairly minor tweeking...

Categories