C# 如何根据类别按以前的非空值填充空值

C# 如何根据类别按以前的非空值填充空值,c#,linq,C#,Linq,我有下面这样的数据集,需要用以前的非空“价格”值推断所有空“价格”值。这仅仅是因为我需要考虑的几个类别——CAT1、CAT2、DateG和Timg——我有这个“价格”。 有没有简单的方法可以在有/没有linq的情况下实现这一点。列出结果= List<DataLoad> result = DataLoad.GetSomeData() .OrderByDescending(d => d.DateG) .ThenByDescen

我有下面这样的数据集,需要用以前的非空“价格”值推断所有空“价格”值。这仅仅是因为我需要考虑的几个类别——CAT1、CAT2、DateG和Timg——我有这个“价格”。

有没有简单的方法可以在有/没有linq的情况下实现这一点。

列出结果=
List<DataLoad> result =
    DataLoad.GetSomeData()
            .OrderByDescending(d => d.DateG)
            .ThenByDescending(t => t.TimeG)
            .ThenByDescending(c1 => c1.Cat1)
            .ThenByDescending(c2 => c2.Cat2)
            .ToList();
DataLoad.GetSomeData() .OrderByDescending(d=>d.DateG) .然后按降序(t=>t.TimeG) .然后下降(c1=>c1.Cat1) .然后再下降(c2=>c2.Cat2) .ToList();

您想要的输出示例令人困惑,但如果我预料到了您想要的,那么这个查询应该会有所帮助。

我在这里看到了几个选项

  • 您可以使用其中的一些方法(例如
    Lag/Lead
    FillForward/FillBackward
  • 您可以编写自己的非常简单的扩展方法来完成填充
使用Morelink: 使用MoreLinq有很多方法可以做到这一点,但我将只展示一个使用
Lag
扩展的示例

var result = GetSomeData() 
    // Do ordering if you want
    .OrderByDescending(d => d.DateG)
    .ThenByDescending(t => t.TimeG)
    .ThenByDescending(c1 => c1.Cat1)
    .ThenByDescending(c2 => c2.Cat2)
    // Add the filling logic with .Lag()
    .Lag(1, (current, previous) =>
    {
        if(previous != null) current.name = current.name ?? previous.name;
        return current;
    }).ToList();
这样做的一个缺点是它不能提供您可能需要的“回填”。如果列表开头有
null
价格,则这些价格将保持为null且不会填充。您可以通过手动处理这些案例或在相反的列表中运行它(可能不推荐)来解决这个问题。另一件需要注意的事情是,这将编辑列表中的实际对象,而不是创建新的对象,这是我在处理LINQ时通常希望避免的。您可以编辑选择器以更改该行为

自定义扩展方法: 我想到了一个:

公共静态IEnumerable填充(此IEnumerable源、Func谓词、Func结果选择器)
{
var回填=假;
var-previous=默认值(TSource);
var回填=新列表();
foreach(源中的var elm)
{
if(谓词(elm))
{
如果(!回填)
{
回填。添加(elm);
}
其他的
{
收益率返回结果选择器(上一个,elm);
}
}
否则,如果(!已回填)
{
//我们已经找到了第一个可以使用
对于(int i=0;i
用法 这里的第一个参数是我要填充的条件。在您的情况下,如果数据
DataLoad.Price
为空,则为空。比如:

data => data.Price == null
如果该条件的计算结果为true,则它将使用当前值和上一个值调用
处理程序
函数。你的应该是这样的:

(prev, curr) => 
{ 
    curr.Price = prev.Price;
    return curr;
}
综上所述,你会得到:

var result = GetSomeData()
    // Do ordering/filtering/grouping here
    .Fill(
        data => data.Price == null,
        (prev, curr) => 
        { 
            curr.Price = prev.Price;
            return curr;
        })
    .ToList();
这里有一个链接到一个你可以玩的游戏

这样做的好处是,您可以更好地控制填充时发生的事情,同时仍然使函数具有良好的通用性。您可以将其应用于任何IEnumerable,并使其仍然有效。这也完成了MoreLinq查询没有立即完成的“回填”


注意:这仍然可以在位编辑列表中的现有对象,但另一个选择器可以解决这一问题。

Linq将为您提供最佳性能方法

DataLoad.GetSomeData()
    .Where(x => x.Price == null)
    .ToList()
    .ForEach(x =>
    {
        x.Price = list.First(v => v.Cat1 == x.Cat1 &&
                                  v.Cat2 == x.Cat2 &&
                                  v.Price != null)
                      .Price;
    });
如果您的数据已经订购,则可以在价格为空时基于上一个值执行索引选择。另一个示例是使用具有递归和排序方式的局部函数:

var orderedList = GetSomeData()
    .OrderBy(x => x.Cat1)
    .ThenBy(x => x.Cat2)
    .ThenBy(x => x.Price);

var result = orderedList.Select((e, i) =>
{
    e.Price = e.Price ?? GetPrice(i);
    return e;
});

double GetPrice(int index)
{
    return orderedList.ElementAt(++index).Price
           ?? GetPrice(index);
}

使用相同的方法逻辑,您可以为循环编写相同的代码。

您用DateG给出的第一个升序,因此B-B1将是记录中的第一个项目。是的,这只是想象一下价格是如何由以前的非空值填充的。在您的示例中,我知道你是如何用之前的值填充价格的,但我不明白第一次输入时是如何工作的。它以前的值不是一无所有吗?或者,在这种情况下,它是否需要检查下一个值?(假设您阅读的是top is first,bottom is last)如果所有的价格都是
null
,该怎么办?OP想要设置
Price
属性。您的代码将只对RecordsOrry进行排序,我忘记了删除排序部分,这并不是解决方案。您给了我完成此练习的好主意。非常感谢你!很好的解决方案。
DataLoad.GetSomeData()
    .Where(x => x.Price == null)
    .ToList()
    .ForEach(x =>
    {
        x.Price = list.First(v => v.Cat1 == x.Cat1 &&
                                  v.Cat2 == x.Cat2 &&
                                  v.Price != null)
                      .Price;
    });
var orderedList = GetSomeData()
    .OrderBy(x => x.Cat1)
    .ThenBy(x => x.Cat2)
    .ThenBy(x => x.Price);

var result = orderedList.Select((e, i) =>
{
    e.Price = e.Price ?? GetPrice(i);
    return e;
});

double GetPrice(int index)
{
    return orderedList.ElementAt(++index).Price
           ?? GetPrice(index);
}