I have a data set that looks like this:
[
{
"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
]
For all items with the same “Size”, I’d like to individually sum up the matching “Detail” entries, and then average them across like “Size”d items. i.e.:
[
{
"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,
}
},
]
The code I’ve got is such, but I’m stuck in figuring out how to average across the nested set. Any pointers would be appreciated.
My code, thus far.
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?
}
}
}
}
UPDATE:
Using Adam Mills code, I’ve gotten closer, combining two separate LINQ Queries. Can this be made into one LINQ query that’s hopefully more readable?
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)))
});
The following produces your desired output given your input, except it sums the two
detail1values formedium:Note it produces a
Dictionary<string, Dictionary<string, int>>.