Enumerable.Where(predicate) vs. Enumerable.All(predicate) - c#

What's the difference between:
items.Where(i => i.Foo == "bar")
and
items.All(i => i.Foo == "bar")
? Seems like they'd do the exact same thing - is this actually the case, or do they behave differently in some subtle way?

Where<T> returns an IEnumerable<T> and acts as a filter for your collection:
Enumerable.Where Method
Filters a sequence of values based on a predicate.
In your case, it returns all elements where the Foo property equals "bar".
All<T> returns a bool and just checks if all elements in your collection satisfies the given predicate:
Enumerable.All Method
Determines whether all elements of a sequence satisfy a condition.
In your case, it returns true when the Foo property of all elements equals "bar", otherwise false.

items.Where - filters based on condition and returns a collection of filtered objects.
items.All - returns true if all objects in the collection satisfy the condition, and false otherwise.
Does not look the exact same thing , does it?

Enumerable.All returns a bool value that indicates whether all the elements in question satisfy the condition that is expressed by the predicate.
Enumerable.Where returns a generic IEnumerable collection that contains only the items that satisfy the said condition.
I do not see anything in common between the results that are being returned from those LINQ functions - one is a bool, the other is a collection. Yes, they are similar in syntax, but are used in completely different situations.
A quick example:
Let's say you have an int list:
List sampleList = new List { 3, 4, 6, 9 };
If you run
sampleList.All(v => v % 3 == 0);
you will get false because 4 does not satisfy the condition.
If you run
sampleList.Where(v => v % 3 == 0);
you will receive an IEnumerable containing the values that satisfy the condition: 3, 6 and 9/

Related

Distinct function on a list does not change the list [duplicate]

Having issues with the OrderBy clause not having any impact on the sort. I have walked through this in the debugger and insuring this is a case that the sort line of the code is being hit and reviewing the results after it the order by has not been applied.
public static IEnumerable<DDLOptions<TValueType>> GetDDLOptionsViewModel<TClass, TValueType>(
IEnumerable<TClass> list,
Func<TClass, TValueType> value,
Func<TClass, string> displayText,
bool sort = true
)
{
List<DDLOptions<TValueType>> ddlOptions;
ddlOptions = list.Select(
l => new DDLOptions<TValueType>
{
Value = value(l),
DisplayText = displayText(l)
}
).ToList(); <========== Works if I put the Order By here.
if (sort)
{
ddlOptions.OrderBy(l => l.DisplayText); <===== Does NOT work here.
}
return ddlOptions;
}
OrderBy returns a query that would perform the ordering: it does not modify the original list (whereas something like List<T>.Sort would modify the original)
Instead try something like:
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText).ToList();
You might want to play around with the type of ddlOptions or where/how you return the data as we're doing an extra ToList than probably necessary, but that's probably a minor/non-issue for this case anyway.
Note that the same applies to other LINQ functions like GroupBy, Distinct, Concat - all return results rather than modifying the source collection.
Try:
if (sort)
{
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText); <===== Should work now.
}
As others have said, you need to assign the result of OrderBy to something as it doesn't mutate the sequence it acts on. It's easiest to make ddlOptions an IEnumerable instead of a List, so that you can assign the result to that. The ToList call on the select is also not needed:
public static IEnumerable<DDLOptions<TValueType>> GetDDLOptionsViewModel<TClass, TValueType>(
IEnumerable<TClass> list,
Func<TClass, TValueType> value,
Func<TClass, string> displayText,
bool sort = true
)
{
IEnumerable<DDLOptions<TValueType>> ddlOptions;
ddlOptions = list.Select(
l => new DDLOptions<TValueType>
{
Value = value(l),
DisplayText = displayText(l)
}
);
if (sort)
{
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText);
}
return ddlOptions;
}
Note that this version of the method will use deferred execution, and so won't actually perform the Select/OrderBy until the sequence is iterated. If you don't want to do that, you can add ToList on the return line.
You need to type:
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText);
OrderBy doesn't sort a List<T> or any other IEnumerable<T>. It produces a new, sorted IEnumerable<T>. So calling ddlOptions.OrderBy(...) doesn't modify ddlOptions.
If you have a List<T> and wish to sort it, you can use the Sort method - in particular the overload that takes a Comparison<T> as a parameter. This actually sorts the list instead of returning a new IEnumerable.
Comparison<T> is a delegate representing a function that takes two of T and returns a negative number if the first is "less" than the second, a positive number if the first is "greater" than the second, and zero if one isn't sorted before or after the other.
In this case you don't have to remember that. Instead, you can just do this:
ddlOptions.Sort((x, y) => string.CompareOrdinal(x.DisplayText, y.DisplayText));
You're passing in a function that takes two items in the list and returns the comparison result of their DisplayText properties, which will be negative, 0, or positive.
Sometimes we use OrderBy because it doesn't modify the original list. But if modifying the list is what we want then we can use Sort.

How can I use a Lambda bool method within a WHERE clause?

I have a method IsMatchingRegex which will return true or false. I want to check whether the Lambda property matches against the IsMatchingRegEx. If it does match, it should be added to the validItems List. How can I make the Lambda Expression work without changing the TRUE/FALSE method?
validItems = items.Where(x => x.Sub.PropertyToCheck == IsMatchingRegex(x.Sub.PropertyToCheck))
Why compare equality to the property? Just:
validItems = items.Where(x => IsMatchingRegex(x.Sub.PropertyToCheck));
The Where expects a predicate that given an item of the collection returns for it true or false. If you method already does that - just call it.

Does LINQ know how to optimize "queries"?

