order results of linq query by multiple items - linq

using the following query
Dim allEvents As New List(Of Events)
Dim digits = "1 2 3 4 5 6 7 8 9 0".Split()
Dim results = (From t In ctx.events
Where t.date = todaysdate.Date AndAlso
Not digits.Any(Function(d) (t.symbol.Contains(d)))
Order By t.time
Select New With {
t.symbol,
t.date, t.time,
t.description,
t.domestic_call_num,
t.web_url})
If results.Count > 0 Then
For x As Integer = 0 To results.Count - 1
Dim newevent As New Events
With newevent
.CompanySymbol = results(x).symbol
.EventDate = results(x).date
.EventTime = Left(results(x).time.ToString(), 5)
.EventDescription = results(x).description
If results(x).domestic_call_num IsNot Nothing Then
.DialInNumber = results(x).domestic_call_num
Else
.DialInNumber = ""
End If
If results(x).web_url IsNot Nothing Then
.WebCastUrl = results(x).web_url
Else
.WebCastUrl = ""
End If
End With
allEvents.Add(newevent)
Next
Return allEvents
I get the results I want, but i'd like to further order them by description and time
I tried the following, which should have worked
Dim results = (From t In ctx.events
Where t.date = todaysdate.Date AndAlso
Not digits.Any(Function(d) (t.symbol.Contains(d)))
Order By t.time
Group By t.description
Select New With {
t.symbol,
t.date, t.time,
t.description,
t.domestic_call_num,
t.web_url})
I also tried ThenBy which the compiler didnt like. Any help on how to accomplish this would be appreciated. Essentially I want a sort order of time then description then symbol.

