marcusstarnes
marcusstarnes

Reputation: 6531

LINQ distinct with corresponding counts based on a List property

I have the following class

public class Photo
{
    public int Id { get; set; }
    public string Caption { get; set; }
    public Collection<string> Tags { get; set; }
}

A photo can have multiple Tags associated with it, and the same Tag can be used in any number of different Photos.

What I ultimately want to do is to retrieve a list of distinct tags, with a count of how many photos are assigned to each tag.

I have no control over the underlying data - I am using an API which has a Search method that I'm using to retrieve a collection of Photos, but there is no method to get a list of distinct tags.

So my question is, if I have a large collection of Photos, how can I use LINQ to get a distinct list of the tags contained within this list, and get the count (of photos) for each of those tags?

Upvotes: 8

Views: 1310

Answers (4)

Rahul Singh
Rahul Singh

Reputation: 21795

You can use SelectMany & GroupBy like this:-

var result = photos.SelectMany(x => x.Tags, (photosObj, tags) => new {photosObj, tags})
                   .GroupBy(x => x.tags)
                   .Select(x => new
                         {
                            Tags = x.Key,
                            PhotoId = String.Join(",",x.Select(z => z.photosObj.Id))
                         });

This will give you all the Tags and their respective PhotoIds in comma separated format.

Update:

Sorry, Just noticied you want the count of Photos object, in that case you simply need this:-

var result = photos.SelectMany(x => x.Tags)
                   .GroupBy(x => x)
                   .Select(x => new
                              {
                                  Tags = x.Key,
                                  PhotoCount = x.Count()
                              });

So, Suppose you have this data:-

 List<Photo> photos = new List<Photo>
 {
     new Photo { Id =1, Caption = "C1", Tags = new List<string> { "T1", "T2", "T3" }},
     new Photo { Id =2, Caption = "C2", Tags = new List<string> { "T4", "T2", "T5" }},
     new Photo { Id =3, Caption = "C3", Tags = new List<string> { "T5", "T3", "T2" }}
 };

You will below output:-

enter image description here

Upvotes: 3

Timothy Shields
Timothy Shields

Reputation: 79441

var tagCounts = photos
    .SelectMany(photo => photo.Tags)
    .GroupBy(tag => tag, (tag, group) => new { tag, count = group.Count() })
    .ToDictionary(tuple => tuple.tag, tuple => tuple.count);

The reasoning is as follows.

  • Obtain the sequence of all tags, with repetition if multiple photos have the same tag.
  • Group the repeated tags by their value, and count the number of tags in each group.
  • Construct a dictionary mapping from each tag to the number of photos that have that tag.

Upvotes: 9

Robert McKee
Robert McKee

Reputation: 21487

var list=new[]{
   new {id=1,Tags=new List<string>{"a","b","c"}},
   new {id=2,Tags=new List<string>{"b","c","d"}},
   new {id=3,Tags=new List<string>{"c","d","e"}},
};
var result=list.SelectMany(r=>r.Tags)
   .GroupBy(r=>r,r=>r,(Key,Vals)=>new {Tag=Key,Count=Vals.Count()});

Result:

Tag Count
a 1 
b 2 
c 3 
d 2 
e 1 

Upvotes: 0

Habib
Habib

Reputation: 223187

You can do that in two steps.

  • First get distinct tags from list
  • Iterate through distinct tags and get count from the list and put it in a Dictionary<string,int>

Something like:

List<Photo> list = new List<Photo>();
var distinctTags = list.SelectMany(r => r.Tags).Distinct();
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (string tag in distinctTags)
{
    dictionary.Add(tag, list.Count(r=> r.Tags.Contains(tag)));
}

Upvotes: 0

Related Questions