如何在列表中特定列的两行之间进行计算
how to make calculation between two rows in a specific column in a list
我有这个代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
namespace CsvDemo
{
class Program
{
static void Main(string[] args)
{
List<DailyValues> values = File.ReadAllLines("C:\Users\Josh\Sample.csv")
.Skip(1)
.Select(v => DailyValues.FromCsv(v))
.ToList();
}
}
class DailyValues
{
DateTime Date;
decimal Open;
decimal High;
decimal Low;
decimal Close;
decimal Volume;
decimal AdjClose;
public static DailyValues FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
DailyValues dailyValues = new DailyValues();
dailyValues.Date = Convert.ToDateTime(values[0]);
dailyValues.Open = Convert.ToDecimal(values[1]);
dailyValues.High = Convert.ToDecimal(values[2]);
dailyValues.Low = Convert.ToDecimal(values[3]);
dailyValues.Close = Convert.ToDecimal(values[4]);
dailyValues.Volume = Convert.ToDecimal(values[5]);
dailyValues.AdjClose = Convert.ToDecimal(values[6]);
return dailyValues;
}
}
}
File.ReadAllLines
将 CSV 文件中的所有行读入字符串数组。
.Skip(1)
跳过 header 行。
.Select(v => DailyValues.FromCsv(v))
使用 Linq 来 select 每一行并使用 FromCsv
方法创建一个新的 DailyValues
实例。这将创建一个 System.Collections.Generic.IEnumerable<CsvDemo.DailyValues>
类型。
- 最后,
.ToList()
将 IEnumerable 转换为列表以匹配您想要的类型。
但我的问题是如何在两个单元格之间进行计算。例如,我想在第 3 列中添加第 2 行和第 3 行,结果显示在最后一列中。
如果我对你的问题理解正确,你想将当前行和下一行的值与当前行最后一列的值相加。
如果正确,您可以在从文件中读取所有数据后使用 for 循环执行此操作。只需在您的 class 中添加一个 属性 ,您将填充该 for 循环。
看起来(虽然,我在这里依赖于一些读心术)您试图在列表的每条记录上都有一个 运行 余额字段。您已经拥有将原始 csv 数据投影到对象列表中的代码:
List<DailyValues> values = File.ReadAllLines("C:\Users\Josh\Sample.csv")
.Skip(1)
.Select(v => DailyValues.FromCsv(v))
.ToList()
你需要做的是将它再次投影到另一个列表中,边做边计算。显然这个计算不能对第一行做任何事情,因为它需要一个以前的值,所以在那种情况下你将它设置为零。
class DailyValuesWithTotal : DailyValues
{
decimal TotalHigh
}
var projectedValues = values.Select( (v,i) => new DailyValuesWithTotal(){
Date = v.Date,
Open = v.Open,
High = v.High,
Low = v.Low,
Volume = v.Volume,
AdjClose = v.AdjClose,
TotalHigh = (i == 0) ? 0.0 : values[i-1].TotalHigh + v.High
})
上面的示例在名为 TotalHigh
的新字段中保留 运行 总计 High
字段。
首先,我建议使用 FileHelpers 或其他 CSV 解析库,因为它会使您的代码更容易(尤其是错误处理、处理引号等)。
(我会在我的回答中使用这个库,但你不必)
向您的 class 添加一个新的 属性 以存储计算值:
[IgnoreFirst(1)]
[DelimitedRecord(",")]
class DailyValues
{
[FieldConverter(ConverterKind.Date, "yyyy-MM-dd")]
public DateTime Date;
public decimal Open;
public decimal High;
public decimal Low;
public decimal Close;
public decimal Volume;
public decimal AdjClose;
[FieldNotInFile]
public decimal SomethingCalculated;
}
然后循环遍历您的元素并使用一个临时变量来跟踪您感兴趣的前一个元素的值,以便您可以进行计算:
var engine = new FileHelperEngine(typeof(DailyValues));
var res = engine.ReadFile(@"your:\path") as DailyValues[];
decimal? prev = null;
foreach(var value in res)
{
if(prev.HasValue)
// or whatever columns you want to use
value.SomethingCalculated = value.High + prev.Value;
prev = value.Open;
}
我有这个代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
namespace CsvDemo
{
class Program
{
static void Main(string[] args)
{
List<DailyValues> values = File.ReadAllLines("C:\Users\Josh\Sample.csv")
.Skip(1)
.Select(v => DailyValues.FromCsv(v))
.ToList();
}
}
class DailyValues
{
DateTime Date;
decimal Open;
decimal High;
decimal Low;
decimal Close;
decimal Volume;
decimal AdjClose;
public static DailyValues FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
DailyValues dailyValues = new DailyValues();
dailyValues.Date = Convert.ToDateTime(values[0]);
dailyValues.Open = Convert.ToDecimal(values[1]);
dailyValues.High = Convert.ToDecimal(values[2]);
dailyValues.Low = Convert.ToDecimal(values[3]);
dailyValues.Close = Convert.ToDecimal(values[4]);
dailyValues.Volume = Convert.ToDecimal(values[5]);
dailyValues.AdjClose = Convert.ToDecimal(values[6]);
return dailyValues;
}
}
}
File.ReadAllLines
将 CSV 文件中的所有行读入字符串数组。.Skip(1)
跳过 header 行。.Select(v => DailyValues.FromCsv(v))
使用 Linq 来 select 每一行并使用FromCsv
方法创建一个新的DailyValues
实例。这将创建一个System.Collections.Generic.IEnumerable<CsvDemo.DailyValues>
类型。- 最后,
.ToList()
将 IEnumerable 转换为列表以匹配您想要的类型。
但我的问题是如何在两个单元格之间进行计算。例如,我想在第 3 列中添加第 2 行和第 3 行,结果显示在最后一列中。
如果我对你的问题理解正确,你想将当前行和下一行的值与当前行最后一列的值相加。
如果正确,您可以在从文件中读取所有数据后使用 for 循环执行此操作。只需在您的 class 中添加一个 属性 ,您将填充该 for 循环。
看起来(虽然,我在这里依赖于一些读心术)您试图在列表的每条记录上都有一个 运行 余额字段。您已经拥有将原始 csv 数据投影到对象列表中的代码:
List<DailyValues> values = File.ReadAllLines("C:\Users\Josh\Sample.csv")
.Skip(1)
.Select(v => DailyValues.FromCsv(v))
.ToList()
你需要做的是将它再次投影到另一个列表中,边做边计算。显然这个计算不能对第一行做任何事情,因为它需要一个以前的值,所以在那种情况下你将它设置为零。
class DailyValuesWithTotal : DailyValues
{
decimal TotalHigh
}
var projectedValues = values.Select( (v,i) => new DailyValuesWithTotal(){
Date = v.Date,
Open = v.Open,
High = v.High,
Low = v.Low,
Volume = v.Volume,
AdjClose = v.AdjClose,
TotalHigh = (i == 0) ? 0.0 : values[i-1].TotalHigh + v.High
})
上面的示例在名为 TotalHigh
的新字段中保留 运行 总计 High
字段。
首先,我建议使用 FileHelpers 或其他 CSV 解析库,因为它会使您的代码更容易(尤其是错误处理、处理引号等)。
(我会在我的回答中使用这个库,但你不必)
向您的 class 添加一个新的 属性 以存储计算值:
[IgnoreFirst(1)]
[DelimitedRecord(",")]
class DailyValues
{
[FieldConverter(ConverterKind.Date, "yyyy-MM-dd")]
public DateTime Date;
public decimal Open;
public decimal High;
public decimal Low;
public decimal Close;
public decimal Volume;
public decimal AdjClose;
[FieldNotInFile]
public decimal SomethingCalculated;
}
然后循环遍历您的元素并使用一个临时变量来跟踪您感兴趣的前一个元素的值,以便您可以进行计算:
var engine = new FileHelperEngine(typeof(DailyValues));
var res = engine.ReadFile(@"your:\path") as DailyValues[];
decimal? prev = null;
foreach(var value in res)
{
if(prev.HasValue)
// or whatever columns you want to use
value.SomethingCalculated = value.High + prev.Value;
prev = value.Open;
}