When using integrated syntax, you'd write this as:
Order By t.time, t.description
Select New With {
...
For details, see the Query Expression Examples for ThenBy.

You can try ordering your items at the end of the query like that :
Dim results = (From t In ctx.events
Where t.date = todaysdate.Date AndAlso
Not digits.Any(Function(d) (t.symbol.Contains(d)))
Select New With {
t.symbol,
t.date, t.time,
t.description,
t.domestic_call_num,
t.web_url}).OrderBy(Function(r) r.time)
.ThenBy(Function(r) r.description)
It's probably faster to do it in the query tho.

Related

Search multiple values in one column

How to select record based on multiple values search in one column using linq query.
like product id is "product1", "product2","product3" n number of values we have
You can use the .Contains method to check whether a value is within a list.
var values = new List<string>() { "Prod1", "Prod2", "Prod3" };
var query = context.Set<Product>().Where(x => values.Contains(x.Name));
You could use something like (this is VB.Net, change to C# if necessary)
Dim result = products.Where(Function(p) p.ID = "product1" Or p.ID = "product2" Or p.ID = "product3", ...)
Alternatively, you could pull it all back to the client and use .Contains, like so:
Dim materializedProducts = products.ToList()
Dim result = materializedProducts.Where(Function(p) {"product1", "product2", "{product3}", ...}.Contains(p.ID))
Going further still, you could create an extension method (generic, if that floats your boat) called IsIn or similar that allows you to swap the order of the collection and the search value:
<Extension()>
Public Function IsIn(Of T)(ByVal searchValue As T,
ByVal searchSet As IEnumerable(Of T))
Return searchset.Contains(searchValue)
End Sub
Dim materializedProducts = products.ToList()
Dim result = materializedProducts.Where(Function(p) p.ID.IsIn({"product1", "product2", "{product3}", ...}))

BLToolKit, Linq Query, SQL Not what I expected

I am using BLToolKit in a project of mine and I was trying to get this to work. What I don't like is that I am trying to average a bunch of temps down to the minute, but the select statement that is being generated groups by the minute but then selects the original time. I think I am doing the linq expression correctly (but then again, i am not getting the results i expect). (this is C#, if you care) Anyone know what is going wrong?
var test = (from r in db.SensorReadingRaws
where r.TimeLogged < DateTime.Now.AddMinutes(-2)
group r by new
{
Sensor = r.SensorNumber,
//group time down to the minute
Time = r.TimeLogged.AddSeconds(-1 * r.TimeLogged.Second).AddMilliseconds(-1 * r.TimeLogged.Millisecond)
} into grouped
select new SensorReading
{
SensorNumber = grouped.Key.Sensor,
TimeLogged = grouped.Key.Time,
Reading = (int)grouped.Average(x => x.Reading)
}).ToList();
textBox1.Text = db.LastQuery;
and the resulting query is this
SELECT
[r].[SensorNumber],
[r].[TimeLogged],
Avg([r].[Reading]) as [c1]
FROM
[SensorReadingRaw] [r]
WHERE
[r].[TimeLogged] < #p1
GROUP BY
[r].[SensorNumber],
DateAdd(Millisecond, Convert(Float, -DatePart(Millisecond, [r].[TimeLogged])), DateAdd(Second, Convert(Float, -DatePart(Second, [r].[TimeLogged])), [r].[TimeLogged])),
[r].[TimeLogged]
I discovered that
BLToolkit.Data.Linq.Sql.AsSql<T>(T obj)
can be used as a workaround for this case.
When applying this function to the required grouped key properties in select statement you get rid of grouping/selecting an original field.
It may look something like:
_queryStore.Leads().
GroupBy(x => new {
x.LeadDate.Hour,
x.LeadDate.Minute
}).
Select(x => new {
Hour = Sql.AsSql(x.Key.Hour),
Minute = Sql.AsSql(x.Key.Minute),
Count = x.Count()
});
and in your particular case:
var test = (from r in db.SensorReadingRaws
where r.TimeLogged < DateTime.Now.AddMinutes(-2)
group r by new
{
Sensor = r.SensorNumber,
//group time down to the minute
Time = r.TimeLogged.AddSeconds(-1 * r.TimeLogged.Second).AddMilliseconds(-1 * r.TimeLogged.Millisecond)
} into grouped
select new SensorReading
{
SensorNumber = grouped.Key.Sensor,
TimeLogged = Sql.AsSql(grouped.Key.Time),
Reading = (int)grouped.Average(x => x.Reading)
}).ToList();
I got same issue yesterday.
Today I found a workaround. The idea is to write 2 linq queries. First transforming the data and the second grouping the result:
var bandAndDate =
(from r in repo.Entities
select new {Band = r.Score / 33, r.StartTime.Date});
var examsByBandAndDay =
(from r in bandAndDate
group r by new {r.Band, r.Date } into g
select new { g.Key.Date, g.Key.Band, Count = g.Count() }).ToList();
Both this queries run one SQL that do the job:
SELECT
[t1].[c1] as [c11],
[t1].[c2] as [c21],
Count(*) as [c3]
FROM
(
SELECT
[r].[Score] / 33 as [c2],
Cast(Floor(Cast([r].[StartTime] as Float)) as DateTime) as [c1]
FROM
[Results] [r]
) [t1]
GROUP BY
[t1].[c2],
[t1].[c1]

When using Linq's GROUP BY to create a list of groups, how can I select a specific group by its key?

What I'm trying to do here is to create a list of groups based on the first letter of a book's title, then display all the books in the group below each particular letter. So for example, if the current letter was "C" I would want to select the group where its key was also "C". I thought running a query to create the groups would be more efficient than running a query for each letter, but I'm running in problems.
Here's how I create the group (sorry for the VB) (Model is defined as IEnumerable(of Book):
Dim TitleGroups = From Bk In Model Group Bk By Bk.FirstLetter Into Group Order By FirstLetter
If I then take TitleGroups and iterate through it, everything works fine:
For Each Grp In TitleGroups
Resposnse.Write(Grp.FirstLetter & "<br/>")
For Each Bk In Grp.Group
Response.Write(Bk.Title & "<br/>")
Next
Next
But if I try to select a group, it doesn't work:
Dim CurrentGroup = From Grp In TitleGroups Where Grp.FirstLetter = "A" Select Grp
I can't seem to work with any of the properties I expect on CurrentGroup. I have also tried "Select Grp.Group", which also doesn't seem to help.
Any suggestions would be appreciated!
UPDATE:
em's answer below proved correct, but I thought I'd share the code after translation to VB (don't judge me):
Dim TitleGroups = From Bk In Model _
Order By Bk.FirstLetter _
Group Bk By Bk.FirstLetter Into Books = Group, BkGrp = AsEnumerable() _
Select FirstLetter, Books
Dim CurrentGroup = From Grp In TitleGroups _
Where Grp.FirstLetter = "A" _
Select Grp
For Each Grp In CurrentGroup
Response.Write(Grp.FirstLetter & "<br/>")
For Each Book In Grp.Books
Response.Write(Bk.Title & "<br/>")
Next
Next
You have to explicitly define your projection (ie. "pack" your group). Some useful LINQ code samples are available here.
see below for a working C# version of your code:
var books = new[] { new Book("A book"),
new Book("Your Book"),
new Book("My book"),
new Book("Anne's book") };
var titleGroups = from book in books
orderby book.FirstLetter
group book by book.FirstLetter
into bookGroup select new {FirstLetter = bookGroup.Key, Books = bookGroup};
var currentGroup = from Grp in titleGroups
where Grp.FirstLetter == "A"
select Grp.Books;
foreach (var group in currentGroup)
{
Console.WriteLine(group.Key); // First letter
foreach (var book in group)
{
Console.WriteLine(book.Title);
}
}

LINQ. Grouping by days. How to do this easily?

I can't seem to find any good reference on this. I have a lot of data in SQL with dates. So I wanted to make a line chart to show this data over time. If I want to show it over a period of days then I need to group by days.. But the LOGDATE is the full date.. not the DAY..
So I have this below, but LINQ doesn't know what 'DayOfYear' property is...
var q = from x in dc.ApplicationLogs
let dt = x.LogDate
group x by new { dayofyear = dt.Value.DayOfYear } into g
select new
{
iCount = g.Count(),
strDate = g.Key
};
In EF core you can use DateTime.Date to get the date portion of a DateTime value.
In EF6 you can use DbFunctions.TruncateTime:
to return the given date with the time portion cleared
var q = from x in dc.ApplicationLogs
let dt = x.LogDate.Date
// EF6:let dt = DbFunctions.TruncateTime(x.LogDate)
group x by dt into g
select new
{
iCount = g.Count(),
strDate = g.Key
};
You want .Date to get the date part of a DateTime not DayOfyear unless you are deliberately trying to put the same day of the year from each year into the group.
Why are you using:
let dt = x.LogDate
group x by new { dayofyear = dt.Value.DayOfYear } into g
instead of just:
group x by x.LogDate.Value.DayOfYear into g
I'm not sure about this, but it's possible using an anonymous object like that in your group by clause is messing up L2S.
Here you go
let d = new DateTime(n.time.Year, n.time.Month, n.time.Day) group n by d into grp select grp

LINQ | How do I get SUM without grouping?

Crazy question...however, I want the sum of all the rows in a table for a column (without using the group by clause)
Example:
Table = Survey
Columns = Answer1, Answer2, Answer3
1 1 1
4 3 5
3 3 2
I want the sums for each column.
Final results should look like:
Answer1Sum Answer2Sum Answer2Sum
8 7 8
This doesn't work:
from survey in SurveyAnswers
select new
{
Answer1Sum = survey.Sum(),
Answer2Sum = survey.Sum(),
Answer3Sum = survey.Sum()
}
Would this work:
var answer1Sum = SurveyAnswers.Sum( survey => survey.Answer1 );
var answer2Sum = SurveyAnswers.Sum( survey => survey.Answer2 );
var answer3Sum = SurveyAnswers.Sum( survey => survey.Answer3 );
A VB.NET soltuion to this answer for anyone that needs it is as follows:
Dim Answer1Sum = SurveyAnswers.Sum(Function(survey) survey.Answer1)
Dim Answer2Sum = SurveyAnswers.Sum(Function(survey) survey.Answer2)
Dim Answer3Sum = SurveyAnswers.Sum(Function(survey) survey.Answer3)
SurveyAnswers.Sum(r => r.Answer1);

Resources