I have a problem with grouping on a List of strings.
My class "Post" contains a List property called Tags:
public class Post
{
public string Title { get; set; }
public int Position { get; set; }
public List<string> Tags { get; set; }
}
Somewhere else in my code I am gathering all my posts in a List. Now, many of these posts share tags with each other. Assume:
Post_1.Tags = new List<string>(){"a", "b", "c"};
Post_2.Tags = new List<string>(){"a", "d", "e"};
Post_3.Tags = new List<string>(){"b", "c", "d"};
Post_4.Tags = new List<string>(){"c", "d", "f"};
I am trying to obtain a collection with this kind of structure:
Key: a; Values: Post_1, Post_2;
Key: b; Values: Post_1, Post_3;
Key: c; Values: Post_1, Post_3, Post_4;
Key: d; Values: Post_2, Post_3, Post_4;
Key: e; Values: Post_2;
Key: f; Values: Post_4;
I guess I need to use GroupBy(), but I'm stuck with the syntax, since:
List<Post> Posts = // all my posts
ILookup<string, Post> ByTag = Posts.GroupBy(p => p.Tags) ...
is obviously targeting the List of tags and not the single tags.
Any suggestions?
var tags = Posts.SelectMany(p => p.Tags).Distinct();
var groups = from p in Posts
from t in tags.Where(tag => p.Tags.Contains(tag))
group p by t
into gr
select gr;
Explanation SelectMany.Distinct - makes IEnumerable, which contains all tags from all posts without duplicates. First 2 from - joins tags and posts. Condition of joining - post should contain tag. Resulting collection will every possible pair of post and it's tag. Group will group those pairs in a way, that gr's key will be a tag and gr will be collection of all posts with that tag. I got used to dictionaries and wrote a code which can be later used converted to Dictionary<string, List<Post>>
by calling ToDictionary(g => g.Key, g => g.ToList())
. If you want a Lookup, you don't need group part:
var tags = Posts.SelectMany(p => p.Tags).Distinct();
var postAndTagPairs = from p in Posts
from t in tags.Where(tag => p.Tags.Contains(tag))
select new {t, p};
var lookup = postAndTagPairs.ToLookup(pat => pat.t, pat => pat.p);
You first need to get a distinct list of tags. Assuming the posts are in a list like this:
var posts = new List<Post>
{
new Post { Tags = new List<string>(){"a", "b", "c"} },
new Post { Tags = new List<string>(){"a", "d", "e"} },
new Post { Tags = new List<string>(){"b", "c", "d"} },
new Post { Tags = new List<string>(){"c", "d", "f"} }
};
You get the tags like this:
var allTags = posts.SelectMany(p => p.Tags).Distinct();
Then you can do your grouping:
var result = from p in posts
from t in allTags.Where(t => p.Tags.Contains(t))
group p by t into grp
select grp;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.