我有以下用数据填充的类
public class cntrydata
{
public string countryid { get; set; }
public string countryname { get; set; }
public IEnumerable<Data> data { get; set; }
}
public class Data
{
public int year { get; set; }
public float value { get; set; }
}
我有一个IEnumerable结果,其结果如下:
IEnumerable<cntryData> result
USA
United States
2000 12
2001 22
2004 32
CAN
Canada
2001 29
2003 22
2004 24
我想使用LINQ评估“result”对象以获得以下结果:
2000 USA 12 CAN null
2001 USA 22 CAN 29
2003 USA null CAN 22
2004 USA 32 CAN 24
如果结果有更多的国家(比如中国的1995年价值为12),那么结果应该是这样的:
1995 USA null CAN null CHN 12
2000 USA 12 CAN null CHN null
2001 USA 22 CAN 29 CHN null
2003 USA null CAN 22 CHN null
2004 USA 32 CAN 24 CHN null
可以使用LINQ完成吗?谢谢。
答案 0 :(得分:4)
<强>更新强>
现在,您可以使用以下代码制作数据表:
var newresult = result.SelectMany(cntry => cntry.data.Select(d => new { id = cntry.countryid, name = cntry.countryname, year = d.year, value = d.value }))
.GroupBy(f => f.year)
.Select(g => new { year = g.Key, placeList = g.Select(p => new { p.id, p.value })});
DataTable table = new DataTable();
table.Columns.Add("Year");
foreach(string name in result.Select(x => x.countryid).Distinct())
table.Columns.Add(name);
foreach(var item in newresult)
{
DataRow nr = table.NewRow();
nr["Year"] = item.year;
foreach(var l in item.placeList)
nr[l.id] = l.value;
table.Rows.Add(nr);
}
table.Dump();
看起来如何:
这就是linq可以做的事情,您可以将其简单地转换为数据表,按年份列出位置及其值。
展平输入然后分组。选择你想要的。喜欢这个
var newresult = result.SelectMany(cntry => cntry.data.Select(d => new { id = cntry.countryid, name = cntry.countryname, year = d.year, value = d.value }))
.GroupBy(f => f.year)
.Select(g => new { year = g.Key, placeList = g.Select(p => new { p.id, p.value })});
这是转储在LinqPad中的样子。
这是完整的测试代码
void Main()
{
List<cntrydata> result = new List<cntrydata>()
{
new cntrydata() { countryid = "USA", countryname = "United States",
data = new List<Data>() {
new Data() { year = 2000, value = 12 },
new Data() { year = 2001, value = 22 },
new Data() { year = 2004, value = 32 }
}
},
new cntrydata() { countryid = "CAN", countryname = "Canada",
data = new List<Data>() {
new Data() { year = 2001, value = 29 },
new Data() { year = 2003, value = 22 },
new Data() { year = 2004, value = 24 }
}
}
};
var newresult = result.SelectMany(cntry => cntry.data.Select(d => new { id = cntry.countryid, name = cntry.countryname, year = d.year, value = d.value }))
.GroupBy(f => f.year)
.Select(g => new { year = g.Key, placeList = g.Select(p => new { p.id, p.value })});
newresult.Dump();
}
public class cntrydata
{
public string countryid { get; set; }
public string countryname { get; set; }
public IEnumerable<Data> data { get; set; }
}
public class Data
{
public int year { get; set; }
public float value { get; set; }
}
答案 1 :(得分:4)
我发现很难在这个问题上找到一个干净的答案,我仍然不太满意,所以欢迎提出反馈意见:
var countries = result.Select(x => x.countryid).Distinct();
var years = result.SelectMany(x => x.data).Select(x => x.year).Distinct();
var data = result.SelectMany(x => x.data
.Select(y => new { Country = x.countryid,
Data = y }))
.ToDictionary(x => Tuple.Create(x.Country, x.Data.year),
x => x.Data.value);
var pivot = (from c in countries
from y in years
select new { Country = c, Year = y, Value = GetValue(c, y, data) })
.GroupBy(x => x.Year)
.OrderBy(x => x.Key);
public float? GetValue(string country, int year,
IDictionary<Tuple<string, int>, float> data)
{
float result;
if(!data.TryGetValue(Tuple.Create(country, year), out result))
return null;
return result;
}
pivot
每年将包含一个项目。每个国家/地区中的每个项目都包含一个项目。
如果要将每一行格式化为字符串,可以执行以下操作:
pivot.Select(g => string.Format("{0} {1}", g.Key, string.Join("\t", g.Select(x => string.Format("{0} {1}", x.Country, x.Value)))));
答案 2 :(得分:3)
//group things up as required
var mainLookup = result
.SelectMany(
country => country.data,
(country, data) => new {
Name = country.Name,
Year = data.Year,
Val = data.Val
}
)
.ToLookup(row => new {Name= row.Name, Year = row.Year}
List<string> names = mainLookup
.Select(g => g.Key.Name)
.Distinct()
.ToList();
List<string> years = mainLookup
.Select(g => g.Key.Year)
.Distinct()
.ToList();
// generate all possible pairs of names and years
var yearGroups = names
.SelectMany(years, (name, year) => new {
Name = name,
Year = year
})
.GroupBy(x => x.Year)
.OrderBy(g => g.Key);
IEnumerable<string> results =
(
from yearGroup in yearGroups
let year = yearGroup.Key
//establish consistent order of processing
let pairKeys = yearGroup.OrderBy(x => x.Name)
let data = string.Join("\t",
from pairKey in pairKeys
//probe original groups with each possible pair
let val = mainLookup[pairKey].FirstOrDefault()
let valString = val == null ? "null" : val.ToString()
select pairKey.Name + " " + valString
)
select year.ToString() + "\t" + data; //resultItem