Select object and add to list linq c# - c#

probably a simple Q but still a beginner at this and not sure on how to....
Each WorkStation can have a number of Invoices So my below code will....
store all the workStations, go Through each workStation,
get the last (most recent) invoice for that workStation,
If the invoices date (for the most recent Invoice) is < 12 Months
Add it to the list of sites...
EDIT:
Thanks for all the help guys but I am trying to do it through c# and avoid the LINQ searche you guys have mentioned...thanks for everyone who replied...
My new problem being i need to sort the ChosenInvoices list into asceding order and return the first...as i think it is selecting anyone in the list:
var allWorkSites =
(from worksites in db.Work_Sites
select worksites).Distinct().ToList();
List<Object> chosenInvoices = new List<Object>();
foreach (Work_Site worksite in allWorksites)
{
Invoice lastInvoice = worksite.Invoices.LastOrDefault();
if (lastInvoice != null)
{
if (lastInvoice.Invoice_Date < DateTime.Now.AddMonths(-12))
{
chosenInvoices.Add(workstation);
}
}
}

List<invoice> actualInvoices = db.Work_Stations.Distinct()
.Where(w => w.Invoices.Last().Invoice_Date < DateTime.Now.AddMonths(-12)).Select(w => w.Invoices.Last()).ToList();
This code will return you the list of most recent invoices from each workstation.
To sort your invoice in asscending order list have a method OrderBy(), so order it with this method and then take the first one.
Also list have Sort() method.

allWorkStations
.Where(w => w.Invoices.Last().Invoice_Date < DateTime.Now.AddMonths(-12))
.Select(w => list.add(w));
Or better yet:
List<Work_Station> list = db.Work_Stations
.Distinct()
.Where(w => w.Invoices.Last().Invoice_Date < DateTime.Now.AddMonths(-12))
.ToList();

var allWorkStations =
(from workstation in db.Work_Stations
where workstation.Invoices.Last().Invoice_Date < DateTime.Now.AddMonths(-12)
select workstation).Distinct();

The following code will create list of workstations which had invoice in last year
var checkDate = DateTime.Now.AddMonths(-12);
var resultList = db.Work_Stations
.Distinct()
.Select(ws => new {Ws = ws, Li = ws.Invoices.OrderBy(i => i.Invoice_Date).LastOrDefault()})
.Where(item => item.Li != null && Li.Invoice_Date < checkDate)
.Select(item => item.Ws)
.ToList();

This will work even if the invoices are not in date order:
invoiceLst.AddRange(allWorkStations
.Where(w => w.Invoices.Max(i => i.Invoice_Date) < DateTime.Now.AddMonths(-12)));

Related

C# Linq - Refactoring ForEach Loop with Sub List

Am trying to refactor some data in order to display some charts.
I can't seem to figure out why using the following, it lists all the values at the top rather than being sequential like the source data.
var categories = VehicleSales.Select(v => v.name).Distinct().ToList();
var refactoredResults = new List<StackedColumnChart>();
foreach (var category in categories)
{
var subresult = VehicleSales.Where(x => x.vehicleType == category)
.GroupBy(x => x.vehicleType)
.Select(gcs => new StackedColumnChart
{
Category = category,
Values = gcs.Select(x => (int)x.data).DefaultIfEmpty(0).ToList()
}).ToList();
refactoredResults.AddRange(subresult);
}
Source Data:
Then the actual results and expected results:
Thanks in advance!
You can do that without loop and selecting a distinct values, just use GroupBy method and map each group to StackedColumnChart using Select
var refactoredResults = VehicleSales
.GroupBy(s => s.Category)
.Select(g => new StackedColumnChart
{
Category = g.Key,
Values = g.Select(s => s.Value).ToList()
})
.ToList();
If the original data is not sorted and you'll need to sort the values by week number, you can use OrderBy clause before selecting a values Values = g.OrderBy(s => s.WeekNumber).Select(s => s.Value).ToList()

C# Datatable linq - where any

