Terrence
Terrence

Reputation: 2804

Linq Filter Posts by list of Tags

public class LogItemTag
{
    public int ID { get; set; }
    public string Tagname { get; set; }
}

var logItemTags = new List<LogItemTag>();
logItemTags.Add(new LogItemTag { ID = 1, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 1, TagName = "green" });
logItemTags.Add(new LogItemTag { ID = 3, TagName = "blue" });
logItemTags.Add(new LogItemTag { ID = 3, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 4, TagName = "green" });
logItemTags.Add(new LogItemTag { ID = 6, TagName = "white" });
logItemTags.Add(new LogItemTag { ID = 7, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 7, TagName = "green" });

var listOfTagsToFilterOn = new List<string> {"red", "green" };

//I need a list of ids 1 and 7 only.

//this query produces 6 ids - 1,1,3,4,7,7
var query = logItemTags.Where(lit => listOfTagsToFilterOn.Contains(lit.TagName)).Select(lit => lit.ID).ToList();

Upvotes: 2

Views: 391

Answers (3)

Hamlet Hakobyan
Hamlet Hakobyan

Reputation: 33381

Use Enumerable.Contains extension method

var filterList = new [] {"red", "green"};
var list = new List<LogItemTag>
       {
             new LogItemTag {ID =1; TagName = "green"},
             ....
       };

var filteredIds = list.Where(i => filterList.Contains(i.TagName)
                  .Select(f => f.ID).ToList();

UPDATE

List<LogItemTag> logItemTags = new List<LogItemTag>();
logItemTags.Add(new LogItemTag { ID = 1, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 1, TagName = "green" });
logItemTags.Add(new LogItemTag { ID = 3, TagName = "blue" });
logItemTags.Add(new LogItemTag { ID = 3, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 4, TagName = "green" });
logItemTags.Add(new LogItemTag { ID = 6, TagName = "white" });
logItemTags.Add(new LogItemTag { ID = 7, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 7, TagName = "green" });

var listOfTagsToFilterOn = new List<string> {"red", "green" };

var filteredList = logItemTags.Where(l => listOfTagsToFilterOn.Contains(l.TagName))
    .GroupBy(l => l.ID,
             l => l.TagName,
             (k, t) => new {Id = k, Count = t.Distinct().Count()})
    .Where(d => d.Count == listOfTagsToFilterOn.Count())
    .Select(d => d.Id).ToList();

Here some explanation

First we group the data by ID and generate objects of anonymous type with ID and distinct count of tags. Then filter them to match tag counts to filter tags count. This will work either you have set the same tag to sme ID.

Upvotes: 2

ocuenca
ocuenca

Reputation: 39366

Use Contains extension method:

var tags=new List<string>(){"red", "green"};// the collection of tags you want to get the ids
var query=LogItemTagList.Where(l=>tags.Contains(l.Tagname)).Select(l=>l.ID);

Update 1

I think this is what you are trying to achieve:

var query=LogItemTagList.Where(l=>tags.Contains(l.Tagname))
                        .GroupBy(l=>l.ID)
                        .Where(g=>g.Count==tags.Count)
                        .Select(g=>g.Key)
                        .ToList();

Upvotes: 2

Beltaine
Beltaine

Reputation: 2821

That's how I would do it. Feel free to edit it / optimize it.

var logItemTags = new List<LogItemTag>();
logItemTags.Add(new LogItemTag { ID = 1, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 1, TagName = "green" });
logItemTags.Add(new LogItemTag { ID = 3, TagName = "blue" });
logItemTags.Add(new LogItemTag { ID = 3, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 4, TagName = "green" });
logItemTags.Add(new LogItemTag { ID = 6, TagName = "white" });
logItemTags.Add(new LogItemTag { ID = 7, TagName = "red" });
logItemTags.Add(new LogItemTag { ID = 7, TagName = "green" });

var listOfTagsToFilterOn = new List<string> {"red", "green" };

var list = logItemTags.Where(
    x => listOfTagsToFilterOn.All(
    y => logItemTags.Any(
    z => z.ID == x.ID && y.TagName == z.TagName)))
    .Select(x => x.ID).Distinct();

Select all ids that passes all filters (where an item has all tagnames)

Upvotes: 1

Related Questions