Отличие LINQ с соответствующими счетчиками на основе свойства List

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

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

С фотографией может быть связано несколько тегов, и один и тот же тег можно использовать в любом количестве разных фотографий.

Что я в конечном итоге хочу сделать, так это получить список отдельных тегов с подсчетом количества фотографий, назначенных каждому тегу.

У меня нет контроля над базовыми данными — я использую API с методом поиска, который я использую для получения коллекции фотографий, но нет способа получить список отдельных тегов.

Итак, мой вопрос: если у меня есть большая коллекция фотографий, как я могу использовать LINQ, чтобы получить отдельный список тегов, содержащихся в этом списке, и получить количество (фотографий) для каждого из этих тегов?


person marcusstarnes    schedule 30.04.2015    source источник
comment
Не уверен, что вы просите... Что вы пробовали?   -  person Maciej Los    schedule 30.04.2015
comment
Хм, попробуй List<Photos>.Where(y = List<Photos>.Select(x = x.Tag).Distinct().Contains(y.Tag))).Count()   -  person Pseudonym    schedule 30.04.2015


Ответы (4)


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

Аргументация следующая.

  • Получите последовательность всех тегов с повторением, если несколько фотографий имеют один и тот же тег.
  • Сгруппируйте повторяющиеся теги по их значению и подсчитайте количество тегов в каждой группе.
  • Создайте словарное сопоставление каждого тега с количеством фотографий с этим тегом.
person Timothy Shields    schedule 30.04.2015

Вы можете использовать SelectMany и GroupBy следующим образом: -

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))
                         });

Это даст вам все теги и соответствующие им PhotoIds в формате, разделенном запятыми.

Обновление:

Извините, только что заметил, что вам нужно количество объектов Photos, в этом случае вам просто нужно это: -

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

Итак, предположим, у вас есть эти данные: -

 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" }}
 };

Ниже вы увидите вывод: -

введите здесь описание изображения

person Rahul Singh    schedule 30.04.2015

Вы можете сделать это в два шага.

  • Сначала получите отдельные теги из списка
  • Переберите отдельные теги, получите количество из списка и поместите его в Dictionary<string,int>

Что-то типа:

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)));
}
person Habib    schedule 30.04.2015
comment
Не показывает, как получить счет, как заданный вопрос - person Pseudonym; 30.04.2015
comment
количество для каждого тега? это должно сделать это dictionary.Add(tag, list.Count(r=> r.Tags.Contains(tag))); - person Habib; 30.04.2015

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()});

Результат:

Tag Count
a 1 
b 2 
c 3 
d 2 
e 1 
person Robert McKee    schedule 30.04.2015