3

我有一个如下所示的数据集:

[
{
 "Size" : "Small",
 "Details" :
  {
    "Detail 1" : 1.0,
    "Detail 1" : 1.0,
    "Detail 2" : 1.0,
  }
},
{
 "Size" : "Small",
 "Details" :
  {
    "Detail 1" : 2.0,
    "Detail 1" : 3.0,
    "Detail 2" : 4.0,
  }
},
{
 "Size" : "Medium",
 "Details" :
  {
    "Detail 1" : 1.0,
    "Detail 1" : 1.0,
    "Detail 2" : 1.0,
    "Detail 3" : 1.0,
  }
},
//... etc
]

对于具有相同“大小”的所有项目,我想单独总结匹配的“详细信息”条目,然后像“大小”d 项目一样对它们进行平均。IE:

[
{
 "Size" : "Small",
 "Details" :
 {
    "Detail 1" : 3.5,
    "Detail 2" : 2.5,     // Average of the summation of the two 'small' items in original data set
 },
    {
     "Size" : "Medium",
     "Details" :
      {
        "Detail 1" : 2.0, // Average of the two details for medium.
        "Detail 2" : 1.0,
        "Detail 3" : 1.0,
      }
    },
]

我得到的代码就是这样,但我一直在弄清楚如何在嵌套集中进行平均。任何指针将不胜感激。

我的代码,到目前为止。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test
{

    class ItemWithDetails
    {
        public string Size;
        public List<KeyValuePair<string, double>> Details { get; private set; }

        public ItemWithDetails(string size)
        {
            Size = size;
            Details.Add(new KeyValuePair<string, double>("Detail 1", 1));
            Details.Add(new KeyValuePair<string, double>("Detail 1", 1));
            Details.Add(new KeyValuePair<string, double>("Detail 2", 1));
            Details.Add(new KeyValuePair<string, double>("Detail 2", 1));
            Details.Add(new KeyValuePair<string, double>("Detail 2", 1));
            Details.Add(new KeyValuePair<string, double>("Detail 3", 1));

            if (size == "Large")
            {
                Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
                Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
                Details.Add(new KeyValuePair<string, double>("Detail 3", 1));
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var testData = new List<ItemWithDetails>()
            {
                new ItemWithDetails("Small"),
                new ItemWithDetails("Small"),
                new ItemWithDetails("Medium"),
                new ItemWithDetails("Medium"),
                new ItemWithDetails("Medium"),
                new ItemWithDetails("Large"),
                new ItemWithDetails("Large"),
                new ItemWithDetails("Large"),
            };

            // Trying to get the average of each detail, per size.

            var detailSummed = from item in testData
                               select new
                               {
                                   size = item.Size,
                                   detailsSummed = from detail in item.Details
                                                   group detail by detail.Key into detailGroup
                                                   select new
                                                   {
                                                       detailName = detailGroup.Key,
                                                       detailSum = detailGroup.Sum(a => (a.Value))
                                                   }
                               };

            var averageAcrossItems =    from item in detailSummed
                                        group item by item.size into itemGroup
                                        select new 
                                        {
                                            size = itemGroup.Key,
                                            detailsAveraged = // not sure how I can average across items, while at this level.  Do I have to flatten it?
                                        }

        }
    }
}

更新:

使用 Adam Mills 代码,我更接近了,结合了两个独立的 LINQ 查询。可以将其制成一个希望更具可读性的 LINQ 查询吗?

        var detailSummed = from item in testData
                           select new
                           {
                               size = item.Size,
                               detailsSummed = from detail in item.Details
                                               group detail by detail.Key into detailGroup
                                               select new
                                               {
                                                   detailName = detailGroup.Key,
                                                   detailSum = detailGroup.Sum(a => (a.Value))
                                               }
                           };

        var test2 = detailSummed.GroupBy(x => x.size)
                                .Select(y =>
                                           new
                                           {
                                               Size = y.Key,
                                               DetailAverages = y   .SelectMany(x => x.detailsSummed)
                                                                    .GroupBy(x => x.detailName)
                                                                    .Select(x => new KeyValuePair<string, double>(x.Key, x.Average(c => c.detailSum)))
                                           });
4

5 回答 5

2
 items.GroupBy(x => x.Size)
                .Select(y =>

                        new
                            {
                                Size = y.Key,
                                Details = y.SelectMany(x => x.Details)
                                            .GroupBy(x => x.Key)
                                            .Select(x => new
                                                             {
                                                                 Key = x.Key,
                                                                 Average = x.Average(c => c.Value),
                                                                 Sum = x.Sum(c => c.Value)
                                                             })
                            });
于 2012-11-13T15:16:06.990 回答
1

以下根据您的输入产生您想要的输出,除了它将两个detail1值相加medium

var output = input
    .Select(iwd => new { Size = iwd.Size, Sums = iwd.Details.GroupBy(kvp => kvp.Key).Select(g => new { Detail = g.Key, Sum = g.Sum(kvp => kvp.Value) }) })
    .GroupBy(ds => ds.Size)
    .ToDictionary(g => g.Key, g => g.SelectMany(ds => ds.Sums).GroupBy(ds => ds.Detail).ToDictionary(dsg => dsg.Key, dsg => dsg.Sum(ds => ds.Sum) / g.Count()));

请注意,它会产生一个Dictionary<string, Dictionary<string, int>>.

于 2012-11-13T15:38:34.250 回答
1

这个怎么样,

首先一些设置:

class ItemWithDetails {

    public string Size;
    public List<KeyValuePair<string, double>> Details { get; private set; }

    public ItemWithDetails() {
      Details=new List<KeyValuePair<string,double>>();
   }
}

以及要初始化的样本数据;

var testData = new ItemWithDetails[] {
    new ItemWithDetails { Size = "Small", 
        Details = { 
            new KeyValuePair<string,double>("Detail 1",1.0),
            new KeyValuePair<string,double>("Detail 1",1.0),
            new KeyValuePair<string,double>("Detail 2",1.0),
        }
    },
    new ItemWithDetails { Size="Small",
        Details = { 
            new KeyValuePair<string,double>("Detail 1",2.0),
            new KeyValuePair<string,double>("Detail 1",3.0),
            new KeyValuePair<string,double>("Detail 2",4.0),
        }
    },
    new ItemWithDetails { Size="Medium",

        Details = { 
            new KeyValuePair<string,double>("Detail 1",1.0),
            new KeyValuePair<string,double>("Detail 1",1.0),
            new KeyValuePair<string,double>("Detail 2",1.0),
            new KeyValuePair<string,double>("Detail 3",1.0),
        }
    },

};

现在这个查询应该以你想要的方式转换数据;

var q = from i in testData
        select new { 
             Size=i.Size,
             Details=i.Details
                     .GroupBy(d =>d.Key)
                     .Select(d=>new KeyValuePair<string,double>(d.Key,d.Sum(a=>a.Value)))
             } into x
        group x by x.Size into g
        let details = (
                       from a in g.SelectMany (b => b.Details)
                       group a by a.Key into g2
           select new KeyValuePair<string,double>(g2.Key,g2.Average(b=>b.Value))
                      )
       select new {
             Size=g.Key,
             Details=details
       };

并且更具可读性,只需在答案中使用匿名类型而不是 kvp:

var q = from i in testData
        select new { 
             Size=i.Size,
             Details= (
                      from d in i.Details
                      group d by d.Key into g1
                      select new {Key=g1.Key,Value=g1.Sum(a=>a.Value)}
                      )

             } into x
        group x by x.Size into g
       select new {
             Size=g.Key,
             Details=(
                       from a in g.SelectMany (b => b.Details)
                       group a by a.Key into g2
           select new {Key =g2.Key, Value= g2.Average(b=>b.Value)}
                      )
       };
于 2012-11-13T15:44:55.077 回答
0

试试这个:

var x =
    from item in testData
    group item by item.Size into sizeItems
    select new
    {
        Size = sizeItems.Key,
        Details =
            from item in sizeItems
            from detail in item.Details
            group detail by detail.Key into detailNumbers
            select new KeyValuePair<string, double>(detailNumbers.Key, detailNumbers.Sum(dn => dn.Value)) into detail
            group detail by detail.Key into detailNumbers
            select new KeyValuePair<string, double>(detailNumbers.Key, detailNumbers.Average(dn => dn.Value))
    };
于 2012-11-13T15:37:15.343 回答
0
var detailSummed = testData
    .SelectMany(d => d.Details.GroupBy(de => de.Key).Select(de => new { Size = d.Size, DetailSize = de.Key, Sum = de.Sum(x => x.Value) } ))
    .GroupBy (d => d.Size)
    .Select (d => 
        new 
        { 
            Size = d.Key, 
            Details = d.GroupBy (x => x.DetailSize)
                .Select(x => 
                    new KeyValuePair<string, double>(x.Key, x.Average(xi => xi.Sum)))
                .ToList()
        });

让我们分解一下:

d.Details.GroupBy(de => de.Key).Select(de => new { Size = d.Size, DetailSize = de.Key, Sum = de.Sum(x => x.Value) } )
我们从测试数据开始,并将详细信息分组为Size, DetailSize, Sum匿名类型。这将使我们能够区分不同的ItemWithDetails.

.SelectMany(d => ...
这使我们可以从列表(详细信息)列表转到汇总值的扁平列表。

.GroupBy (d => d.Size)
这将按主要项目大小分组。此时,这会将我们汇总的数据分组到属于主要大小的所有详细信息中。我们将对这组数据进行选择。

.Select (d => 
    new 
    { 
        Size = d.Key, 
        Details = d.GroupBy (x => x.DetailSize)
            .Select(x => new KeyValuePair<string, double>(x.Key, x.Average(xi => xi.Sum)))
            .ToList()
    });

最后一种方法将我们的汇总值转换为它们各自的 DetailSize,其中的平均值位于KeyValuePairs 列表中。

于 2012-11-13T16:41:33.080 回答