Linq, where entity property contains another list

I have two list:

List of strings:

var keywords = _db.KeyWords.Select(k => k.Word);

and list of objects:

var articles = _db.Articles;

What I want to achieve is to filter articles where article properties contains keywords, for example:

var model = articles.Where(a => a.Title.Contains(keywords) || a.Description.Contains(keywords));

Is possible in one query without foreach loop?

One solution is :

 List<Article> model = new List<Article>();
 foreach (var keyword in keywords)
 {
     model.AddRange(articles.Where(a => a.Title.Contains(keyword) || a.Description.Contains(keyword)));
 }

But i have 100 keywords, and it is heavy process using foreach loop.

Answers


In words, you need to take only those items which have any word from keywords. So, in LINQ it can be done with something like:

var model = articles
    .Where(a => keywords.Any(k => a.Title.Contains(k) || a.Description.Contains(k)))
    .ToList();

If title or description does not contain any word from keywords, then it will not be added in the resulted list.

Note that it will not improve time complexity compared with foreach, but it may improve readbility. In general, any algorithm iterating through all the keywords and articles, is going to take approximately Number of articles * Number of keywords * Average length of article operations, which grows pretty fast when variables grow. Keep it in mind.


You can do this using the Any extension method.

For your solution:

var model = articles
           .Where(a => !keywords.Any(k => a.Title.Contains(k) || a.Description.Contains(k)))
           .ToList();

Here is a basic example to understand the method:

public class Person
{
    public string Name { get; set; }
    public string Pet { get; set; }
}

List<string> animals = new List<string>();
animals.Add("donkey");
animals.Add("horse");

List<Person> people = new List<Person>();
people.Add(new Person()
{
    Name = "Marco",
    Pet = "horse"
});
people.Add(new Person()
{
    Name = "John",
    Pet = "donkey"
});
people.Add(new Person()
{
    Name = "Penny",
    Pet = "dog"
});

//result will only include people who have pets that are in the animal list.
var res = people.Where(p => animals.Any(a => p.Pet.Contains(a)));

foreach (var item in res)
{
    Console.WriteLine("{0}-{1}", item.Name, item.Pet);
}

Console.ReadLine();

I suspect your foreach-style example is wrong. If you have an article containing "foo bar" and keywords "foo" and "bar", the article will be included twice.

You want to loop through articles first, then through keywords. Here's this without any linq at all:

List<Article> model = new List<Article>();
foreach (var article in articles)
{
    foreach (var keyword in keywords)
    {
        if (article.Title.Contains(keyword) || article.Description.Contains(keyword))
        {
            model.Add(article);
            break;
        }
    }
}

It's pretty straightforward to translate this into a linq query:

var model = articles.Where(
                a => keywords.Any(
                     k => a.Title.Contains(k) || a.Description.Contains(k))).ToList();

Need Your Help

Cocoa NSStream TCP connection to FTP

cocoa ftp tcp tcpclient nsstream

I'm new to Cocoa, but not to programming.

Selenium ChromeDriver switch tabs

c# selenium selenium-chromedriver

When I click on a link in my test, it opens a new tab.