Please explain System.Linq.Enumerable.Where(Func<T, int, bool> predicate) - c#

I can't make any sense of the MSDN documentation for this overload of the Where method that accepts a predicate that has two arguments where the int, supposedly, represents the index of the source element, whatever that means (I thought an enumerable was a sequence and you couldn't see further than the next item, much less do any indexing on it).
Can someone please explain how to use this overload and specifically what that int in the Func is for and how it is used?

The int parameter represents the index of the current item within the current iteration. Each time you call one of the LINQ extension methods, you aren't in theory guaranteed to get the items returned in the same order, but you know they're all be returned once each and thus can be assigned indices. (Well, you are guaranteed if you know the query object is a List<T> or such, but not in general.)
Example:
var result1 = myEnumerable.Where((item, index) => index < 4);
var result2 = myEnumerable.Take(4);
// result1 and result2 are equivalent.

You can't index an IEnumerable<T> in the same way you can an array, but you might be able to use the index to filter the list in some way, or possibly to index some data in another collection which will be used in the condition.
EDIT: As an example, to skip every other element you could use:
var results = sequence.Where((item, idx) => idx % 2 == 0);

Related

C# Linq questions on select parameters

I have the basic understanding of select method in linq.
var arrayIndex = Enumerable.Range(10, 10).ToArray();
This one will create an array with integers 10 to 19.
For some real life problems, I need to use the index of the original data.
After some research, I found that I can do something like this
var multipliedArray = arrayIndex.Select((i, Index) => i * Index).ToArray();
I know that i is the item itself, in this case i will be integer.
So, just two questions
How do I know the full list of parameters that can put within the bracket (like the Index).
What's the other use case for having different parameter in the bracket.
The part within the Select...
(i, Index) => i * Index
...is lambda expression, which is equivalent to an anonymous method. If you would write the method non-anonymously, it would look like this:
int Calculate(int i, int Index) {
return i * Index;
}
When you want to pass a method as an argument, the respective parameter needs to be a delegate that matches the method's signature. The given Calculate nethod and, since they are sharing the same signature, your lambda expression match the signaturee of the Func<int, int, int> delegate. As it turns out, Enumerable.Select<int, int>offers you an overload with exactly this delegate, so you can use your lambda expression. The other (more widely used) overload accepts a Func<int, int>, which is the version without the index. There are no other overloads, so you can not have other arguments in your lambda expression.
For a better understanding, you can refer to the relevant document Or using Code Completion in development environments.

Manipulating array's index using Lambda Expression

I was going through some tutorial about LINQ and I bumped into following code, I could not understand what 'n' is doing here,though I understand Author is trying to get every third element.It clearly shows I lack understanding of Lambda expression.(It would be great if some one can provide beginner to master link for that, as of now when I try to find them I never find them with solid fundamentals and result is botched understanding). In following array every third element is in 'Yen'(currency).
static double[] ExchangedPrices = {827.70, 604.50, 111869.70,
1869.00, 1,365.00, 252609.00,
521.36, 380.77, 70465.88,
455.68, 332.80, 61588.48,
2018.34, 1474.07, 272793.66,
920.26, 672.10, 124379.86,
1873.45, 1368.25, 253210.45,
149.34, 109.07, 20184.66,
455.68, 332.80, 61588.48,
525.28, 383.63, 70995.16,
9.08, 6.63, 1226.96,
311.50, 227.50, 42101.50};
IEnumerable<double> yenQuery = ExchangedPrices.Where((n, index) => index%3 == 0);
Using Where will essentially loop through the array and return elements that meet a given condition.
n represents the element itself and index represents the index of the element on each iteration.
So the where statement is going through each of the elements of the array one by one and each time testing if the index of that element is divisible by 3.
The msdn Lambda article is a good start to gain a better understanding of Lambdas.
Here n represents each item in your ExchangedPrices array. index is variable which holds the (zero based) index value of the sequence and using that we are checking an if condition index%3==0. So when the code runs, This condition will be evaluated againist each item in the iteration and will return true or false. The LINQ Where clause takes this predicate and will eventually return a subset of original data based on the result of this if condition. So if the predicate expression returns true, the corresponding item (value of n at that iteration) will be used to build the subset data which will be returned.
Since the if condition expression is going to return true for 12 times while evaluating 37 items in the loop, It will get those 12 items from your original array and will be stored to your yenQuery variable.
ExchangedPrices.Where((n, index) => index%3 == 0);
This line is creating a lambda expression of n and index. n represents the value of the double and index represents the index of that double in the array ExchangedPrices. You could use n inside of your lambda expression just like you use index.
ExchangedPrices.Where((n, index) => n%3 == 0);
This line would get you all of the values in your array that are divisible by 3 as opposed to every 3rd element in the array by index.

Using AsSequential in order to preserve order

