Can you dynamically cast during runtime inside a linq statement? - c#

Hello is there a way to dynamically cast inside the following code so I do not need a bunch of if statements with nearly identical code?
List<DateTime> dateTimeList = null;
if(_dataSeriesList[0].GetType().Name == "Class1")
{
dateTimeList =
_dataSeriesList.ConvertAll(x => (Class1) x)
.Where(d => d.Time >= min && d.Time <= max)
.OrderBy(t => t.Time)
.Select(d => d.Time)
.ToList();
}
else if(_dataSeriesList[0].GetType().Name == "Class2")
{
dateTimeList =
_dataSeriesList.ConvertAll(x => (Class2) x)
.Where(d => d.Time >= min && d.Time <= max)
.OrderBy(t => t.Time)
.Select(d => d.Time)
.ToList();
}
.
.
and so on
I tried using the following code:
public static T Cast<T>(object o)
{
return (T)o;
}
Type t2 = _dataSeriesList[0].GetType();
dateTimeList =
_dataSeriesList.ConvertAll(x => castMethod.Invoke(null, new object[] { x }))
.Where(d => d.Time >= min && d.Time <= max)
.OrderBy(t => t.Time)
.Select(d => d.Time)
.ToList();
But then the linq statement will not compile.

If they don't share a common base type you can do it like this:
dateTimeList =
_dataSeriesList.Select(x => {
if (x.GetType().Name == "Class1")
return ((Class1)x).Time;
else
return ((Class2)x).Time;
})
.Where(d => d >= min && d <= max)
.OrderBy(t => t)
.Select(d => d)
.ToList();
Since all you are doing is working with the time element you just select out the item of interest according to your rules and then use it in a common way.
Of course if what you are working with is more complicated you select that information instead.
This is fairly standard in Linq -- make a new type on the fly that you need in order to do your work.
((as has been pointed out in the comments "on the fly" is actually determined at compile time and not dynamic in the typical use of the word))

Related

Use same, looked-up value across multiple linq Where clauses without looking up more than once

I have a LINQ query which has three Where clauses. In each Where clause I am looking up the same set of items in order to compare values:
var items = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children
.Where(x => level1Category == 0 || x
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id).Contains(level1Category))
.Where(x => !level2Categories.Any() || x
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id).Intersect(level2Categories.AsEnumerable()).Any())
.Where(x => !level3Categories.Any() || x
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id).Intersect(level3Categories.AsEnumerable()).Any());
Is there a way I can get the value of UmbracoAlias.Items.Categories once and store that value to be used in the other where clauses without causing the GetPropertyValue method to execute more than once?
You can pair up each item with category IDs, like this:
var items = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children
.Select(c => new {
Child = c
, CategoryIds = c
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id)
.ToList()
})
.Where(x => level1Category == 0 || x.CategoryIds.Contains(level1Category))
.Where(x => !level2Categories.Any() || x.CategoryIds.Intersect(level2Categories.AsEnumerable()).Any())
.Where(x => !level3Categories.Any() || x.CategoryIds.Intersect(level3Categories.AsEnumerable()).Any())
.Select(x => x.Child);
This does the filtering on children paired up with their category IDs, and then keeps only the Child object in the final projection.
You could further simplify this by combining all three Where clauses:
var items = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children
.Where(c => {
var categoryIds = c
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id)
.ToList();
if (level1Category != 0 && !categoryIds.Contains(level1Category)) {
return false;
}
if (level2Categories.Any() && !categoryIds.Intersect(level2Categories.AsEnumerable()).Any()) {
return false;
}
if (level3Categories.Any() && !categoryIds.Intersect(level3Categories.AsEnumerable()).Any()) {
return false;
}
return true;
});

IEnumerable<List<T>> to List<T>

I'm working on a query of in-memory objects:
var rankingSummary = resultSet
.Where(r => r.Accuracy >= 95 && r.Accuracy <= 105)
.Select(r => r.Results).Where(r => r.ResultType == 1)
.Select(r => r.Subjects)
Subjects is a List<Subject> property that the Result class has. Where I'm at now in the query is with an IEnumerable<List<Subject>>. What I would like is for them to all be joined into a single List as I have further querying to do based on the properties of each Subject. Is there an elegant or practical way to achieve this?
I believe this should work:
var rankingSummary = resultSet
.Where(r => r.Accuracy >= 95 && r.Accuracy <= 105)
.Select(r => r.Results).Where(r => r.ResultType == 1)
.SelectMany(r => r.Subjects)
.ToList();
What you're looking for is SelectMany.
var rankingSummary = resultSet
.Where(r => r.Accuracy >= 95 && r.Accuracy <= 105)
.Select(r => r.Results).Where(r => r.ResultType == 1)
.SelectMany(r => r.Subjects)
.ToList();

Return results for closest Date if given Date does not exist in database using LINQ

