conditional 'or' in nhibernate where clause - c#

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();
}

Related

generate all possibilities for entities in 2 collections

I'm simplifying my blind spot to the following
assume you have 4 entities a, b, c and d; 2 collections x and y; possibilities could be for
(x;y) => (abcd;)(abc;d)(abd;c)(acd;b)(bcd;a)(ac;bd)(ad;bc)(ab;dc)(bc;ad)etc..
in short, i need to generate all possibilities for n entities in m collections in an efficient way
(bit more about the domain, i don't care for ordering, so (ab;cd) is essentially the same as (ba;cd) for my case use, if that's going to make it easier for you)
Very closely related is an answer given here, which deals with generation of all subsets. All subsets of a given sequence can be generated by tho following snippet, which is taken from there.
static IEnumerable<T[]> GetSubsets<T>(T[] set) {
bool[] state = new bool[set.Length+1];
for (int x; !state[set.Length]; state[x] = true ) {
yield return Enumerable.Range(0, state.Length)
.Where(i => state[i])
.Select(i => set[i])
.ToArray();
for (x = 0; state[x]; state[x++] = false);
}
}
Based on enumeration of all subsets,the desired solution can be ganerated by determining the complement as follows.
public class Partition
{
public IEnumerable<string> First;
public IEnemurable<string> Second;
};
var input = new string[]{ "a", "b", "c", "d" };
var subsets = Getsubsets(input);
var Partitions = new List<Partition>();
foreach (var subset in subsets)
{
var iPart = new Partition();
iPart.First = subset;
iPart.Second = input.Where(iEl => false == subset.Contains(iEl));
Partitions.Add(iPart);
}
Here is an implementation that uses as state a BigInteger instead of a bool[] or a BitArray:
using System.Numerics;
public static IEnumerable<(T[], T[])> GetCollectionPairs<T>(T[] source)
{
BigInteger combinations = BigInteger.One << source.Length;
for (BigInteger i = 0; i < combinations; i++)
{
yield return
(
Enumerable.Range(0, source.Length)
.Where(j => (i & (BigInteger.One << j)) != 0)
.Select(j => source[j])
.ToArray(),
Enumerable.Range(0, source.Length)
.Where(j => (i & (BigInteger.One << j)) == 0)
.Select(j => source[j])
.ToArray()
);
}
}
Usage example:
var items = new string[] { "A", "B", "C", "D" };
var pairs = GetCollectionPairs(items);
foreach (var pair in pairs)
{
Console.WriteLine(
$"({String.Join("", pair.Item1)};{String.Join("", pair.Item2)})");
}
Output:
(;ABCD)
(A;BCD)
(B;ACD)
(AB;CD)
(C;ABD)
(AC;BD)
(BC;AD)
(ABC;D)
(D;ABC)
(AD;BC)
(BD;AC)
(ABD;C)
(CD;AB)
(ACD;B)
(BCD;A)
(ABCD;)
This generates (AB;CD) and (CD;AB) as different pairs. If this is not desirable, then simply loop until i < combinations / 2 instead of i < combinations.

Count users where subscription end within month Linq c#

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.

How to append more rows to a Linq list after the condition is met

