Исключить результаты из запроса Linq, исключая все, когда список исключений пуст - PullRequest
1 голос
/ 24 февраля 2011

У меня есть следующий код:

        public IList<Tweet> Match(IEnumerable<Tweet> tweetStream, IList<string> match, IList<string> exclude)
    {
        var tweets = from f in tweetStream
                     from m in match
                     where f.Text.ToLowerInvariant().Contains(m) 
                     select f;

        var final = from f in tweets
                    from e in exclude
                    where !f.Text.ToLowerInvariant().Contains(e.ToLowerInvariant())
                    select f;

        return final.Distinct().ToList<Tweet>();
    }

Я создавал тесты, которые не включали набор результатов final, и теперь успешно соответствовал, теперь я добавил исключение, если IList<string>exclude пусто, все элементы удалены.

Итак, этот тест проходит как следует:

        [TestMethod]
    public void Should_exclude_items_from_exclude_list()
    {
        IEnumerable<Tweet> twitterStream = new List<Tweet>
                                               {
                                                   new Tweet("I have a Mazda car"),
                                                   new Tweet("I have a ford"),
                                                   new Tweet("Mazda Rules"),
                                                   new Tweet("My Ford car is great"),
                                                   new Tweet("My renault is brill"),
                                                   new Tweet("Mazda cars are great")
                                               };
        IList<string> matches = new List<string>{"mazda","car"};
        IList<string> exclude = new List<string>{"ford"};

        Matcher target = new Matcher();
        IList<Tweet> actual = target.Match(twitterStream, matches, exclude);

        Assert.AreEqual(3, actual.Count);            
    }

, но теперь этот тест не пройден:

        [TestMethod]
    public void Should_match_items_either_mazda_or_car_but_no_duplicates()
    {
        IEnumerable<Tweet> twitterStream = new List<Tweet>
                                               {
                                                   new Tweet("I have a Mazda car"),
                                                   new Tweet("I have a ford"),
                                                   new Tweet("Mazda Rules"),
                                                   new Tweet("My Ford car is great"),
                                                   new Tweet("My renault is brill"),
                                                   new Tweet("Mazda cars are great")
                                               };
        IList<string> matches = new List<string>{"mazda","car"};
        IList<string> exclude = new List<string>();

        Matcher target = new Matcher();
        IList<Tweet> actual = target.Match(twitterStream, matches, exclude);

        Assert.AreEqual(4, actual.Count);
    }

Я знаю, что яЯ упускаю что-то действительно простое, но после того, как час смотрю на код, оно не приходит ко мне.

Ответы [ 2 ]

5 голосов
/ 24 февраля 2011

Ну, я знаю, почему это терпит неудачу: вот это предложение:

from e in exclude

Это будет пустая коллекция, поэтому нет записей, которые могли бы даже попасть в предложение where.

Вот альтернативный подход:

var final = from f in tweets
            let lower = f.Text.ToLowerInvariant()
            where !exclude.Any(e => lower.Contains(e.ToLowerInvariant())
            select f;

Несмотря на то, что я рассмотрел и подход msarchet, хорошо то, что этот метод в конечном итоге оценивает tweetStream только один раз - даже если он читает из сети или делаетчто-то еще больно, вам не нужно беспокоиться.Там, где это возможно (и удобно), я стараюсь избегать оценки потоков LINQ более одного раза.

Конечно, вы можете сделать весь запрос одним делом очень просто:

var tweets = from f in tweetStream
             let lower = f.Text.ToLowerInvariant()
             where match.Any(m => lower.Contains(m.ToLowerInvariant())
             where !exclude.Any(e => lower.Contains(e.ToLowerInvariant())
             select f;

Я бы подумалчто еще чище, если честно :) 1015 *

1 голос
/ 24 февраля 2011

Итак, что происходит, это:

var final = from f in tweets
            from e in exclude
            where !f.Text.ToLowerInvariant().Contains(e.ToLowerInvariant())
            select f;

Так как второе из пустое, Если я прав, остальная часть утверждения не оценивается, поэтому ваш выбор никогда не происходит.

Вместо этого попробуйте сделать это следующим образом

var excludeTheseTweet = from f in tweets
                        from e in exclude
                        where f.Text.ToLowerInvariant().Contains(e.ToLowerInvariant())
                        select f;

return tweets.Except(excludeTheseTweets).Distinct().ToList<Tweet>();

Таким образом, вы получите список твитов, которые нужно исключить (поэтому, если нет ничего, что нужно исключить, он ничего не получит), а затем удалит эти элементы.сформировать оригинальный список.

...