I am looking at this code
var numbers = Enumerable.Range(0, 20);
var parallelResult = numbers.AsParallel().AsOrdered()
.Where(i => i % 2 == 0).AsSequential();
foreach (int i in parallelResult.Take(5))
Console.WriteLine(i);
The AsSequential() is supposed to make the resulting array sorted. Actually it is sorted after its execution, but if I remove the call to AsSequential(), it is still sorted (since AsOrdered()) is called.
What is the difference between the two?
AsSequential is just meant to stop any further parallel execution - hence the name. I'm not sure where you got the idea that it's "supposed to make the resulting array sorted". The documentation is pretty clear:
Converts a ParallelQuery into an IEnumerable to force sequential evaluation of the query.
As you say, AsOrdered ensures ordering (for that particular sequence).
I know that this was asked over a year old but here are my two cents.
In the example exposed, i think it uses AsSequential so that the next query operator (in this case the Take operator) it is execute sequentially.
However the Take operator prevent a query from being parallelized, unless the source elements are in their original indexing position, so that is why even when you remove the AsSequential operator, the result is still sorted.

Get certain item from each Tuple from List

What is the correct way to go about creating a list of, say, the first item of each Tuple in a List of Tuples?
If I have a List<Tuple<string,string>>, how would I get a List<string> of the first string in each Tuple?
A little Linq will do the trick:
var myStringList = myTupleList.Select(t=>t.Item1).ToList();
As an explanation, since Tim posted pretty much the same answer, Select() creates a 1:1 "projection"; it takes each input element of the Enumerable, and for each of them it evaluates the lambda expression and returns the result as an element of a new Enumerable having the same number of elements. ToList() will then spin through the Enumerable produced by Select(), and add each element one at a time to a new List<T> instance.
Tim has a good point on the memory-efficiency; ToList() will create a list and add the elements one at a time, which will cause the List to keep resizing its underlying array, doubling it each time to ensure it has the proper capacity. For a big list, that could cause OutOfMemoryExceptions, and it will cause the CLR to allocate more memory than necessary to the List unless the number of elements happens to be a power of 2.
List<string> list = tuples.Select(t => t.Item1).ToList();
or, potentially less memory expensive:
List<string> list = new List<String>(tuples.Count);
list.AddRange(tuples.Select(t => t.Item1));
because it avoids the doubling algorithm of List.Add in ToList.
If you have a List<Tuple<string, string>> listoftuples, you can use the List's implementation of the Select method to take the first string from each Tuple.
It would look like this:
List<string> firstelements = listoftuples.Select(t => t.Item1).ToList();
Generialised Variant:
for selecting a particular item where the collection's tuple's length is unknown i.e 2,3,4 ...:
static IEnumerable TupleListSelectQuery<T>(IEnumerable<T> lst, int index) where T : IStructuralEquatable, IStructuralComparable, IComparable
{
return lst.Select(t => typeof(T).GetProperty("Item" + Convert.ToString(itemNumber)).GetValue(t)).ToList();
}
where index's value corresponds to the way tuples are enumerated i.e 1,2,3 ... (not 0,1,2...).

LINQ - selecting second item in IEnumerable

I have
string[] pkgratio= "1:2:6".Split(':');
var items = pkgratio.OrderByDescending(x => x);
I want to select the middle value and have come up with this. Is this a correct way to select the second value in an IEnumberable?
pkgratio.Skip(1).Take(1).First();
While what you have works, the most straightforward way would be to use the array's index and reference the second item (at index 1 since the index starts at zero for the first element): pkgratio[1]
Console.WriteLine(pkgratio[1]);
A more complete example:
string[] pkgratio = "1:2:6".Split(':');
for (int i = 0; i < pkgratio.Length; i++)
Console.WriteLine(pkgratio[i]);
With an IEnumerable<T> what you have works, or you could directly get the element using the ElementAt method:
// same idea, zero index applies here too
var elem = result.ElementAt(1);
Here is your sample as an IEnumerable<string>. Note that the AsEnumerable() call is to emphasize the sample works against an IEnumerable<string>. You can actually use ElementAt against the string[] array result from Split, but it's more efficient to use the indexer shown earlier.
var pkgratio = "1:2:6".Split(':').AsEnumerable();
Console.WriteLine(pkgratio.ElementAt(1));
I don't think you need to .Take(1).
pkgratio.Skip(1).First()
pkgratio.ElementAt(1); for your scenario.
However, your method is only applicable if you were using some data that implemented IQueryable or you needed to take a range of items starting at a specific index eg:
pkgratio.Skip(5).Take(10);
Well, the Take(1) isn't strictly necessary if you're going to just First() it, so I might go with
pkgratio.Skip(1).First();
However, that First() will throw an exception if there no value, so you might want to try FirstOrDefault() and then check for null.

Categories