6

我有以下填充了数据的类

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 评估“结果”对象以获得以下结果:

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 完成吗?谢谢你。

4

3 回答 3

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; }
}
于 2013-09-06T14:02:18.797 回答
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)))));
于 2013-09-06T14:07:44.460 回答
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
于 2013-09-06T14:25:56.483 回答