I need to check if a sequence has any items satisfying some condition but at the same time NOT all items satisfying the same condition.
For example, for a sequence of 10 items I want to have TRUE if the sequence has at least one that satisfy the condition but not all:
10 items satisfying, 0 items not, result is FALSE
0 items satisfying, 10 items not, result is FALSE
1 item satisfying, 9 items not, result is TRUE
9 items satisfying, 1 item not, result is TRUE
I know I could to this:
mySequence.Any (item => item.SomeStatus == SomeConst) && !mySequence.All (item => item.SomeStatus == SomeConst)
But this is not optimal.
Is there a better way?
You'll like this.
var anyButNotAll = mySequence
.Select(item => item.SomeStatus == SomeConst)
.Distinct()
.Take(2)
.Count() == 2;
The Take(2) stops it iterating over any more elements than it has to.
If your concern is iterating through all of the elements in a large collection, you're okay - Any and All will short-circuit as soon as possible.
The statement
mySequence.Any (item => item.SomeStatus == SomeConst)
will return true as soon as one element that meets the condition is found, and
!mySequence.All (item => item.SomeStatus == SomeConst)
will return true as soon as one element does not.
Since both conditions are mutually exclusive, one of the statements is guaranteed to return after the first element, and the other is guaranteed to return as soon as the first element is found.
As pointed out by others, this solution requires starting to iterate through the collection twice. If obtaining the collection is expensive (such as in database access) or iterating over the collection does not produce the same result every time, this is not a suitable solution.
You could define your own extension method. This version is more verbose, but still readable, and it only enumerates the IEnumerable once:
bool AnyButNotAll<ItemT>(this IEnumerable<ItemT> sequence, Func<ItemT, bool> predicate)
{
bool seenTrue = false;
bool seenFalse = false;
foreach (ItemT item in sequence)
{
bool predResult = predicate(item);
if (predResult)
seenTrue = true;
if (!predResult)
seenFalse = true;
if (seenTrue && seenFalse)
return true;
}
return false;
}
Much shorter, but enumerates the IEnumerable twice:
bool AnyButNotAll<ItemT>(this IEnumerable<ItemT> sequence, Func<ItemT, bool> predicate)
{
return sequence.Any(predicate) && !sequence.All(predicate);
}
That is likely a fairly optimal solution if the source is a database. This extension method might be better depending on your source (I think, I just threw it together -- likely many many errors, consider it more pseudo code). The benefit here is it only enumerates once and does a short-circuit as soon as it has read enough to determine the outcome:
static bool SomeButNotAll<TSource>(this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
using(var iter=source.GetEnumerator())
{
if (iter.MoveNext())
{
bool initialValue=predicate(iter.Current);
while (iter.MoveNext())
if (predicate(iter.Current)!=initialValue)
return true;
}
}
return false; /* All */
}
You could try this:
var result = mySequence.Select(item => item.SomeStatus == SomeConst)
.Distinct().Count() > 1 ? false : true;
Basically I select true or false for each value, get distinct to only get one of each, then count those.
You could put your predicate in a variable so that you don't have to repeat the predicate twice:
Func<MyItemType, bool> myPredicate = item => item.SomeStatus == SomeConst;
if (mySequence.Any(myPredicate) && !mySequence.All(myPredicate))
...
If you wanted to define this as a method, you could take then approach Linq takes in defining both IEnumerable<T> and IQueryable<T> extension methods. This allows for the optimal approach to be taken automatically:
public static bool SomeButNotAll<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
if(source == null)
throw new ArgumentNullException("source");
if(predicate == null)
throw new ArgumentNullException("predicate");
return source.
Select(predicate)
.Distinct()
.Take(2)
.Count() == 2;
}
public static bool SomeButNotAll<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
if(source == null)
throw new ArgumentNullException("source");
if(predicate == null)
throw new ArgumentNullException("predicate");
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
bool first = predicate(en.Current);
while(en.MoveNext())
if(predicate(en.Current) != first)
return true;
}
return false;
}
If you're using EntityFramework (or another provider that provides a CountAsync you can also provide an asynchronous version easily:
public static async Task<bool> SomeButNotAllAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, CancellationToken cancel)
{
if(source == null)
throw new ArgumentNullException("source");
if(predicate == null)
throw new ArgumentNullException("predicate");
cancel.ThrowIfCancellationRequested();
return await source.
Select(predicate)
.Distinct()
.Take(2)
.CountAsync(cancel)
.ConfigureAwait(false) == 2;
}
public static Task<bool> SomeButNotAllAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
return source.SomeButNotAllAsync(predicate, CancellationToken.None);
}
You can use the Aggregate method to do both things at once. I would suggest to use an anonymous type for the TAccumulate, containing both counters. After the aggregation you can read both values from the resulting anonymous type.
(I can't type an example, I'm on my phone)
See the documentation here: https://msdn.microsoft.com/en-us/library/vstudio/bb549218(v=vs.100).aspx
Related
Hi can someone help me how best we can use whereif in LINQ, here I have a code which works fine, but I want to convert this query with WhereIf.
public async Task LoadQuery(IEnumerable<string> codes)
{
var query = _dBContext.QueryTable.Where(x => !x.InActive).AsQueryable();
if (codes!= null && codes.Any())
query = query.Where(x => codes.Contains(x.FirstCode) || query.Contains(x.SecondCode));
else
query = query.Where(x => !x.HasException.HasValue);
var data = query.ToList();
}
I have tried it with WhereIF ienumerable but not succeed. Here is the link which I followed.
https://extensionmethod.net/csharp/ienumerable-t/whereif
WhereIf isn't really suitable for your case, for 2 reasons:
You're calling two different functions on your if-else, while WhereIf is built to accept a single function (predicate) to be executed if some condition is satisfied.
WhereIf is an extension method for IEnumerable<TSource>, while your'e trying to use it as an extension method for IQueryable<TSource>.
If you insist, you'd have to define an extension method for IQueryable<TSource>, and in doing so, just define it as WhereIfElse:
public static class ExtensionMethods
{
public static IQueryable<TSource> WhereIfElse<TSource>(this IQueryable<TSource> source, bool condition, Func<TSource, bool> predicateIf, Func<TSource, bool> predicateElse)
{
if (condition)
return source.Where(predicateIf).AsQueryable();
else
return source.Where(predicateElse).AsQueryable();
}
}
So, let's say that query's type is IQueryable<Item> (replace Item with your actual type):
public async Task<List<Item>> LoadQuery(IEnumerable<string> codes)
{
var query = _dBContext.QueryTable.Where(x => !x.InActive).AsQueryable();
query = query.WhereIfElse(
// condition
codes != null && codes.Any(),
// predicateIf
(Item x) => codes.Contains(x.FirstCode) || codes.Contains(x.SecondCode),
// predicateElse
(Item x) => !x.HasException.HasValue
);
var data = query.ToList();
return data;
}
P.S. note I changed your return value, though there still isn't an await.
bool condition = codes!= null && codes.Any();
var data = _dBContext.QueryTable
.WhereIf(condition, a=> codes.Contains(a.FirstCode) || codes.Contains(a.SecondCode))
.WhereIf(!condition, a=> !a.HasException.HasValue && !a.InActive).ToList();
I'm confused after reading the documentation on what to expect when using Linq with a SortedList.
https://msdn.microsoft.com/en-us/library/ms132319(v=vs.110).aspx
I guess the enumeration is guaranteed to be sorted and also retrieve by index, but what about Values and Keys? Are all these cases safe?
var list = new SortedList<DateTime, object>();
//add entries here ...
var firstValue1 = list.Values[0];
var firstValue2 = list.First().Value;
var firstValue3 = list.Values.First();
var firstKey1 = list.Keys[list.Count-1];
var firstKey2 = list.First().Key;
var firstKey3 = list.Keys.First();
var sortedList = list.Where(x => x.Key > DateTime.Now)
.Select(x => x.Value);
Read the documentation...
From the documentation on the Values property:
"The order of the values in the IList<T> is the same as the order in the SortedList<TKey, TValue>."
From the documentation on the Keys property:
"The order of the keys in the IList<T> is the same as the order in the SortedList<TKey, TValue>."
You can check out the source code here:
https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sortedlist.cs,de670561692e4a20
Apparently, the Keys property is just a wrapper around an instance of this class:
https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sortedlist.cs,374aa21b960ae2e2
If you look at the GetEnumerator() method, you can see it creates a SortedListKeyEnumerator. Here's the source code for that:
https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sortedlist.cs,a4492235f85c77d8
As far as I can tell, the MoveNext() of this just iterates through the keys of the contained SortedList.
You can find out the same way how Values works.
If you look at the source code of Enumerable.cs, you will see that the overload without a predicate simply tries to treat the source as an IList and if that doesn't work, it returns the first element using the enumerator. Both the index and the enumerator are supposed to be handled internally by the SortedList class so that you get the appropriate (sorted) result:
public static TSource First<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
IList<TSource> list = source as IList<TSource>;
if (list != null) {
if (list.Count > 0) return list[0];
}
else {
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return e.Current;
}
}
throw Error.NoElements();
}
The overload with a predicate works slightly different in that it executes the predicate against every item using the enumerator, looking for the first match:
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
foreach (TSource element in source) {
if (predicate(element)) return element;
}
throw Error.NoMatch();
}
Either way, you should be getting the same (sorted) result.
I have the following records
The last 2 are children of record 3 and 4, I would like to be able to sort the records by amount but it should be that the non interest(parents) ones are sorted first then their children should show up after so for example it would be like this
2000
2000
20001
99.84 (child of the above)
50000
249.58 (child of the above)
Basically I would like my sort by amount to disregard the one with "IsInterest" set to true but make them show up after their parent.
I can do this by first taking all the parents into a new collection.. then go through the parent to see if there is any children then insert them after the parent in the new collection but I feel this is not efficient and dirty code so I thought I would ask maybe someone knows black magic.
The sort should also be aware of asc/desc on the amount.
I can post my code of ripping the collection apart and putting it together if it helps but I am trying not to use that code if possible.
My sort method takes a string for "ascending" or "descending" if that helps
Thank you
UPDATE2
I will point out that there is only ever going to be 2 levels, and that the children will ever only have one parent (no grand parents) and that each parent will have a maximum of 1 child
UPDATE code as requested (fields name may differ from the db fields..)
switch (sortMember.ToUpper())
{
case "AMOUNT":
{
//check to see if any imputed interests exist
if (contributions.Any(x => x.IsImputedInterest))
{
var children = contributions.Where(x => x.IsImputedInterest);
var sortedColl = contributions.Where(x => x.IsImputedInterest == false).OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING").ToList();
foreach (var child in children )
{
//find the parent
var parentIndex = sortedColl.FindIndex(x => x.ContributionId == child.ParentContirbutionId);
sortedColl.Insert(parentIndex+1, child);
}
}
else
{
contributions = contributions.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING");
}
break;
}
}
.................
public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool descending)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool descending)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
Here's a single statement Linq solution:
var desc = order == "descending";
var result = list
//group parents with it's children
.GroupBy(x => x.ParentId ?? x.Id)
//move the parent to the first position in each group
.Select(g => g.OrderBy(x => x.ParentId.HasValue).ThenBy(x => desc ? -x.Amount : x.Amount))
//sort the groups by parents' amounts
.OrderBy(g => desc ? -g.First().Amount : g.First().Amount)
//retrieve the items from each group
.SelectMany(g => g);
Some performance hints:
You can drop the ThenBy(...) if there's always going to be at most one child or you don't care about children order
Use an if statement to check the order and have two versions of the statement - the second one using OrderByDescending/ThenByDescending, and drop the ternary operator (desc ? ... : ...) - otherwise it will be evaluated for each item
I'm not giving any guarantees on performance in relation to your current solution - it might as well turn out to be slower.
You can use the following generic method (not limited by levels or number of parent/children):
public static class Extensions
{
public static IEnumerable<T> ThenByHierarchy<T, TKey>(this IOrderedEnumerable<T> source, Func<T, TKey> keySelector, Func<T, TKey> parentKeySelector)
{
var itemByKey = source.ToDictionary(keySelector);
var processSet = new HashSet<T>();
var stack = new Stack<T>();
foreach (var item in itemByKey.Values)
{
for (var next = item; processSet.Add(next); )
{
stack.Push(next);
if (!itemByKey.TryGetValue(parentKeySelector(next), out next)) break;
}
while (stack.Count != 0)
yield return stack.Pop();
}
}
}
Just append it at the end of your OrderBy sequence like this
var result = contributions
.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING")
.ThenByHierarchy(x => x.ContributionId, x => x.ParentContirbutionId);
UPDATE: It turns out that it's not so simple. While the method above provides a correct order for the leaf elements as well for the element to its parent, it does not order correctly the parents. The correct one is as follows (using another reusable method from here How to flatten tree via LINQ?, so if we don't count that it isn't really much bigger than the previous):
public static class Extensions
{
public static IEnumerable<T> HierarchicalOrder<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, Func<T, TKey> parentKeySelector, Func<IEnumerable<T>, IOrderedEnumerable<T>> order)
{
// Collect parent/child relation info
var itemById = source.ToDictionary(keySelector);
var childListById = new Dictionary<TKey, List<T>>();
var rootList = new List<T>();
foreach (var item in itemById.Values)
{
var parentKey = parentKeySelector(item);
List<T> childList;
if (parentKey == null || !itemById.ContainsKey(parentKey))
childList = rootList;
else if (!childListById.TryGetValue(parentKey, out childList))
childListById.Add(parentKey, childList = new List<T>());
childList.Add(item);
}
// Traverse the tree using in-order DFT and applying the sort on each sublist
return order(rootList).Expand(item =>
{
List<T> childList;
return childListById.TryGetValue(keySelector(item), out childList) ? order(childList) : null;
});
}
public static IEnumerable<T> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
{
var stack = new Stack<IEnumerator<T>>();
var e = source.GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
var item = e.Current;
yield return item;
var elements = elementSelector(item);
if (elements == null) continue;
stack.Push(e);
e = elements.GetEnumerator();
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
}
}
finally
{
e.Dispose();
while (stack.Count != 0) stack.Pop().Dispose();
}
}
}
and the usage in your case is simple
var result = contributions
.HierarchicalOrder(x => x.ContributionId, x => x.ParentContirbutionId, c =>
.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING"));
I'm writing an entry for an AI competition in C#, and I'm looking for a more elegant way to search for items. (I'm much more familiar with embedded C programming, but I prefer C# for an AI contest.)
The contest server is using dmcs to compile entries, which is .Net framework 4.0; I'm using Visual Studio Express 2013 for my testing.
I'm trying to search for an item in a list with the maximum value of a parameter that also meets a certain prerequisite. I don't want the maximum value, though, I want the item that has said maximum value.
Here's my original code that does what I want using a foreach loop:
List<Region> myList = new List<Region>();
// ...
// myList gets populated with elements
// ...
Region biggest = null;
int biggestSize = -1;
foreach (Region r in myList)
{
// We only want elements that are eligible for expansion
if (r.EligibleForExpansion())
{
if (r.Size > biggestSize)
{
biggest = r;
biggestSize = r.Size;
}
}
}
return biggest; // I want the biggest Region, not the Size of the biggest region.
I'm trying to find a more elegant way to do this so I don't have foreach loops all over my code. I tried this:
return myList.Max(delegate(Region r) { if (r.EligibleForExpansion()) return r.Size; else return -1; });
However, that returns the Size value of the largest region, not the largest Region itself (which is what I need).
I know that my foreach code will return null if no Region meets the requirement while the Max code will give -1 (or any Region that doesn't meet the requirement); I can deal with either way.
I don't think I can just make Region IComparable, though; I have many searches for Region objects, and I need to sort by different parameters at different times, so the comparison function would be different in different searches.
I could just wrap my foreach code in a static function and call that wherever I need to search, but it seems like there should be a more elegant way to do this in C#.
Use MaxBy from moreLINQ library:
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MaxBy(selector, Comparer<TKey>.Default);
}
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
if (comparer == null) throw new ArgumentNullException("comparer");
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var max = sourceIterator.Current;
var maxKey = selector(max);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, maxKey) > 0)
{
max = candidate;
maxKey = candidateProjected;
}
}
return max;
}
}
like that:
var item = myList.Where(x => x.EligibleForExpansion())
.MaxBy(x => x.Size);
How about this?
myList.Where(r => r.EligibleForExpansion).OrderBy(r => r.Size).LastOrDefault()
You can use Aggregate out of the box for this purpose:
var item = myList
.Where(r => r.EligibleForExpansion())
.Aggregate((Region)null, (max, cur) => (max == null ? cur : cur.Size > max.Size ? cur : max));
If Region were a value type (which it isn't) you could wrap the initial value in a nullable, and get a null value for an empty list:
var item = myList
.Where(r => r.EligibleForExpansion())
.Aggregate((Region?)null, (max, cur) => (max == null ? cur : cur.Size > max.Value.Size ? cur : max));
I have a collection of Obj's, I want to go through the collection, and set a property if a condition is true, so in normal world it would be:
foreach (var o in obj)
{
if (o.SomeProperty == Something)
{
o.SomeOtherProperty = true;
}
}
Anyway to do this, using Linq, to make it in a single line?
LINQ isn't all that useful for executing side effects, it's primarily intended for querying. In truth, the fact that it has deferred execution so engrained in its behaviour makes it a poor choice for executing side-effects, IMO.
The code you've got looks perfectly fine to me. If you did want to use LINQ though, I doubt you could improve much on:
foreach (var o in obj.Where(i => i.SomeProperty == Something))
{
o.SomeOtherProperty = true;
}
Now, that isn't all that better (arguably worse) than your original.
On the other hand, if you wanted to create a new, streaming sequence with projections of the original items having the desired characteristics, you could do something like:
var projection = obj.Where(i => i.SomeProperty == Something)
.Select(i => new Foo(i) { SomeOtherProperty = true });
// assuming your type has a copy-constructor
EDIT: If you don't want to heed the advice of the experts (read: Eric Lippert), you can write your own extension-method:
public static void Do<T>(this IEnumerable<T> source, Action<T> action)
{
if (source == null)
throw new ArgumentNullException("source");
if (action == null)
throw new ArgumentNullException("action");
foreach (T item in source)
action(item);
}
This will allow you to do:
obj.Where(o => o.SomeProperty == Something)
.Do(o => o.SomeOtherProperty = true);
obj.Where(i => i.SomeProperty == Something).ToList().ForEach(o => o.SomeOtherProperty = true);
Using an extension method:
public static int UpdateOnPredication<T>(this IEnumerable<T> source, Func<T, bool> predicate, Action<T> update)
{
//check the parameters here if (source==null) ...
var query = source.Where(predicate);
foreach (var item in query)
{
update(item);
}
return query.Count();
}
Usage:
results.UpdateOnPredication(x => x.ID > 1000, x => x.Status = 1);