views:

58

answers:

3

How to perform this without iteration, with LINQ?

 string[] flatSource = {"region1", "subregion1", 
                        "region2", "sub1", "sub2", 
                        "region3", "sub1", "sub2"};

 string previousRegion = "";
 foreach (var item in flatSource)
 {
    if (SomeRegionDictionary.Contains(item))
       previousRegion = item;  //This is what I can't figure out 
    else
       yield return new SubregionWithRegion{Region = previousRegion, SubRegion = item};
 }
+1  A: 

Maybe you could find the Enumerable.Aggregate method useful.

Konamiman
+2  A: 

Your current solution is fine; LINQ isn't the ideal choice for such stateful queries. Here's a pure-LINQ solution though; it's not ideal because it is a little cryptic and has quadratic complexity, but is functionally equivalent:

return flatSource.Select((item, index) => 
                     new SubregionWithRegion
                     {
                       Region = flatSource.Take(index + 1)
                                          .LastOrDefault(SomeRegionDictionary.ContainsKey) ?? "",
                       SubRegion = item
                     })
                  .Where(srwr => !SomeRegionDictionary.ContainsKey(srwr.SubRegion));

The stateful nature of the loop is handled with the Take + LastOrDefault queries, and the tricky else condition is handled by the final Where clause.

Ani
+2  A: 

Seems that i didn't read the question carefully enough. So the extension below might be useful, but not to this question.

public static class IEnumerableOfTExtensions
{
    public static T Before<T>(this IEnumerable<T> source, Func<T, bool> condition)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        if (condition == null)
            throw new ArgumentNullException("condition");

        using (var e = source.GetEnumerator())
        {
            var first = true;
            var before = default(T);

            while (e.MoveNext())
            {
                if (condition(e.Current))
                {
                    if (first)
                        throw new ArgumentOutOfRangeException("condition", "The first element corresponds to the condition.");

                    return before;
                }

                first = false;
                before = e.Current;
            }
        }

        throw new ArgumentOutOfRangeException("condition", "No element corresponds to the condition.");
    }
}
Oliver
@Oliver: How does the final query come anywhere close to solving the OP's original question?
Ani
@Ani: You're right. I just didn't read the question carefully enough. So i let my answer here, but yours seem to solve the concrete problem.
Oliver