I have a datatable that populates a list. The datatable has 3 columns, 'unit', 'clientId' and 'incident'. The incident could fx be '1. rate paid' or 'resold'.
I'm trying to exclude unit's from the list where any of it's incident is = 'resold', something like:
.Where(r => r.Field<string>("clientId") & r => r.Field<string>("unit").any() != resold))
This is my code:
var units = new List<string>();
if (showIndividualData == true)
{
units = dt.AsEnumerable()
.Where(r => r.Field<string>("clientId") == clientId)
.Select(r => r.Field<string>("unit"))
.Distinct()
.ToList();
}
else
{
units = dt.AsEnumerable()
.Where(r => r.Field<string>("clientId").Length > 0)
.Select(r => r.Field<string>("unit"))
.Distinct()
.ToList();
}
Here's a sample of a working solution. I assumed all string fields but you get the idea. I have also commented each line to explain the process of the linq for easier understanding if you have difficulties.
// some dummy table i assume you have filled
var dt = new DataTable();
// arbitrary client if, you can alter the beginning of the linq for your multi client query somehow
var clientId = "some client";
// the important linq syntax
var units = dt.Rows.Cast<DataRow>() // as datarow array
.Where(dr => dr["ClientId"].ToString() == clientId) // filter client
.GroupBy(dr => dr["unit"].ToString()) // group by unit for easy conditional after
.Where(grp => grp.ToList().All(dr => dr["incident"].ToString() != "resold")) // filter each group and keep only those where all the records are not "resold"
.Select(grp => grp.Key).ToList(); // select the key of the groups which is the "unit" column and already filtered
One can use the Any with the predicate you mentioned...just change it to
&& r.Field<string>("unit").Any(itm => !itm.Equals(resold))
and that will help discriminate it in your Where clause.

Enhance performance with for loop and linq query

