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