How do I write a LINQ query that inverts the grouping of a hierarchical data source?

后端 未结 3 626
孤独总比滥情好
孤独总比滥情好 2021-02-10 22:57

How would one write a LINQ query which takes a hierarchical source data and transforms it so that the grouping is inverted?

Say I have a list of Topic objects each of wh

相关标签:
3条回答
  • 2021-02-10 23:32

    What you're looking for is a Pivot.

    Is it possible to Pivot data using LINQ?

    This source contains C# code for a Linq Pivot extension method:

    public static class LinqExtensions 
    {
    
        public static Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>> Pivot<TSource, TFirstKey, TSecondKey, TValue>(this IEnumerable<TSource> source, Func<TSource, TFirstKey> firstKeySelector, Func<TSource, TSecondKey> secondKeySelector, Func<IEnumerable<TSource>, TValue> aggregate) 
        {
            var retVal = new Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>>();
    
            var l = source.ToLookup(firstKeySelector);
            foreach (var item in l) 
            {
                var dict = new Dictionary<TSecondKey, TValue>();
                retVal.Add(item.Key, dict);
                var subdict = item.ToLookup(secondKeySelector);
                foreach (var subitem in subdict) 
                {
                    dict.Add(subitem.Key, aggregate(subitem));
                }
            }
    
            return retVal;
        }
    
    }
    
    0 讨论(0)
  • 2021-02-10 23:43
    IDictionary<Topic, IList<Tag>> data;
    var n = data.SelectMany(x => x.Value.Select(y => new { Topic = x.Key, Tag = y }))
      .GroupBy(x => x.Tag, x => x.Topic);
    
    0 讨论(0)
  • 2021-02-10 23:50

    After playing around in LinqPad a bit I think I may have found a suitable solution.

    Here is the simple example.

    var topicsByTags = 
        from topic in topics
        from tag in topic.Tags
        group topic by tag;
    

    And in order to get rid of the redundant Tags collection under each topic we can do the following.

    var topicsByTags = 
        from topic in topics
        from tag in topic.Tags
        group new 
        {
            Title = topic.Title,
            Color = topic.Posted,
        } by tag into g
        select new
        {
            g.Key.Name,
            g.Key.Color,
            Topics = g,
        };
    

    UPDATE: Below is another alternative which takes advantage of the grouping itself in the projection. Upside is slightly cleaner query, downside is that the group Key sticks around with the group even if it's not going to be used.

    var topicsByTags = 
        from topic in topics
        from tag in topic.Tags
        group new 
        {
            Title = topic.Title,
            Color = topic.Posted,
        } by tag into g
        select new
        {
            g.Key.Name,
            g.Key.Color,
            Topics = g,
        };
    

    I'll hold off accepting my own answer to allow some debate over which solution solves the problem that I posed the best.

    0 讨论(0)
提交回复
热议问题