i have an object array and i need to get some object Info from database based on certain parameters, i am implementing this:
public IList<object[]> GetRelevants(Registro[] records)
{
List<object[]> returnList = new List<object[]>();
using (var session = SessionFactory.OpenStatelessSession())
{
for (int kr = 0; kr < records.Length; kr++)
{
Registro record = records[kr];
var query = session.QueryOver<Registro>()
.Where(sb => sb.Asunto == record.Asunto)
.And(sb => sb.FechaInicial == record.FechaInicial)
.And(sb => sb.FechaFinal == record.FechaFinal)
.And(sb => sb.FoliosDesde == record.FoliosDesde)
.And(sb => sb.FoliosHasta == record.FoliosHasta)
.And(sb => sb.TomoNumero == record.TomoNumero)
.And(sb => sb.TomoTotal == record.TomoTotal)
.And(sb => sb.SerieCodigo == record.SerieCodigo)
.And(sb => sb.Caja == record.Caja)
.And(sb => sb.Carpeta == record.Carpeta).SelectList(list => list
.Select(p => p.Id)
.Select(p => p.NuevaCaja)
.Select(p => p.NuevaCarpeta)
.Select(p => p.Periodo));
var result = query.SingleOrDefault<object[]>();
returnList.Add(result);
}
}
return returnList;
}
In records however, there are more than 10000 items so NHibernate takes about 10 minutes to do that.
Is there any way to enhance performance in this?
A good solution would probably be to ditch NHibernate for this task and insert your data into a temporary table, then join on that temporary table.
However, if you want to use NHibernate, you could speed this up by not issuing 10,000 separate queries (which is what's happening now). You could try to break your query into reasonably sized chunks instead:
List<object[]> ProcessChunk(
IStatelessSession session,
int start,
IEnumerable<Registro> currentChunk)
{
var disjunction = Restrictions.Disjunction();
foreach (var item in currentChunk)
{
var restriction = Restrictions.Conjunction()
.Add(Restrictions.Where<Registro>(t => t.Asunto == item.Asunto))
/* etc, calling .Add(..) for every field you want to include */
disjunction.Add(restriction);
}
return session.QueryOver<Registro>()
.Where(disjunction)
.SelectList(list => list
.Select(t => t.Id)
/* etc, the rest of the select list */
.List<object[]>()
.ToList();
}
Then call that method from your main loop:
const int chunkSize = 500;
for (int kr = 0; kr < records.Length; kr += chunkSize)
{
var currentChunk = records.Skip(i).Take(chunkSize);
resultList.AddRange(ProcessChunk(session, i, currentChunk));
}
What you're doing here is issuing 20 (instead of 10,000) queries that look like:
select
/* select list */
from
[Registro]
where
([Registro].[Asunto] = 'somevalue' and ... and ... and .. ) or
([Registro].[Asunto] = 'someothervalue' and ... and ... and ... )
/* x500, one for each item in the chunk */
and so on. Each query will return up to 500 records if 500 is the size of each chunk.
This is still not going to be blazing fast. My local test about halved the running time.
Depending on your database engine, you might quickly run up against a maximum number of parameters that you can pass. You'll probably have to play with the chunkSize to get it to work.
You could probably get this down to a few seconds if you used a temporary table and ditched NHibernate though.

Grouping records using LINQ method syntax

I'm trying to find a way to group records by date (not taking into account time) using the LINQ method syntax but only select one instance of each record (which is ItemId within the model)
My simple query is as follows:
range1.Count(x => ((x.OpenedDate >= todayFirst) && (x.OpenedDate <= todayLast))
How could I count the unique records within this range by ItemId?
Sounds like you want:
var query = range1.Where(x.OpenedDate >= todayFirst && x.OpenedDate <= todayLast)
.GroupBy(x => x.ItemId)
.Select(g => new { ItemId = g.Key, Count = g.Count() });
foreach (var result in query)
{
Console.WriteLine("{0} - {1}", result.ItemId, result.Count);
}
It's possible that I haven't really understood you properly though - it's not clear whether you really want to group by date or item ID
EDIT: If you just want the count of distinct item IDs in that range, you can use:
var count = range1.Where(x.OpenedDate >= todayFirst && x.OpenedDate <= todayLast)
.Select(x => x.ItemId)
.Distinct()
.Count();

LINQ - Distinct by value?

Code :
news = (from New myNew in new News()
select myNew).Distinct().ToList();
but this Distinct is for "object" with same values. I need, into my list, a myNew for each month. (so one for january, one for februaru, and so on). Than, news will get 12 record.
Is it possible a sort of Distinct(myNew.Month)?
You could group by month and take the first or last or whatever(you haven't told us):
var news = News()
.GroupBy(n => n.Month)
.Select(grp => grp.Last());
Edit: From the comment on Habib's answer i see that you want 12 months even if there are no news. Then you need to do a "Linq Outer-Join":
var monthlyNews = from m in Enumerable.Range(1, 12) // left outer join every month
join n in News() on m equals n.Month into m_n
from n in m_n.DefaultIfEmpty()
group n by m into MonthGroups
select new {
Month = MonthGroups.Key,
LastNews = MonthGroups.Last()
};
foreach (var m in monthlyNews)
{
int month = m.Month;
var lastNewsInMonth = m.LastNews;
if (lastNewsInMonth != null) ; // do something...
}
Edit: Since you have problems to implement the query in your code, you don't need to select the anonymous type which contains also the month. You can also select only the news itself:
var monthlyNews = from m in Enumerable.Range(1, 12) // every motnh
join n in news on m equals n.Month into m_n
from n in m_n.DefaultIfEmpty()
group n by m into MonthGroups
select MonthGroups.Last();
Note that you now get 12 news but some of them might be null when there are no news in that month.
Solution 1. Get MoreLinq (also available as NuGet package and use
News().DistinctBy(n => n.Property)
Solution 2. Implement an IEqualityComparer and use this Distinct() overload.
var result = News()
.GroupBy(p => p.Month)
.Select(g => g.First())
.ToList();
Short hand solution
var vNews = News()
.GroupBy(p => p.Month, (key, p) => p.FirstOrDefault())
.ToList();
var vNews = News()
.GroupBy(p => p.Month)
.Select(g => g.First())
.ToList();

Categories

Resources