Need lambda expression OrderBy with DateTime conversion - c#

I am trying to create a lambda expression (Linq, C# 3.5) that can perform a OrderBy on a value that is of data type String but which actually contains a parse-able DateTime.
For example, typical values may be "5/12/2009" , "1/14/2008", etc.
The OrderBy clause below works correctly for ordering (as if string data), but I actually want to treat the values as DateTimes, and perform the sort by Date. (The sortColumn would be something like "dateCreated".)
List<MyObject> orderedList = unorderedList.OrderBy(p => p.Details.Find(s => s.Name == sortColumn).Value).ToList();
Is there a way to convert the values in the predicate to do this? Any help appreciated!

Rather gross and inefficient:
List<MyObject> orderedList = unorderedList.OrderBy(p => DateTime.Parse(p.Details.Find(s => s.Name == sortColumn).Value)).ToList();
To reduce the number of lookups/parsing:
List<MyObject> orderedList =
(from extracted in (from p in unorderedList
select new { Item = p, Date = DateTime.Parse(p.Details.Find(s => s.Name == sortColumn).Value })
orderby extracted.Date
select extracted.Item)
.ToList();

Project the date/time value and then sort by it.
var orderedList =
(from p in unorderedList
let value = DateTime.Parse(p.Details.Find(s => s.Name == sortColumn).Value)
orderby value
select p)
.ToList();

Related

ASP.NET Core SQL Linq using list of strings

I have a list of strings like this
list = {a,b,c}
I would like to compare it in the database but I'm not sure how to go about doing that. This is my linq query:
var initialData = (from item in dbContext.DocumentCategories
join df in dbContext.DocumentFields
on item.Id equals df.DocumentCategoriesId
join dfs in dbContext.DocumentFieldsStore
on df.Id equals dfs.DocumentFieldsId
select new SearchDocumentsListViewModel
{
CategoryId = item.Id,
DocumentId = dfs.DocumentsId,
FieldId = df.Id,
Data = dfs.Data
})
.ToList();
initialData = initialData
.Where(u => u.Data.Contains(list))
.ToList();
Currently its showing me this error:
cannot convert from 'System.Collections.Generic.List' to
'char'
Not sure what that means
There is a Where statement at the end that has the Contains expression backwards.
.Where(u => u.Data.Contains(list)
This line of code explained:
u.Data is being treated as a char[] and a char cannot contain a list.
This line of code needs to have the contains flipped around to:
.Where(u => list.Contains(u.Data)

linq - find item in list within multiple lists

I have a highly nested class, and trying to find a single item buried deep within. The following gives me an error "Can't convert type match to bool', although I don't see why it thinks I'm trying to return a boolean.
var match = community.TeamLeagues
.Where(x => x.Seasons
.Where(y => y.Divisions
.Where(z => z.Matches
.Where(a => a.Id == "1234").FirstOrDefault())));
Where by itself returns a (deferred) enumerable of items and cannot as such be used as a condition by the outer Where. What you probably want to do is to use Contains(), Any() or All() inside the outer Wheres that will return the result you're looking for.
Something like this might be what you're after:
var match = community.TeamLeagues.Where(t =>
t.Seasons.Any(
s => s.Divisions.Any(
d => d.Matches.Any(
m => m.Id == "1234")
)));
The Where method needs to evaluate an expression that returns a bool. Your nested Wheres are not doing that - the only Where that is, is the last one a => a.Id == "1234", all the other expressions are returning an IEnumerable.
z.Matches.Where(a => a.Id == "1234").FirstOrDefault() returns a object of type Match(your collection item type of the IEnumerable Matches) (or null), no boolean value. I guess you need to check if there are entires in matches that have a Id 1234. Use Any to evaluate a condition:
var match = community.TeamLeagues.Where(x =>
x.Seasons.Any(y =>
y.Divisions.Any(z =>
z.Matches.Any(a => a.Id == "1234")
)));
[items.Where(x => x.Id == 4).Any() is the same as items.Any(x => x.Id == 4)]
This returns you all TeamLeagues which contain a Season which contain a Division which contain a Match which has a element with the id 1234.
To make it simple you can also use the Matches table directly and using a ViewModel you can represent your view.
like:
var MyViewModel = (from l in Mathes
where l.Id == "1234"
select new MyViewModel
{
Id = l.Id,
MatchName = l.Name,
}).ToList();
Couldn't get it working with linq, but works with query syntax.
var leagueMatch = (from teamLeague in community.TeamLeagues
from season in teamLeague.Seasons
from division in season.Divisions
from match in division.Matches.Where(x => x.Id == "1234")
select match).FirstOrDefault();

how to use linq to return a value from 4 tables

I am reasonably new to Linq and it seems quite easy to iuse but I am having an issue when trying to extract a value from a table that is linked/constrained by 3 other tables.
I have this in my SQL DB:
I am using Asp.Net 4 and Entity Framework 6.
I have as a parameter the 'DatabaseName'.
I ultimately want to get the SubscriptionRef that is assigned to this name.
I could do this step-by-step (ie using multiple linqs) but I thought it would look 'clean' using just 1 linq statment.
I have got as far as this:
var names = o.RegisteredNames.Where(d => d.DatabaseName == DBName).Where(d => d.ClientNames.Where(f => f.ClientId == f.Client.ClientId).FirstOrDefault();
But I get the error:
Cannot implicitly convert type 'Services.ClientName' to 'bool'
You have a Problem here:
d => d.ClientNames.Where(f => f.ClientId == f.Client.ClientId)
f => ... returns a single ClientName or null, which causes your error, because there should be a boolean.
If you want this first value or null, you should replace
.Where(d => d.ClientNames ...
//with
.Select(d => d.ClientNames ...
Try this:
o.RegisteredNames.First(d => d.DatabaseName == DBName).ClientNames.Select(x=>x.Client.Subscription.SubscriptionRef)
It should give you list go SubscriptionRef.
You can try with one LINQ query like...
var names = o.RegisteredNames.Where(d => d.DatabaseName == DBName ).FirstOrDefault();
You might wanna try sql style:
var client = from c in db.Clients
join cn in db.ClientNames on c.ClientId equals cn.ClientId
join rn in db.RegisteredNames on cn.RegisteredNamesId equals rn.RegisteredNameId
where rn.DatabaseName == "YourDBName"
select c;
But it also depends on how your objects were built.
Try using join:
var names = (
from names in o.RegisteredNames.Where(d => d.DatabaseName == DBName)
join cnames in o.ClientNames on names.RegisteredNamesId equals cnames.RegisteredNamesId
select cnames.ClientId
).FirstOrDefault();
Add as many joins as you want.
Try this
It works in List,
var option1= o.RegisteredNames
.Where(g => g.DbName == "YourDbName")
.Where(h => h.ClientNames.Any(f => f == 5))
.FirstOrDefault();
var option2= o.RegisteredNames
.FirstOrDefault(h => h.DbName == "Name" && h.ClientNames.Any(j => j == 1));

Linq: GroupBy and Sum

I'm having the hardest time wrapping my mind around LINQ .
I'm querying database
date ColumnC
25-04-2016 10
01-05-2016 8
10-05-2016 4
That I need to Group and Sum by year and month.
I tried to do this but it doesn't work :
public TYPE getX(DateTime value) {
var total = from p in context.table
.Where(p => p.date.Year == value.Year)
group p by p.date.Year into dp
select new
{
result = dp.Sum(s => s.ColumnC),
};
return total;
}
i also could not know the return type.
in other words :
how to get a linq query from this :
select Month(date) Month ,sum(ColumnC) result
from table
group by(Month(date))
==============
Thanks in advance
If you want to have result you have to cast your sum explicitly, therefore losing some data. This is necessary because your initial data is decimal. Therefore the code:
var total = (from p in context.table
.Where(p => p.date.Year == value.Year)
group p by p.date.Month into dp
select new KeyValuePair<int,int> (dp.Key, (int)dp.Sum(s => s.ColumnC))
);
The result of this will be IQueryable<KeyValuePair<int,int>> which is one way to also know the type.
Second option which is more correct would be not to cast the decimal to integer, therefore the code:
var total = (from p in context.table
.Where(p => p.date.Year == value.Year)
group p by p.date.Month into dp
select new KeyValuePair<int,decimal> (dp.Key, dp.Sum(s => s.ColumnC))
);
The result will be IQueryable<KeyValuePair<int,decimal>> which is pretty easy to work with.
In both cases the Key is the Month and the Value is the Sum of ColumnC
Your return type here is partially anonymous, which means you cannot ever know the type.
You can, however, directly retrieve the result as part of the grouping:
public IEnumerable<IGrouping<int, decimal>> getTotalHeures(DateTime value)
{
var total = from p in context.table
.Where(p => p.date.Year == value.Year)
.GroupBy(p => p.date.Month, p => p.ColumnC);
return total;
}

How to filter entity framework result with multiple columns using a lambda expression

I have the following table:
And the following data:
How can i filter the result, so that i only get the latest row from each omraade_id (sorted descending by timestamp)?
Which in this case would be the rows with id: 1010 and 1005
--
From #lazyberezovsky's answer, i have created the following expression:
dbConnection = new ElecEntities();
var query = from data in dbConnection.Valgdata
orderby data.timestamp descending
group data by data.omraade_id into g
select g.FirstOrDefault();
return query.ToList();
It returns two rows with the ID 3 and 4, which are the first two rows in the database, and also the ones with the lowest timestamp. Any idea why?
var query = dbConnection.Valgdata
.GroupBy(x => x.omraade_id)
.Select(g => g
.OrderByDescending(x => x.timestamp)
.FirstOrDefault());
I have no experience with EF, so I'm unsure if only SQL-esque linq works here. A plain C#-ish:
var query = dbConnection.Valgdata.GroupBy(u => u.omraade_id)
.Select(x => x.FirstOrDefault(y => x.Max(p => p.timestamp) == y.timestamp));
You have put filter on every item. It should be applied on complete query result, not on every item.
Following is updated query.
var query = (from data in dbConnection.Valgdata
orderby data.timestamp descending
group data by data.omraade_id into g
select g).FirstOrDefault();
var query = from v in dbConnection.Valgdata
orderby v.timestamp descending
group v by v.omraade_id into g
select g.First();
This will return only record with max timestamp for each omraade_id.
UPDATE query above works fine to me (at least for MS SQL Linq provider). Also you don't need to do FirstOrDefault - if omraade_id is grouped, then it definitely has at least one row.
var query = from v in dbConnection.Valgdata
group v by v.omraade_id into g
select g.OrderByDesc(x => x.timestamp).First();
This is my solution so far:
var data = dbConnection.Valgdata.Where(x => x.godkendt == false).ToList();
var dataGrouped = data.GroupBy(x => x.omraade_id).ToList();
List<Valgdata> list = new List<Valgdata>();
foreach (var grpdata in dataGrouped)
{
var dataGroup = grpdata.OrderByDescending(x => x.timestamp).ToList();
list.Add(dataGroup.FirstOrDefault());
}
return list;
I dont know if it is the most effective, but it works.

Categories