3

I have a LINQ statement that uses a custom function to find certain matching data in a compressed string.

There is only ever one match and if I had done a FOR loop I would have added a "break" once the data had been found. However using LINQ it seems to iterate through all records looking for matches whether it needs to or not (which makes sense from an SQL/subset point of view) however I was wondering if there was a way to make the LINQ statement break on completion of the WHERE clause so that it didn't have to carry on searching for matching records after it had found the single match.

I could just re-write it all to use a FOR loop but I wanted to know if there was a way of limiting the LINQ statement to stop searching after X iterations if the condition had been matched.

The code is below

    IEnumerable<MarketDataType> queryMarkets =
            from m in Mdata
            where !String.IsNullOrEmpty(m)
            let field = m.Split('~')
            where (MatchMarket(field[5], BaseDate.AddMilliseconds(DaylightSavings + Convert.ToDouble(field[4])), field[1], racecourse, racedatetime, marketType))
            select new MarketDataType()
            {
                marketId = Convert.ToInt32(field[0]),
                marketName = field[1].Replace(ColonCode, ":"),
                marketType = field[2],
                marketStatus = field[3],
                eventDate = BaseDate.AddMilliseconds(DaylightSavings + Convert.ToDouble(field[4])),
                menuPath = field[5].Replace(ColonCode, ":"),
                eventHeirachy = field[6],
                betDelay = Convert.ToInt32(field[7]),
                exchangeId = Convert.ToInt32(field[8]),
                countryCode = field[9],
                lastRefresh = BaseDate.AddMilliseconds(DaylightSavings + Convert.ToDouble(field[10])),
                noOfRunners = Convert.ToInt32(field[11]),
                noOfWinners = Convert.ToInt32(field[12]),
                totalAmountMatched = Convert.ToDouble(field[13]),
                bspMarket = (field[14] == "Y"),
                turningInPlay = (field[15] == "Y")
            };

        marketData = queryMarkets.ToList();

Could I add in another WHERE clause that somehow looked inside my IEnumerable object MarketDataType to ensure that if a row existed then it was ignored? OR would re-writing this as a foreach/for loop be the best way.

Any help would be much appreciated. Thanks

Monkey Magix
  • 163
  • 2
  • 3
  • 10

3 Answers3

8

Don't forget Take and TakeWhile

From TakeWhile...

Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.

Amy B
  • 108,202
  • 21
  • 135
  • 185
4

It sounds like you want First(), FirstOrDefault(), Single() or SingleOrDefault():

MarketDataType result = queryMarkets.FirstOrDefault();

(All these methods will give you the same result if there's exactly one result; they differ in their handling of zero or multiple results.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for your reply but I am pretty new to LINQ, can you give me the edited example I provided with the position of the code you mention. I guess you have to add it to the main LINQ statment somewhere otherwise it would still loop through the whole set to create the subset? – Monkey Magix Nov 11 '11 at 16:18
  • @user1037545: Just replace your current last line of code. You don't need to change the declaration of `queryMarkets` at all. LINQ is lazily evaluated - the statement declaring `queryMarkets` just sets up the query - it's only when you start evaluating the results that it actually runs. However, if you're new to LINQ I'd recommend you'd start with something slightly simpler than your current big query :) – Jon Skeet Nov 11 '11 at 16:20
  • Well I'm new to LINQ but have 15+ years of SQL experience so I was under the impression it was a pretty similar way to iterate over datasets of multiple types. The code has been working for over a year now but I just noticed during a debug that even though the first race of the day was being matched at the beginning of the LINQ if I put a log message in the MatchMarket function that all the other races were still looked at which seemed a waste of loop iterations. Thanks for your help – Monkey Magix Nov 11 '11 at 16:43
  • @user1037545: Ignore the "it looks a bit like SQL" part - learn LINQ as LINQ; its idioms, its execution model etc. – Jon Skeet Nov 11 '11 at 16:45
  • I'm actually getting "Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object." error now I added that code in where I wasn't before? – Monkey Magix Nov 11 '11 at 17:01
  • @user1037545: If you're using `FirstOrDefault`, that suggests it may be returning null, which is what it will do if there are no results. – Jon Skeet Nov 11 '11 at 17:30
1

Just use First() instead of ToList() on the IEnumerable, this will stop the evaluation after the first match.

Brandon Moretz
  • 7,512
  • 3
  • 33
  • 43