使用Linq的GroupBy函数,但是当不存在时用零填充数据?

时间:2016-12-28 22:43:28

标签: c# linq

我正在使用linq来整形从数据库返回的对象,以便更好地转换为JSON数据以与第三方控件(例如highcharts)一起使用。在对数据进行整形时,我需要确保每个数据点都存在值,即使它们为零。

例如,在查询数据库时,我可能会返回以下对象的列表:

public class DatabaseResponse
{
    public string TypeOfService {get;set;}
    public string FacilityName {get;set;}
    public int ServiceCount {get;set;}
}

现在数据库可能会返回任意数量的这些对象,有时它们的不同TypeOfService字符串匹配。其他时候他们没有。当我对数据进行整形时,我需要返回几个不同的数据。即,各个ServiceCount的类别(FacilityNames数组)及其TypeOfService

最初我做了类似以下的事情来获取类别:

var result = GetTypeOfServiceCountsByFacility(startDate, endDate, companyDivisionIds, facilityIds, catcCompanyId);

我正在做这样的事情以获得服务计数:

var data = result.OrderBy(n => n.FacilityName).GroupBy(n => n.TypeOfService).Select(n => new {
    name = n.Key,
    data = n.Select(x => x.ServiceCount).ToArray()
});

如果每个Facility都有匹配的服务,则上述工作非常有效,但如果它们关闭,则阵列关闭。是否有一种简单的方法来填充数据以确保所有内容都被填充(即使它只是零)?

修改

示例输入:

var result = new List<ServiceTypeFacilityCounts>
{
    { new ServiceTypeFacilityCounts {FacilityName = "Facility 2", TypeOfService="Phone", ServiceCount=50 } },
    { new ServiceTypeFacilityCounts {FacilityName = "Facility 2", TypeOfService="Visit", ServiceCount=10 } },
    { new ServiceTypeFacilityCounts {FacilityName = "Facility 2", TypeOfService="Call-In", ServiceCount=3 } },
    { new ServiceTypeFacilityCounts {FacilityName = "Facility 1", TypeOfService="Phone", ServiceCount=5 } }
};

使用上面的代码我的输出将是:

categories = ["Facility 1", Facility 2"]
data = { 
            [{name: "Phone", data: [5,50]}],
            [{name: "Visit", data: [10]}],
            [{name: "Call-In", data: [3]}]
       }

但实际上它应该是:

categories = ["Facility 1", Facility 2"]
data = { 
            [{name: "Phone", data: [5,50]}],
            [{name: "Visit", data: [0,10]}],
            [{name: "Call-In", data: [0,3]}]
       }

2 个答案:

答案 0 :(得分:1)

您基本上对内部数据项执行左连接。把它写成:

var facilities = results.Select(x => x.FacilityName).Distinct().OrderBy(x => x).ToList();
var query =
    from x in results
    group x by x.TypeOfService into g
    select new
    {
        Name = g.Key,
        Data =
            (from c in facilities
            join x in g on c equals x.FacilityName into xs
            from x in xs.DefaultIfEmpty()
            select x?.ServiceCount ?? 0).ToArray()
    };

以下是使用方法语法的上述查询的等效版本。

var facilities = results.Select(x => x.FacilityName).Distinct().OrderBy(x => x).ToList();
var query = results.GroupBy(x => x.TypeOfService)
    .Select(g => new
    {
        Name = g.Key,
        Data = facilities.GroupJoin(g, c => c, x => x.FacilityName, (c, xs) => xs)
            .SelectMany(xs =>
                xs.DefaultIfEmpty().Select(x => x?.ServiceCount ?? 0)
            ).ToArray(),
    });

答案 1 :(得分:0)

我提出的一个可能的解决方案是构建一个临时列表,强制迭代类别列表和分组服务类型。然后我尝试从主结果集中选择以查看是否存在值。如果有,我将其添加到新列表中。如果它没有给新列表添加零。那个版本看起来像:

var categories = result.OrderBy(n => n.FacilityName).Select(n => n.FacilityName).Distinct().ToArray();
var paddedData = new List<Tuple<string, int>>();

result.OrderBy(n => n.FacilityName).GroupBy(n => n.TypeOfService).ToList().ForEach(n =>
{
    foreach (var category in categories)
    {
        var newItem = new Tuple<string,int>
        (
            n.Key,
            result.FirstOrDefault(item => item.FacilityName == category && item.TypeOfService == n.Key)?.ServiceCount ?? 0
        );
        paddedData.Add(newItem);
    }
});

var data = paddedData.GroupBy(n => n.Item1).Select(n => new
{
    name = n.Key,
    data = n.Select(x => x.Item2).ToArray()
});
var shapedData = new
{
    series = data,
    categories
};