I have a list which contains items (let assume 1000 items). I want to select data from that list where TimeInSecond matches the criteria.
newList = oldList.Where(x => x.TimeInSecond >= 30 && x.TimeInSecond <= 90).ToList();
// the above query returns 20 items (from 10 to 20)
However, I need to append next N number of rows from the oldList
// This is just an example of what I need, for example 10 next more items
newList = oldList.Where(x => x.TimeInSecond >= 30 && x.TimeInSecond <= 90).GetNextMoreItems(10).ToList() ;
// the above query returns (20 + 10) 30 items (from 1 to 30)
Since you mentioned in the comments, you want 10 additional elements from the last element where condition is true (satisfied), you could do something like this.
// Get the elements where condition is satisfied.
newList = oldList
.Where(x => x.TimeInSecond >= 30 && x.TimeInSecond <= 90)
.ToList() ;
// Find the index of last element and then take 10 elements from then on.
newList = newList.Concat(oldList
.Skip(oldList.ToList().IndexOf(newList.Last() + 1))
.Take(10))
.ToList();
Check sample Demo to see how it works.
You can use Union or AddRange to append your new 10 items to your existing list. Of course, the data-types has to be the same. Here's an example with int type:
List<int> l1 = new List<int>() {1,2,3 };
List<int> l2 = new List<int>() { 1,4,5,6};
l1.AddRange(l2); // l1 now contains {1,2,3,1,4,5,6}
// will remove duplicates using default Equality Comparer
var l3 = l1.Union(l2).ToList(); // contains {1,2,3,4,5,6}
With your example, it may look like this:
var list1 = oldList.Where(x => x.TimeInSecond >= 30 && x.TimeInSecond <= 90);
var list2 = GetNextMoreItems(10).ToList();
var finalList = list1.AddRange(list2);
Edit based on comment
To exclude items that you've already selected, you can use Except, which is opposite of Intersect . So, for the int example above:
var l4 = l3.Except(l1); // contains {4,5,6}
And then, to pick certain number of elements, append .Take(count).
If you want those that match and N of which other items in the collection you can simply do:
Option 1: where condition or counter
int n = 10;
int counter = 0;
var values = oldList.Where(x => (x.TimeInSecond >= 30 && x.TimeInSecond <= 90) ||
counter++ < n).ToList();
Option 2: use a helping method to avoid repeating condition
Func<dynamic, bool> condition = (x) => x.TimeInSecond >= 30 && x.TimeInSecond <= 90;
var result = oldList.Where(x => condition(x))
.Concat(oldList.Where(x => !condition(x)).Take(n));
If order matters and you want to take N items that come only after all those that match:
Option 1:
int n = 10;
var values = oldList.Select((item, index) => new { item, index })
.Where(x => x.item.TimeInSecond >= 30 && x.item.TimeInSecond <= 90);
var lastItem = values.Max(item => item.index);
var result = values.Select(item => item.item)
.Concat(oldList.Skip(lastItem + 1).ToList().Take(n));
Option 2: Maybe looks a bit cleaner.. not sure (and has to have the ToList() just before the concat:
int n = 1;
int lastItem = 0;
var result = oldList.Where((x, i) =>
{
var outcome = x.TimeInSecond >= 30 && x.TimeInSecond <= 90;
if (outcome)
lastItem = i;
return outcome;
}).ToList()
.Concat(oldList.Skip(lastItem + 1).ToList().Take(n)); //Result: 40, 50, 100 (from data below)
Tested with:
List<dynamic> oldList = new List<dynamic>()
{
new {TimeInSecond = 10},
new {TimeInSecond = 40},
new {TimeInSecond = 50},
new {TimeInSecond = 100},
new {TimeInSecond = 120},
};

Select last wrong item from the list

I have a list with items, that have a Time property. If I want to select all items where Time is equal or bigger then some startTime, then I write something like this:
var newList = list.Where(i => (i.Time >= startTime));
But now I also want to get the last item, where the time is smaller than startTime. Is there a better way to implement this?
For example I have list where items have Time from this list:
[5:32, 5:46, 5:51, 6:07, 6:11, 6:36]
We specify a startTime as 6:00.
Now we want to get this times:
[5:51, 6:07, 6:11, 6:36]
Getting the whole List at once:
var newList = list
.OrderByDescending(i => i.Time)
.Take(list.Count(j => j.Time >= startTime) + 1)
.OrderBy(k => k.Time); //Optional
With Cognition's suggestion:
var newList = list
.OrderBy(i => i.Time)
.Skip(list.Count(j => j.Time < startTime - 1));
var result=list
.Where(i=>i.Time<startTime)
.OrderBy(i=>i.Time)
.Last()
.Concat(list
.OrderBy(i=>i.Time)
.Where(i=>i.Time>=startTime)
);
or
var result=list
.OrderBy(i=>i.Time)
.Last(i=>i.Time<startTime)
.Concat(list
.OrderBy(i=>i.Time)
.Where(i=>i.Time>=startTime)
);
var smallerThan = list
.Where(i => i.Time < startTime)
.OrderByDescending(o => o.Time)
.Take(1)
.Concat(list.Where(i => i.Time => startTime));
As your list is in order of the property you want to find, you can do something along the lines of
List<int> things = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
int threshold = 4;
var newThings = things.Skip(things.FindIndex(x => x >= threshold) - 1);
Console.WriteLine(string.Join(", ", newThings));
Which outputs
3, 4, 5, 6, 7, 8
Extending it to use a class with a Time property which happens to be a TimeSpan:
class Z
{
public TimeSpan Time { get; set; }
};
class Program
{
static void Main(string[] args)
{
Random rand = new Random();
List<Z> zs = new List<Z>();
for (int i = 0; i < 10; i++)
{
zs.Add(new Z { Time = new TimeSpan(i, rand.Next(0,61), rand.Next(0,61)) });
}
TimeSpan threshold = new TimeSpan(4,0,0);
var newThings = zs.Skip(zs.FindIndex(x => x.Time >= threshold) - 1);
Console.WriteLine(string.Join(", ", newThings.Select(x => x.Time.ToString("c"))));
Console.ReadLine();
}
}
Sample output:
03:03:57, 04:09:37, 05:14:44, 06:58:55, 07:40:33, 08:37:06, 09:10:06
Many of the answers seem to require a descending orderby. But you can easily avoid this with a clean one liner and good efficiency:
var newList = list.Skip(list.Count(j => j.Time < startTime) - 1);
var newList = list
.Where(i => (i.Time >= startTime))
.ToList()
.Add(list
.Where(i => (i.Time < startTime))
.OrderByDescending(o => o.Time)
.FirstOrDefault()
)
int lastItemIndex = list.OrderBy(D => D.TimeOfDay).ToList()
.FindLastIndex(D => D.TimeOfDay < startTime);
var newList = list.Where(D => list.IndexOf(D) > lastItemIndex);

C# moving an item up/down

I got a list that contains items. They all got a 'Sort' column. The sort column is of type int, and it's unique.
Scenario:
sort 1; sort 2; sort 3;
If the user moves an item up (for example sort 3) in the list (for example to position 1, which would give the sort value 1), the items that are under the one that just got moved up, have to be shifted down in the list, and the sort number should be applied accordingly. In this case all shifted items sort - 1.
So the end state of the scenario looks like this:
sort 1 was sort 3; sort 3 was sort 2; sort 3 is now sort 1;
How can i do this with LINQ?
It's not just 3 items. It can be a lot more.
[Edit]
public ActionResult Up(int id)
{
var item = dataContext.item.FirstOrDefault(x => x.item == id);
return View(dataContext.items);
}
That might not be the easiest code to understand but I've tested it and it seems to work as intended.
Let's setup some data.
var array = new []
{
new { Sort = 1, Value = "foo1", },
new { Sort = 2, Value = "foo2", },
new { Sort = 3, Value = "foo3", },
new { Sort = 4, Value = "foo4", },
};
var oldSort = 1;
var newSort = 3;
First, query is split into three parts depending on positions of old and new indexes, so we can handle each case separately.
var q =
oldSort > newSort ?
array
.Where(x => x.Sort >= newSort && x.Sort < oldSort)
.Select(x => new { Sort = x.Sort + 1, Value = x.Value })
.Union(array.Where(x => x.Sort < newSort || x.Sort > oldSort))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
oldSort < newSort ?
array
.Where(x => x.Sort <= newSort && x.Sort > oldSort)
.Select(x => new { Sort = x.Sort - 1, Value = x.Value })
.Union(array.Where(x => x.Sort > newSort || x.Sort < oldSort))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
array;
Results for moving an item down (oldSort = 1, newSort = 3):
1 foo2
2 foo3
3 foo1
4 foo4
Results for moving an item up (oldSort = 4, newSort = 2):
1 foo1
2 foo4
3 foo2
4 foo3
UPDATE: The query works by splitting a sequence into three parts
Item with the old index becomes an item with the new index;
Items between the old and new indexes are shifted either up or down;
The rest keeps their indexes.
The result is the union of the parts.
UPDATE 2: The query works for any number of items and the absence of loops is intentional.
UPDATE 3: Here's one way to make the query work with LINQ-to-Entities.
using (var context = new TestDBEntities())
{
var array = context.TestTables;
var q =
oldSort > newSort ?
array
.Where(x => x.Sort >= newSort && x.Sort < oldSort)
.Select(x => new { Sort = x.Sort + 1, Value = x.Value })
.Union(array.Where(x => x.Sort < newSort || x.Sort > oldSort)
.Select(x => new { Sort = x.Sort, Value = x.Value }))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
oldSort < newSort ?
array
.Where(x => x.Sort <= newSort && x.Sort > oldSort)
.Select(x => new { Sort = x.Sort - 1, Value = x.Value })
.Union(array.Where(x => x.Sort > newSort || x.Sort < oldSort)
.Select(x => new { Sort = x.Sort, Value = x.Value }))
.Union(array.Where(x => x.Sort == oldSort)
.Select(x => new { Sort = newSort, Value = x.Value }))
:
array.Select(x => new { Sort = x.Sort, Value = x.Value });
}
The difference is that the types are now explicitly compatible.
The conditional operator is useful here:
var newitems = items.Select(x =>
new
{
Value = x.Value,
Sort = x.Sort == oldSort ? newSort :
x.Sort < oldSort && x.Sort >= newSort ? x.Sort + 1 :
x.Sort > oldSort && x.Sort < newSort ? x.Sort - 1 :
x.Sort
});
This is using Serge's setup:
var items = new []
{
new { Sort = 1, Value = "foo1", },
new { Sort = 2, Value = "foo2", },
new { Sort = 3, Value = "foo3", },
new { Sort = 4, Value = "foo4", },
};
var oldSort = 1;
var newSort = 3;
Its performance is decent (O(n) in all scenarios), plus it's concise and readable.
I know that you asked for a LINQ solution, but LINQ seems complicated to use in this situation, especially if you want to adjust the Sort column as well. I suggest a plain old approach using for loops and indexes. It performs the sort operation in-place and does not created a new list.
In order to make it reusable I create it as extension method for the IList interface, which makes it compatible to arrays too.
In order to make it generic, you need some way to access the Sort column. Exposing this column through an interface would restrict the solution to classes implementing this interface. Therefore I opted for accessors that you have to pass as delegates. They also work if the Sort column has another name like Order for instance.
public static class ListExtensions
{
public static void MoveItem<T>(this IList<T> list, int fromIndex, int toIndex,
Func<T, int> getSortKey, Action<T, int> setSortKey)
{
T temp = list[fromIndex];
int lastSortKey = getSortKey(temp);
setSortKey(temp, getSortKey(list[toIndex]));
if (fromIndex > toIndex) { // Move towards beginning of list (upwards).
for (int i = fromIndex; i > toIndex; i--) {
list[i] = list[i - 1];
int nextSortKey = getSortKey(list[i]);
setSortKey(list[i], lastSortKey);
lastSortKey = nextSortKey;
}
} else if (fromIndex < toIndex) { // Move towards end of list (downwards).
for (int i = fromIndex; i < toIndex; i++) {
list[i] = list[i + 1];
int nextSortKey = getSortKey(list[i]);
setSortKey(list[i], lastSortKey);
lastSortKey = nextSortKey;
}
}
list[toIndex] = temp;
}
}
You can use the method like this
list.MoveItem(3, 1, x => x.Sort, (x, i) => x.Sort = i);
Note that you have to pass the list indexes and not the sort values.
Here are the classes I used for the tests. Just set a breakpoint at the end of the two test methods in order to inspect the result in the locals window. Start the test in the Class View by right clicking on the Test class and choosing "Invoke Static Method".
public class SomeItem
{
public int Sort { get; set; }
public string Value { get; set; }
public override string ToString()
{
return String.Format("Sort = {0}, Value = {1}", Sort, Value);
}
}
public static class Test
{
public static void MoveUp()
{
List<SomeItem> list = InitializeList();
list.MoveItem(3, 1, x => x.Sort, (x, i) => x.Sort = i);
}
public static void MoveDown()
{
List<SomeItem> list = InitializeList();
list.MoveItem(1, 3, x => x.Sort, (x, i) => x.Sort = i);
}
private static List<SomeItem> InitializeList()
{
return new List<SomeItem> {
new SomeItem{ Sort = 1, Value = "foo1" },
new SomeItem{ Sort = 2, Value = "foo2" },
new SomeItem{ Sort = 3, Value = "foo3" },
new SomeItem{ Sort = 4, Value = "foo4" },
new SomeItem{ Sort = 5, Value = "foo5" }
};
}
}
UPDATE
A note on adjusting the sort key: The solution above works well if the sort keys are in-order and unique. If this is not always the case, a more robust solution would be to adjust the sort keys before storing the list back to the DB by simply setting the sort key equal to the list index. This would simplify the MoveItem method.
public static void MoveItem<T>(this IList<T> list, int fromIndex, int toIndex)
{
T temp = list[fromIndex];
if (fromIndex > toIndex) { // Move towards beginning of list (upwards).
for (int i = fromIndex; i > toIndex; i--) {
list[i] = list[i - 1];
}
} else if (fromIndex < toIndex) { // Move towards end of list (downwards).
for (int i = fromIndex; i < toIndex; i++) {
list[i] = list[i + 1];
}
}
list[toIndex] = temp;
}
public static void FixSortKeys<T>(this IList<T> list, Action<T, int> setSortKey)
{
for (int i = 0; i < list.Count; i++) {
setSortKey(list[i], i);
}
}

Categories