I want to get an item from db, and items that come before and after it.
var data= repo .OrderBy(a => a.Date)
.Select((item, index) => new { item, index })
.Where(itemAndIndex=>itemAndIndex.item.Id == someId)
this is what I got so far.
To clarify,
lets say this is my Table
Id Name Date
1 SomeText1 01.01.2017
2 SomeText2 03.01.2017
3 SomeText3 02.01.2017
4 SomeText4 04.01.2017
5 SomeText5 05.01.2017
I want to do a select query for Id==3
, then sort the result by the Date field, and get a list of items
Id Name Date
1 SomeText1 01.01.2017
3 SomeText3 02.01.2017
2 SomeText2 03.01.2017
like this.
Thank you.
Here is the same idea as in Harald Coppoolse's answer (which I think is the only reasonable way to satisfy your requirement), but with best (IMO) LINQ to Entities SQL query translation (assuming repo
is IQueryable<T>
representing your table):
var data = repo
.Where(elem => elem.Id == someId)
.SelectMany(elem =>
repo.Where(e => e.Date < elem.Date).OrderByDescending(e => e.Date).Take(1)
.Concat(new[] { elem })
.Concat(repo.Where(e => e.Date > elem.Date).OrderBy(e => e.Date).Take(1)))
.ToList();
The simplest way:
var data = repo.OrderBy(x => x.Date)
.Select((item, index) => new { item, index });
var idx = data.First(x => x.item.Id == someId);
var result = data
.Where(x => x.index >= idx.index)
.Take(3);
This is not for EF
, but will also work with slight modifications:
public class Data
{
public int id;
public string name;
public DateTime date;
}
class Program
{
static void Main(string[] args)
{
List<Data> l = new List<Data>
{
new Data { id = 1, name = "Name1", date = DateTime.Parse("2017/01/01")},
new Data { id = 2, name = "Name2", date = DateTime.Parse("2017/01/03")},
new Data { id = 3, name = "Name3", date = DateTime.Parse("2017/01/02")},
new Data { id = 4, name = "Name4", date = DateTime.Parse("2017/01/04")},
new Data { id = 5, name = "Name5", date = DateTime.Parse("2017/01/05")},
};
int id = 2;
var result = l.Where(c => c.id == id)
.Union(l.Where(c => c.date < l.Where(t => t.id == id).Select(d => d.date).First()).OrderByDescending(c => c.date).Take(1))
.Union(l.Where(c => c.date > l.Where(t => t.id == id).Select(d => d.date).First()).OrderBy(c => c.date).Take(1)).ToList();
}
}
another The simplest way:
var data= repo.OrderBy(c => c.Date)
.Where(c => c.Id == id || c.Id == id - 1 || c.Id == id + 1)
.Select(c => new { c.Id, c.Name,c.Date });
You forgot to mention what should be filled if your item with someId is the first or the last in your ordered list in which case you don't have a previous or next item.
And what do you want if there is no item with someId? an empty sequence or a sequence with three NULL values?
Furthermore, are your date values unique? What do you want as previous item and next item if you have several items with the same date?
Setting these problems aside, looking at your problem, from your source sequence of RepoElements, you want to select a sequence of 3 RepoElements, where
From your repo sequence, use a Where to select the result 1 . After that use a Select with Min and Max to find result[0] and result[2] and put all three results in an array. Finally use SingleOrDefault to select the one and only element with the correct result 1 :
var result = repo
.Where(repoElement => repoElement.Id == someId)
.Select( result1 => new RepoElement[]
{
// [0]: the largest element with date < date of item with Id == someId
repo.Where(repoElement => repoElement.Date < result1.Date)
.Max(repoElement => repoElement.Date),
// [1] the item with Id == someId
result1,
// [2] the smallest element date > date of item with Id == someId
repo.Where(repoElement => repoElement.Date > result1.Date)
.Min(repoElement => repoElement.Date),
})
.SingleOrDefault();
The first Where is done using your index: fast. The Select needs two passes: one to find the largest element that is smaller than your middle element and one to find the smallest element that is larger than your middle element. Still I think in most cases that will be faster than a sort
Note that if there are no elements with smaller / larger dates, then the result of Max will be default, which in your case is NULL
If your Date is not unique, you will find the largest cq smallest value that does not have date.
Addition
I just realized that function Max that I used does not return the element with the Max value, but the Maximum value itself. Luckily MSDN Enumerable.Max describes that the function Max uses IComparable.
Consider to let your class would implement IComparable, which would rank by Date.
private class RepoElement: IComparable<MyData>
{
public int Id {get; set;}
public string Name {get; set;}
public DateTime Date {get; set;}
public int CompareTo(RepoElement other)
{ // this object smaller than other object
// if this date smaller than other date
return this.Date.CompareTo(other.Date);
}
}
var result = repo
.Where(repoElement => repoElement.Id == someId)
.Select( result1 => new RepoElement[]
{
repo.Where(repoElement => repoElement.Date < result1.Date).Max(),
repoElement,
repo.Where(repoElement => repoElement.Date > result1.Date).Min(),
}
This would use extension function Enumerable.Max() which returns a TSource.
Although this would work, this method has the disadvantage that it only works on IEnumerable, not on IQueryable. It can't be performed on the database side.
If your collection is very large, and you really need to perform it as an IQueryable (on the database side), consider using Enumerable.Aggregate to find elements [0] and [2].
Aggregate compares the two first elements of the sequence, decides which one is the "best", and uses this "best" one to compare with the third item to decide which one is the "best" again and use this one to compare with the fourth item, etc. Finally it returns the "best".
var result = repo
.Where(repoElement => repoElement.Id == someId)
.Select(repoElement => new RepoElement[]
{
// [0]: take only the elements that are smaller,
// aggregate to find the largest one:
repo.Where(element => element.Date < repoElement.Date)
.Aggregate(
// compare the largest item already found, with the current one
// and take the largest one as largest
(largest, next) => next.Date > largest.Date ? next : largest),
// [1]
repoElement,
// [2]: take only the elements that are bigger
// aggregate to find the smallest one
repo.Where(element => element.Date > repoElement.Date)
.Aggregate(
// compare the smallest item already found, with the current one
// and take the smallest one as smallest
(smallest, next) => next.Date < smallest.Date ? next : smallest),
})
.SingleOrDefault();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.