Suppose I do something like
var Ordered = MyList.OrderBy(x => x.prop1).ThenBy(x => x.prop2);
Does MyList.OrderBy(x => x.prop1) return the filtered list, and then does it further filter that list by ThenBy(x => x.prop2)? In other words, is it equivalent to
var OrderedByProp1 = MyList.OrderBy(x => x.prop1);
var Ordered = OrderedByProp1.OrderBy(x => x.prop2);
???
Because obviously it's possible to optimize this by running a sorting algorithm with a comparator:
var Ordered = MyList.Sort( (x,y) => x.prop1 != y.prop1 ? x.prop1 < y.prop1 : ( x.prop2 < y.prop2 ) );
If it does do some sort of optimization and intermediate lists are not returned in the process, then how does it know how to do that? How do you write a class that optimizes chains of methods on itself? Makes no sense.
Does MyList.OrderBy(x => x.prop1) return the filtered list
No. LINQ methods (at least typically) return queries, not the results of executing those queries.
OrderBy just returns an object which, when you ask it for an item, will return the first item in the collection given a particular ordering. But until you actually ask it for a result it's not doing anything.
Note you can also get a decent idea as to what's going on by just looking at what OrderBy returns. It returns IOrderedEnumerable<T>. That interface has a method CreateOrderedEnumerable which:
Performs a subsequent ordering on the elements of an IOrderedEnumerable according to a key.
That method is what ThenBy uses to indicate that there is a subsequent ordering.
This means that you're building up all of the comparers that you want to be used, from the OrderBy and all ThenBy calls before you ever need to generate a single item in the result set.
For more specifics on exactly how you can go about creating this behavior, see Jon Skeet's blog series on the subject.

Sort based on function

I have a method that given 2 strings he returns a number (between 0 and 100) which represents is how alike they are, being 0 "not similar at all" and 100 "they are the same"
Now the thing is that i have a list of County (string name, GeoRef coordinates, string Mayor) which i would like to sort based on the return of my function...
im looking for something like myList.Sort(f=>MyScoreEvaluator("York",f.Name))
Can anyone tell me how to do so?
Edit1: I dont think that the method "Sort" is quite i want... Sort compare itens inside of the list... i want to compare the itens of the list against a external info and based on that result sort the items
The OrderBy and OrderByDescending are returning the same item order...
Edit2: Heres is the code of the OrderBy I'm using: aux.OrderBy(f => StringComparisonHelper.HowAlike(f.Name, countyNameSearched));
You can use OrderBy, and re-assign your list:
list = list.OrderBy(f => MyScoreEvaluator("York", f.Name))
You could just use OrderBy:
list.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or Implement a custom Comparer:
public static int SortByName(County x, County y)
{
return x.Name.CompareTo(y.Name);
}
Usage:
list.Sort(new Comparison<County>(SortByName))
There is an OrderBy in LINQ:
var sorted = myList.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or to sort descendingly:
var sortedDesc = myList.OrderByDescending(f => MyScoreEvaluator("York", f.Name))
It's very easy to use the LINQ OrderBy extension (see others' answers).
If you want to use Sort, it would be:
myList.Sort((x, y) => MyScoreEvaluator("York", x.Name)
.CompareTo(MyScoreEvaluator("York", y.Name)));
This assumes that myList is a System.Collections.Generic.List<>.
If you want the other sort direction, swap x and y on one side of the lambda arrow =>, of course.
EDIT:
Remember .Sort method on List<> modifies the same instance. The return type of Sort method is void. On the other hand, OrderBy creates a new IEnumerable<> on which you can call .ToList() to get a new list object. The old object is unchanged. You might assign the new object to the variable that held the original list. Other variables that reference the old object won't be affected by that. Example:
myList = myList.OrderBy(f => MyScoreEvaluator("York", f.Name)).ToList();
NEW EDIT:
If performance is an issue, it's not clear which of these two to use. The OrderBy method calls the MyScoreEvaluator only once per item in your original list. The Sort method as presented here, calls MyScoreEvaluator a lot more times, because it doesn't "remember" the result of each MyScoreEvaluator call (the Comparison<> delegate instance is a black box to the Sort algorithm). So if it wants to compare "Fork" and "Kork", it calls MyScoreEvaluator twice. Then afterwards if it wants to compare "Kork" and "Yorc", it does the "Kork" MyScoreEvaluator again. On the other hand, the sort algorithm of List<>.Sort is superior to that of OrderBy.

How to make a default(TSource)

In Linq When I call SingleOrDefault or FirstOrDefault how do I return something other than null for a particular object eg.
List<CrazyControls> cc = CrazyControlRepository.All();
cc.SingleOrDefault(p => p.Id == id).Render();
How do I make my CrazyControls return a default instance that implements a base Render() method?
With DefaultIfEmpty(defaultValue). This will ensure that if the collection is empty, it will be populated with a default instance of the type.
So you can do:
var defaultValue = new CrazyControl(...);
List<CrazyControls> cc = CrazyControlRepository.All();
cc.Where(p => p.Id == id).DefaultIfEmpty(defaultValue).First().Render();
The query expression needed to change a bit. The new one works like this:
Filter the collection according to the existing criteria. This will leave either one or no items in the filtered sequence.
Use DefaultIfEmpty to make sure that the sequence contains exactly one item (if it had one already, DefaultIfEmpty will do nothing).
Use First to get the single item. The reason I did not use Single instead of first is that if the predicate were different (or it changes in the future) and it accepted multiple items, Single would throw.
You need to define this `something' that you want to return if there are no elements:
(cc.SingleOrDefault(p => p.Id == id) ?? new CrazyControls()).Render();
In other words you need to define the default value.

Categories

Resources