I am trying to return a set of results based on a given date and if that date does not exist then then I want to return the result from the closet past date to that.
I am trying to return the results from an ApiController. The method I am using is pretty slow and I'm sure it's not the best one.
[HttpPost]
public IHttpActionResult GetItemsForDate(DateDTO Date)
{
using (var context = new CafeteriaContext())
{
bool vreauTOT = Date.vreauTOT;
var itemsList = new List<MenuItem>();
var getDates = context.MenuItems.Where(d => d.Date == Date.Date || d.Date < Date.Date).Select(d => d.Date).ToList();
var availableDate = getDates.OrderByDescending(t => t.Date).First();
if (vreauTOT)
{
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Select(r => r)
.ToList();
}
else
{
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Where(d => d.OnlyExternal == false)
.Select(r => r)
.ToList();
}
return Ok(itemsList);
}
Is it possible to save a trip to the database and maybe construct a single query that will return the same results ? Or maybe a faster way than what I am doing right now.
You probably don't need if .. else here. It can be reduced to below using compound condition
itemsList = context.MenuItems
.Where(d => d.Date == availableDate && (!vreauTOT && d => !d.OnlyExternal))
.ToList();
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Select(r => r)
.ToList();
No need to use Select here.
itemsList = context.MenuItems
.Where(d => d.Date == availableDate)
.Where(d => d.OnlyExternal == false)
.Select(r => r)
.ToList();
No need to use Select here.
Use 1 where and check the conditions there:
.Where(d => d.Date == availableDate && ! d.OnlyExternal)
Explanation: each LINQ-method will perform a loop in the background, and the more loops you create, the slower it will run.

Filtering a group by linq query

var testingAll = (from ac in metaData.AcTable
where ac.Call >= DateTime.Now.AddMonths(-2) && ac.Call <= DateTime.Now
group adminCall by ac.LanguageCode into acc
select new { lang = acc.Key, count = acc.Count() }).ToDictionary(x => x.lang, y => y.count).OrderByDescending(x => x.Key);
Can I have filter again after the datetime ?
Something like this:
var Today = testingAll.Where( /*x => x.Call >= DateTime.Now.AddDays(-2)*/)
I think you want something like
var testingAll = (from ac in metaData.AcTable
where ac.Call >= DateTime.Now.AddMonths(-2) && ac.Call <= DateTime.Now
group adminCall by adminCall.LanguageCode into ac
select ac
this should give back a collection where you can then query a number of times.
The short answer is that you can't do this. Think of it this way, the problem is effectively the same as me giving you the average age of children in a class and then you taking that number and trying to work out the average age of the boys only - it's not possible without the source data.
Now you could maybe do this by building an expression and spending a lot of effort there, but it would still have to re-query the database.
If you really want to abstract it away slightly, then you could create a function that takes the where predicate as a parameter:
public IEnumerable<KeyValuePair<string, int>> GetFilteredCalls(
Expression<Func<Call, bool>> predicate)
{
return calls
.Where(predicate)
.GroupBy(c => c.LanguageCode)
.Select(g => new { Lang = g.Key, Count = g.Count() })
.ToDictionary(x => x.Lang, y => y.Count)
.OrderByDescending(x => x.Key);
}
And you use it like this:
var calls = GetFilteredCalls(c => c.Call >= DateTime.Now.AddMonths(-2)
&& c.Call <= DateTime.Now);
var moreCalls = GetFilteredCalls(c => c.Call >= DateTime.Now.AddDays(-2)
&& c.Call <= DateTime.Now);

Get index of lines that match my linq

I have a linq statement and I would like to know if it is possible to get indicies of lines that match my statement? Here it is:
var result = list3.Where(middle => list4.Any(x => x == middle.Middle.category1)).Select(obj => new { obj, dt = DateTime.ParseExact(obj.LeftColumn, dateFormat, CultureInfo.InvariantCulture) })
.Where(x => x.dt >= datetimepickerChoice1 && x.dt <= datetimepickerChoice2)
.Select(x => x.obj).ToList();
You can use the overload of Select (or Where) which projects also the index of the element:
var result = list3.Select((middle, index) => new{ middle, index })
.Where(x => list4.Any(xx => xx == x.middle.Middle.category1))
.Select(x => new { x.middle, x.index, dt = DateTime.ParseExact(x.middle.LeftColumn, dateFormat, CultureInfo.InvariantCulture) })
.Where(x => x.dt >= czas11 && x.dt <= czas22)
.Select(x => x.index)
.ToList();
Side-note: consider to change your variable names to be more meaningful. That is unreadable.
do you mean this?
var result = list3.Where(middle => list4.Any(x => x == middle.Middle.category1))
.Select(obj => new { obj, dt = DateTime.ParseExact(obj.LeftColumn, dateFormat, CultureInfo.InvariantCulture) })
.Where(x => x.dt >= czas11 && x.dt <= czas22)
.Select((x,index) =>new{ x.obj,Index=index}).ToList();
Also note that if you want to search for the indicies of items matching a predicate very often, it could be worth writing a very simple extension method:
public static class IEnumerableExt
{
public static IEnumerable<int> FindIndices<T>(this IEnumerable<T> self, Predicate<T> predicate)
{
int i = 0;
foreach (var element in self)
{
if (predicate(element))
yield return i;
++i;
}
}
}
Which you would call like this:
var result = list3.FindIndices(x => list4.Any(xx => xx == x.middle.Middle.category1));

Categories

Resources