Tuesday, May 3, 2011

Is there a way to capture the index value in a LINQ Where method in C#?

My following C# code is obviously a hack so how do I capture the index value in the Where method?

     string[] IntArray = { "a", "b", "c", "b", "b"};
                    int index=0;
                    var query = IntArray.Where((s,i) => (s=="b")&((index=i)==i));

    //"&" and "==i" only exists to return a bool after the assignment ofindex

    foreach (string s in query)
                {
                    Console.WriteLine("{0} is the original index of {1}", index, s);
                }
//outputs...
//1 is the original index of b
//3 is the original index of b
//4 is the original index of b
From stackoverflow
  • Which index do you want? You have three times b. If you want all indices of b, then:

            string[] IntArray = { "a", "b", "c", "b", "b" }; 
    
            var query = IntArray
                        .Select((x, i) => new { value = x, idx = i })
                        .Where(s => s.value == "b");
    
    David Smith : Thanks, that was the intent.
  • The Where method only returns whether or not the item should be included in the result or not. The function can't provide any more information in a sensible way (it could capture a local variable and do stuff with it, but that would be horrible).

    If you want the index in the final result, you'll need to create a projection which includes that index. If you want the original index in the final result, you'll need to put that projection before any Where clauses.

    Here's an example of that:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public class Test
    {
        static void Main()
        {
            IEnumerable<char> letters = "aBCdEFghIJklMNopQRsTUvWyXZ";
    
            var query = letters.Select((c, i) => 
                                       new { Char=c, OriginalIndex=i })
                               .Where(x => char.IsLower(x.Char))
                               .Select((x, i) =>
                                       new { x.Char,
                                             x.OriginalIndex,
                                             FinalIndex=i});
    
            foreach (var result in query)
            {
                Console.WriteLine(result);
            }
        }
    }
    

    Results:

    { Char = a, OriginalIndex = 0, FinalIndex = 0 }
    { Char = d, OriginalIndex = 3, FinalIndex = 1 }
    { Char = g, OriginalIndex = 6, FinalIndex = 2 }
    { Char = h, OriginalIndex = 7, FinalIndex = 3 }
    { Char = k, OriginalIndex = 10, FinalIndex = 4 }
    { Char = l, OriginalIndex = 11, FinalIndex = 5 }
    { Char = o, OriginalIndex = 14, FinalIndex = 6 }
    { Char = p, OriginalIndex = 15, FinalIndex = 7 }
    { Char = s, OriginalIndex = 18, FinalIndex = 8 }
    { Char = v, OriginalIndex = 21, FinalIndex = 9 }
    { Char = y, OriginalIndex = 23, FinalIndex = 10 }
    
    David Smith : Thanks Jon, that cleared things up. I was just making sure I have not missed something with the Where method or maybe something else to do with let or into. I need to read some more.
  • Your current code captures the last index of "b" in the variable index. To replicate your functionality using LINQ, use:

    int index = IntArray.LastOrDefault(s => s == "b");
    

    I don't know whether this is what you actually want.

    David Smith : The code in the question does work. When projected it gives the index value of each occurrence of the letter b not just the last one.

0 comments:

Post a Comment