I just want to count users where their subscription end date is within the coming month:
int Count = oUsers
.Where(x =>
0 < x.SubscriptionEnddate.Value.Subtract(DateTime.Now).Days < 30)
.Count();
But its not working.
What I want to do is 0 < Value < 30.
Use &&. Period. It is where it is designed for.
You can circumvent this by creating a Between extension method, or concatenate two Where clauses, but really, why trade that over &&. But if you insist:
int c = oUsers
.Select(x=> x.SubscriptionEnddate.Value.Subtract(DateTime.Now.AddDays(30)).Days)
.Where(d => 0 < d)
.Where(d => d < 30)
.Count();
you can try something like this
int count = oUsers.Where(x=> x.Days > 0).Count(x => x.Days < 30);
I would do it this way to keep it readable:
int Count =
oUsers
.Select(u => new
{
u,
days = u.SubscriptionEnddate.Value.Subtract(DateTime.Now.AddDays(30)).Days,
})
.Where(x => x.days > 0 && x.days < 30)
.Select(x => x.u)
.Count();
This obviously uses && but it eliminates the code duplication which I think is what you're really trying to avoid.
The use of .Count() at the end removes the need to keep track of the original value, so, as Patrick has already posted, this suffices:
int Count =
oUsers
.Select(u => u.SubscriptionEnddate.Value.Subtract(DateTime.Now.AddDays(30)).Days)
.Where(x => x > 0 && x < 30)
.Count();
Not always the shorter why is better.
When someone else will read your code it will be much easier for him to understand when you calling SomeRange method
I think the best why is when the code more readable so you can do method for returning if your value is match the start and end ranges and call it.
example for some int range but the same why you can check dates
public static bool SomeRange(int value,int start,int end)
{
return (value > start && value < end);
}
static void Main()
{
List<int> valueList = new List<int> { 1,65,3,76,34,23,11,5,665,334};
var result = valueList.Where(x => SomeRange(x, 0, 30)).Count();
}
Yes Solve it with Enumerable.Range:
oUsers.Where(x => x.SubscriptionEnddate != null && Enumerable.Range(0, 30).Contains(x.SubscriptionEnddate.Value.Subtract(DateTime.Now).Days)).Count();
I'd say, precompute the range start and end before executing the count, then compare for start && end:
// some test data
var oUsers = Enumerable.Range(0, 100).Select(x => new { SubscriptionEnddate = (DateTime?)DateTime.Now.AddDays(x) });
var rangeStart = DateTime.Now;
var rangeEnd = rangeStart.AddDays(30);
// Conditional count... can also be done as .Where(condition).Count()
int Count = oUsers.Count(x => x.SubscriptionEnddate >= rangeStart && x.SubscriptionEnddate < rangeEnd);
Possibly use DateTime.Today instead since you are only interested in days.
You could use nested if statements, if you're really opposed to using &&
if x.SubscriptionEnddate.Value.Subtract(DateTime.Now.AddDays(30)).Days > 0 {
if x.SubscriptionEnddate.Value.Subtract(DateTime.Now.AddDays(30)).Days < 30 {
int count = oUsers;
}
}
This seems like an incredibly obtuse way to go about this though. This is why we have the && operator.
Related
currently in the code I am outputting the employees table which have end dates of anything from before today's date and up to 90 days. I am trying to make it so only employees with maximum -30 days and + 90 of today's date.
(Fairly new so be easy)
(NOT SURE HOW TO CORRECTLY USE 2 DATES FOR .ADDDAYS )
Thanks
public ActionResult Index()
{
var employees = db.employees;
var today = DateTime.Today.AddDays(90);
var past = DateTime.Today.AddDays(-30);
var q = db.employees.Where(t => t.EndDate <= today );
return View(q.OrderByDescending(t => t.EndDate));
}
Why can't you use a AND (&&) condition like
var q = db.employees.Where(t => t.EndDate <= today && t.EndDate >= past);
It is simple by using conditional-AND && operator & combining both queries as single line:
var q = db.employees.Where(t => t.EndDate >= past && t.EndDate <= today)
.OrderByDescending(t => t.EndDate);
return View(q);
Try following code. You can add the OrderByDescending(t => t.EndDate) on the first line of code.
var q = db.employees.Where(t => t.EndDate >= past && t.EndDate<= today)OrderByDescending(t => t.EndDate);
return view(q);
I'm working on an nhibernate query where I need to select all records in a table where the id matches any id in an array I have.
so I have int[] ids and I need a
.Where(x => x.id == ids[0]
|| x.id == ids[1]
|| x.id == ids[2]
etc... but the array can have a variable number of ids in it. What's the correct way of doing this?
I'm not sure what to search either, otherwise I would've found something on google probably
NHibernate can convert a Contains call to In query in SQL.
.Where(x => ids.Contains(x.id));
You can use IsIn():
.WhereRestrictionOn(x => x.Id).IsIn(ids);
You can also try with:
.Where(x => Array.IndexOf(i, x.Id)>-1 );
Pros:
+NHibernate is not using sql - like IsIn()
+Is 3x faster than Cointains()
here u find code to test it
static void Main(string[] args)
{
int[] i = new[] { 1, 2, 3, 4, 5, 6, 7 };
Stopwatch stopwatch = Stopwatch.StartNew();
for (int j= 0; 1000000 > j; j++)
{
int pos = Array.IndexOf(i, 5);
if (pos > -1)
{ }
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Stopwatch stopwatch2 = Stopwatch.StartNew();
for (int j = 0; 1000000 > j; j++)
{
bool pos = i.Contains(5);
if (pos)
{ }
}
stopwatch2.Stop();
Console.WriteLine(stopwatch2.ElapsedMilliseconds);
Console.Read();
}
Given the following contrived example:
public bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers) {
var numberOfEvens = numbers.Count(x => x % 2 == 0);
var numberOfOdds = numbers.Count(x => x % 2 != 0);
return numberOfEvens == numberOfOdds;
}
This works but requires multiple enumerations of the collection.
Is it possible to re-write this to use a single linq expression that enumerates the collection once.
NOTE: I'm trying to solve the general case of comparing counts with two filters so try and ignore the fact that the sample is about odd and even numbers.
I have included a sample .NET Fiddle
A bit cryptic at first glance, but only iterates through the collection once.
Func<int, bool> isEven = n => n % 2 == 0;
Func<int, bool> isFive = n => n == 5;
int diff = numbers.Aggregate(0, (sum, next) => isEven(next) ? sum + 1 : isFive(next) ? sum - 1 : sum);
For each item in the collection, it checks the two conditions. If the first condition applies, it adds one to the aggregate variable; if the second applies, it subtracts one. The end result is the difference between the number of items that meet the first criteria and the number of items that meet the second.
You can use GroupBy:
var groupedByCondition =
= numbers.GroupBy(x => x % 2 == 0)
.Select(x => new { Result = x.Key, Count = g.Count() })
.ToArray();
return groupedByCondition.Length == 2
&& groupedByCondition[0].Count == groupedBycondition[1].Count;
If you have two disjoint filters (that is, an item can't simultaneously satisfy both filters), then BJ Myer's answer may be as simple and efficient as you can get.
If you have two not-necessarily disjoint filters, then you can use the following slight variation which always evaluates both filters for every item:
public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
// Compute
// numbers.Count(x => x % 2 == 0) - numbers.Count(x => x % 2 != 0)
// or equivalently,
// numbers.Sum(x => x % 2 == 0 ? 1 : 0) - numbers.Sum(x => x % 2 != 0 ? 1 : 0)
int sum = numbers.Sum(x =>
(x % 2 == 0 ? 1 : 0) -
(x % 2 != 0 ? 1 : 0));
return sum == 0;
}
If you have an arbitrary number of not-necessarily disjoint filters, then you can use the following generic method:
public static bool HasEqualSizeSubsets<T>(
IEnumerable<T> items, params Func<T, bool>[] filters)
{
var indexedFilters = filters
.Select((filter, index) => new { Filter = filter, Index = index })
.ToArray(); // to avoid repeated object allocations later
IEnumerable<int> subsetSizes = items
.SelectMany(item => indexedFilters
.Where(indexedFilter => indexedFilter.Filter(item))
.Select(indexedFilter => indexedFilter.Index))
.GroupBy(index => index)
.Select(grouping => grouping.Count());
return subsetSizes.Distinct().Count() == 1;
}
HasEqualSizeSubsets looks complicated, but the basic idea is straightforward:
First, we get the array index of each filter passed in the filters array parameter.
Then, for each item in items, we get the index of each filter that the item satisfies. (For example, if the item satisfies just the first filter, we output a "0". If the item satisfies the first two filters, we output "0" and "1".) The result of the SelectMany call is a sequence of filter indexes.
Next, we count the number of times each index appears. The result of the GroupBy and Select calls is a sequence of subset sizes.
Finally, we check whether all the subset sizes are the same unique value.
HasEqualSizeSubsets could be used like this:
public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
return HasEqualSizeSubsets(numbers, x => x % 2 == 0, x => x % 2 != 0);
}
You code do it this way using .Aggregate:
public bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
var result =
numbers
.Aggregate(
new { evens = 0, odds = 0 },
(a, x) =>
{
a = x % 2 == 0
? new { evens = a.evens + 1, a.odds }
: a;
a = x % 2 != 0
? new { a.evens, odds = a.odds + 1 }
: a;
return a;
});
return result.evens == result.odds;
}
The logic could be updated to compute and number of different projections from the source numbers.
If you want to you check two different conditions and loop it only one time.
you can use foreach loop and have your own logic inside
public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
int evenCount = 0;
int oddCount = 0;
foreach (var item in numbers)
{
if (item % 2 == 0)
evenCount++;
else if (item % 2 != 0)
oddCount++;
}
return evenCount == oddCount;
}
Is there any way to avoid "The result of a query cannot be enumerated more than once" exception without using ToList()?
Here is a simplified version of my code:
var entities = _db.Trades.Where(t => t.A > 10);
int totalCount = entities.Count();
entities = entities.Where(t => t.B > 10);
int totalCountAfterAdditionalFilter = entities.Count();
I cannot call ToList() due to performance considerations. I know that I could just generate one more IQueryable, but that seems wrong (I have more filters like this). Could I somehow preserve/duplicate the IQueryable after my first call of Count()?
Thanks!
No, you can't achieve your desired result like that. As an alternative you can, however, save your filters to variables and then compose them as needed:
Func<Trade, bool> filter1 = t => t.A > 10;
Func<Trade, bool> filter2 = t => t => t.B > 10;
Func<Trade, bool> compositeFilter = t => filter1(t) && filter2(t);
int totalCount = _db.Trades.Count(filter1);
int totalCountAfterAdditionalFilter = _db.Trades.Count(compositeFilter);
//Need more?
compositeFilter = t => compositeFilter(t) && t.C > 100;
int totalAfterMoreFilters = _db.Trades.Count(compositeFilter);
may be:
Trades.Where(x => x.A > 10).Select(x => new { i = 1, j = x.B > 10 ? 1 : 0}).
GroupBy(y => y.i).
Select(g => new { c = g.Count(), = g.Sum(z => z.j)})
That's give you the 2 informations in one query.
So I'm trying to have a list of profits in each month from my database.
I want to do this in loop, I thnik it will be the best soulution.
Here we have a loop, that I want to count a profit for each month.
using (var context = new ModelContext("ConnectionStringDbMagazynier"))
{
for (int i = 0; i < 12; i++)
{
decimal q = (from ar in context.Archives
where ar.SalesDate <= DateTime.Now.AddYears(-1).AddMonths(i + 1) && ar.SalesDate >= DateTime.Now.AddYears(-1).AddMonths(i)
let sum = context.Archiwum.Sum(x => x.Price)
select sum);
profits[i] = decimal.ToDouble(q);
}
}
from this query i get an error:
Error 2 Cannot implicitly convert type 'System.Linq.IQueryable<decimal>' to 'decimal'
My question is, how to make it witohut error? and is this solution ok? What in case, that i didn't sell anything in partiuclar month and the sum is null?
It's easier to use lambda syntax in this case.
var q = context.Archives
.Where(ar => ar.SalesDate <= DateTime.Now.AddYears(-1).AddMonths(i + 1) && ar.SalesDate >= DateTime.Now.AddYears(-1).AddMonths(i))
.Sum(x => x.Price);
Or if you really like the query syntax
var records = (from ar in context.Archives
where ar.SalesDate <= DateTime.Now.AddYears(-1).AddMonths(i + 1) && ar.SalesDate >= DateTime.Now.AddYears(-1).AddMonths(i)
select ar);
profits[i] = records.Sum(x => x.Price);
var q = context.Archives
.Where(p => p.SalesDate <= DateTime.Now.AddYears(-1).AddMonths(i + 1) && p.SalesDate >= DateTime.Now.AddYears(-1).AddMonths(i))
.Sum(x => x.Price.HasValue ? x.Price.Value : 0);
It's possible get month stat without loop
var grouped = context.Archives.Where(p=> p.SalesDate <= DateTime.Now.AddYear(-1))
.GroupBy(k => new {Year = k.Year, Month = k.Month})
.Select(p=> {Date = p.Key, Sum = p.Sum(x=> x.Price.HasValue ? x.Price.Value : 0)})