I am struggling to understand why does not List<T>FindAll(...) method accepts Func<TSource, bool> but instead insists on accepting Predicate<TSource>.
So when I have a List of books and I want to get only books, which are cheaper than 10. This code runs just fine.
Predicate<Book> CheapBooksPredicate = b => b.Price < 10;
var cheapBooksPredicate = books.FindAll(CheapBooksPredicate);
But when I change Predicate<TSource> to Func<TSource, bool>
Func<Book, bool> CheapBooksFunc = b => b.Price < 10;
var cheapBooksFunc = books.FindAll(CheapBooksFunc);
I am getting error:
Argument 1: cannot convert from 'System.Func' to 'System.Predicate'
What Am I missing here ? When both Func<TSource, bool> and Predicate<TSource> are predicates. Predicate<TSource> should be specialized version of a Func that takes and evaluates a value against a set of criteria and returns a boolean, thus I though, that they can replace each other in terms of usage.
They have the same signature, but they are fundamentally different types and cannot be cast as a reference-preserving conversion. Since FindAll wants a Predicate<T>: use Predicate<T>.
Would it be nice if they were castable like this? Maybe, but it would require CLR and language changes, and is unlikely to happen.
You can use the following Extension method to convert Func<T, bool> to Predicate<T>
static Predicate<T> FuncToPredicate<T>(Func<T, bool> func)
{
return new Predicate<T>(func);
}
Reference
Related
I want to know if a determind string is in a string list, with the StringComparison.InvariantCultureIgnoreCase criteria. So I tried something like:
bool isInList = _myList.Contains(toFindString, StringComparison.InvariantCultureIgnoreCase);
Or:
bool isInList = _myList.Contains(listElement => listElement .Equals(toFindString,StringComparison.InvariantCultureIgnoreCase));
But, the Contains method does not contain the Func<TSource, bool> predicate overload which I think is the cleanest for this case. The Where or the Count method for example got it.
Overload method signature for Count:
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
Specific application for my case:
int foundCount = _myList.Count(listElement => listElement.Equals(toFindString, StringComparison.InvariantCultureIgnoreCase));
What would be the cleanest way to add the StringComparison.InvariantCultureIgnoreCase criteria to the linq Contains current overload with the IEqualityComparer that is this one public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer);?
You've got several options.
The first is to use Enumerable.Any, and test each element using the string.Equals overload which takes a StringComparison:
bool isInList = _myList.Any(x => string.Equals(x, "A", StringComparison.InvariantCultureIgnoreCase));
(It's safer to use the static string.Equals(string, string) where possible, as it's safe to either argument being null).
Linq also provides an overload of Contains which takes an IEqualityComparer<T>, which you can pass StringComparer.InvariantCultureIgnoreCase to:
bool isInList = _myList.Contains("A", StringComparer.InvariantCultureIgnoreCase);
See here.
Either use StringComparer.InvariantCultureIgnoreCase
bool isInList = _myList.Contains(toFindString, StringComparer.InvariantCultureIgnoreCase);
or use Any with StringComparison:
bool isInList = _myList.Any(s => s.Equals(toFindString,StringComparison.InvariantCultureIgnoreCase));
the latter has a problem if there are null-strings in the list, so you could use string.Equals.
Note that both ways are not suppported with Linq-To-Entities.
I've just started to study on LINQ. The following example uses Where which is one of the standard query operators.
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames = System.Linq.Enumerable.Where(names, n => n.Length >= 4);
I did some research on how it works and found this source:
public static partial class Enumerable
{
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);
if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
return new WhereEnumerableIterator<TSource>(source, predicate);
}
/* ... */
}
I don't understand why its first parameter, this IEnumerable<TSource> source, is prefixed with the this keyword. I know that extension methods allow an existing type to be extended with new methods without
altering the definition of the original type and that the type
of the first parameter will be the type that is extended.
Can you explain its logic beneath?
Because it is an Extension Method.
So that instead of
IEnumerable<string> filteredNames = System.Linq.Enumerable.Where(names, n => n.Length >= 4);
You can also use it like:
IEnumerable<string> filteredNames = names.Where(n => n.Length >= 4);
The Reason it is an extension method is that IEnumerable, List, ... existed long before Linq (which was introduced in .Net 3.5) and its job is just to extend finding, filtering, ordering, ... them. So it is logical to have it as an Extention method rather than a separate library. And also consider that this way you can use chaining, which woudln'd be possible if it wasn't an extension:
name.Where(x => x.Length > 4).Select(x => x.Substring(4));
Compare it to:
System.Linq.Enumerable.Select(System.Linq.Enumerable.Where(name, x => x.Length > 4), x => x.Substring(4));
And this is only a very simple one, consider how dirty it gets with larger, complex queries.
Since it's an Extension Method. It means that Where is not a method on IEnumerable but when you reference Linq namespace ,Where method is added to IEnumerable.
for more info read this :
Extension Methods
First of all, The simple answer is because it is an extension method. The definition of extension method is that Their first parameter specifies which type the method operates on, and the parameter is preceded by the this modifier.
Secondly, I disagree Ashkan Mobayen Khiabani's answers last part. you must have a look fluent implementation without extension method.
I have two version of grouping by a list of items
List<m_addtlallowsetup> xlist_distincted = xlist_addtlallowsetups.DistinctBy(p => new { p.setupcode, p.allowcode }).OrderBy(y => y.setupcode).ThenBy(z => z.allowcode).ToList();
and groupby
List <m_addtlallowsetup> grouped = xlist_addtlallowsetups.GroupBy(p => new { p.setupcode, p.allowcode }).Select(grp => grp.First()).OrderBy(y => y.setupcode).ThenBy(z => z.allowcode).ToList();
these two seemed to me that they are just the same, but there's gotta be a layman's explanation of their difference, their performance and disadvantages
Let's review the MoreLinq APIs first, following is the code for DistinctBy:
MoreLinq - DistinctBy
Source Code
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
return _(); IEnumerable<TSource> _()
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
}
}
Working
Using HashSet<T> internally it just checks the first match and returns the first element of Type T matching the Key, rest are all ignored, since Key is already added to the HashSet
Simplest way to get the first element pertaining to every unique Keyin the collection as defined by the Func<TSource, TKey> keySelector
Use case is limited (Subset of what GroupBy can achieve, also clear from your code)
Enumerable - GroupBy
(Source Code)
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) {
return new GroupedEnumerable<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
}
internal class GroupedEnumerable<TSource, TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>
{
IEnumerable<TSource> source;
Func<TSource, TKey> keySelector;
Func<TSource, TElement> elementSelector;
IEqualityComparer<TKey> comparer;
public GroupedEnumerable(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) {
if (source == null) throw Error.ArgumentNull("source");
if (keySelector == null) throw Error.ArgumentNull("keySelector");
if (elementSelector == null) throw Error.ArgumentNull("elementSelector");
this.source = source;
this.keySelector = keySelector;
this.elementSelector = elementSelector;
this.comparer = comparer;
}
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() {
return Lookup<TKey, TElement>.Create<TSource>(source, keySelector, elementSelector, comparer).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
Working
As it can be seen, internally use a LookUp data structure to group all the data for a given Key
Provides flexibility to element and result selection via projection, thus would be able to meet lot of different use cases
Summary
MoreLinq - DistinctBy achieves a small subset of what Enumerable - GroupBy can achieve. In case your use case is specific, use the More Linq API
For your use case, speed wise as the scope is limited MoreLinq - DistinctBy would be faster, since unlike Enumerable - GroupBy, DistinctBy doesn't first aggregate all data and then select first for each unique Key, MoreLinq API just ignores data beyond first record
If the requirement is specific use case and no data projection required then MoreLinq is a better choice.
This is a classic case in Linq, where more than one API can provide same result but we need to be wary of the cost factor, since GroupBy here is designed for much wider task than what you are expecting from DistinctBy
The Differences
GroupBy should result a 'group' that contains key (the grouping criteria) and its value. thats why you need to do Select(grp => grp.First()) first.
You might suspect MoreLinq just provide shorthand of it. MoreLinq by the source, the DistinctBy is actually done in memory by picking every single item that is new for the HashSet. The HashSet#Add will add item and returns true if its a new element for the HashSet, then the yield will return the newly added element into the enumerable.
Which One?
SQL Related
Based on the difference above, you could say doing GroupBy then project it with Select is much more safer approach as it can be translated into SQL command if you are using Entity Framework (or Linq2Sql, i suppose). Being able to be translated into SQL command is a great advantage to reduce the burden from your application and delegate operations to the Database Server instead.
However, you had to understand that GroupBy in the Entity Framework actually uses OUTER JOIN that considered as complex operation and on some cases it may cause your query being dropped immediately. Its pretty rare case, even the query i throw had lots of column, around four GroupBys are used, a bunch of ordering and Wheres.
Linq to Object
Roughly speaking when dealing with an already in memory enumerables. Running GroupBy then Select may end up have your enumerable need to be iterated by two operations. While directly using DistinctBy from MoreLinq can save some graces as it guarantees to be a single operation backed with HashSet as explained by Mrinal Kamboj answer with in depth analysis against the source code.
I have a list of strings:
List<string> tList=new List<string>();
tList.add("a");
tList.add("mm");
I want to convert this list to a Dictionary so the key and the value of the dictionary is the same using linq
I have tried:
var dict = tList.ToDictionary<string,string>(m => m, c => c);
but I get the following error:
Cannot convert lambda expression to type 'IEqualityComparer' because it is not a delegate type
Use ToDictionary method:
List<string> tList = new List<string>();
tList.add("a");
tList.add("mm");
var dict = tList.ToDictionary(k => k, v => v);
Do not forget add reference to System.Linq.
Here are the signatures for ToDictionary
ToDictionary<TSource, TKey>(
IEnumerable<TSource>,
Func<TSource, TKey>)
ToDictionary<TSource, TKey>(
IEnumerable<TSource>,
Func<TSource, TKey>,
IEqualityComparer<TKey>)
ToDictionary<TSource, TKey, TElement>(
IEnumerable<TSource>,
Func<TSource, TKey>,
Func<TSource, TElement>)
ToDictionary<TSource, TKey, TElement>(
IEnumerable<TSource>,
Func<TSource, TKey>,
Func<TSource, TElement>,
IEqualityComparer<TKey>)
You want the 3rd one, but since you call it and specify two generic types it is instead using the 2nd one and your second argument (actually 3rd since the first is the argument the extension method is called on) is not an IEqualityComparer<TKey>. The fix is to either specify the third type
var dict = tList.ToDictionary<string,string,string>(m => m, c => c);
Don't specify the generic types and let the compiler figure it out via type inference
var dict = tList.ToDictionary(m => m, c => c);
Or since you want the items to be the values you can just use the 1st one instead and avoid the second lambda altogether.
var dict = tList.ToDictionary(c => c);
Has anyone encountered this problem? I have two same candidates to method Enumerable.Where
And what is the Func'2 and Func'3?
When i trying to filter enumerable
var subItems = itemsToShow.Where(item => item.Visible);
I have an error:
Cannot resolve method 'Where(lambda expression)', candidates are
System.Collection.Generic.IEnumerable<T> Where<T>(this System.Collection.Generic.IEnumerable<T>, System.Func'2) (in calss Enumerable)
System.Collection.Generic.IEnumerable<T> Where<T>(this System.Collection.Generic.IEnumerable<T>, System.Func'3) (in calss Enumerable)
On .Net 3.5 this work perfect
A quick look at the MSDN tells you that there are in fact two overloads.
One just filters based on a predicate, and the second overload also takes the index of the item in the enumeration into account.
Func'3 and Func'2 meens that it is a generic class with 2 and 3 type parameters.
I assume that first is for Func<T, bool> where T is your earlier defined type.
and Func<T, int, bool> the same plus indexer.
Func<T, int, bool> - it is a predicate that accepts two arguments of types T and int and returns bool.
Just build the solution and see the detailed error. Mine was a nullable boolean.
It happened to me because I was trying to use .Contains on a List type, while what I needed was .Any
for example
var myObjectsList = new List<MyClass>();
// instead of this
myObjectList.Contains(x => x.Id == 1)
// use this
myObjectList.Any(x => x.Id == 1)
Try casting to an IQueryable. Like so: itemsToShow.AsQueryable()