2013-09-06 26 views
6

Tôi có lớp sau đó được phổ biến với các dữ liệutham gia nhiều IEnumerable <> sử dụng LINQ

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; } 
} 

Tôi có một kết quả IEnumerable trong đó có dữ liệu như thế này:

IEnumerable<cntryData> result 

USA 
United States 
    2000 12 
    2001 22 
    2004 32 

CAN 
Canada 
    2001 29 
    2003 22 
    2004 24 

tôi muốn đánh giá " kết quả "đối tượng sử dụng LINQ để nhận được kết quả sau:

2000 USA 12 CAN null 
2001 USA 22 CAN 29 
2003 USA null CAN 22 
2004 USA 32 CAN 24 

Ngoài ra nếu kết quả có nhiều quốc gia hơn (hãy nói Ch ina với giá trị năm 1995 12) thì kết quả sẽ giống như sau:

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 

Điều này có thể thực hiện bằng LINQ không? Cảm ơn bạn.

+0

bạn muốn xem xét groupby() – Jonesopolis

+0

Hình như bạn muốn có một [ "trái ngoài -join "] (http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx) trong LINQ. Vì vậy, liên kết tất cả các năm với tất cả các quốc gia. Bạn nhận được phạm vi theo cách này: 'int minYear = result.Min (c => c.data.Min (d => d.year)); int maxYear = result.Max (c => c.data.Max (d => d.year)); var range = Enumerable.Range (minYear, maxYear-minYear + 1); ' –

+0

@TimSchmelter Nếu bạn bỏ qua rất nhiều năm không phải là một cách tiếp cận rất tốt. Đó là chỉ khả thi nếu tất cả chúng phù hợp trong một phạm vi khá nhỏ. – Servy

Trả lời

4

tôi thấy nó đáng ngạc nhiên khó có thể đưa ra một câu trả lời sạch trên thế này, và tôi vẫn không thực sự hài lòng, vì vậy thông tin phản hồi được hoan nghênh :

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 sẽ chứa một mục mỗi năm. Mỗi mục này sẽ chứa một mục cho mỗi quốc gia.

Nếu bạn muốn định dạng mỗi dòng là một chuỗi, bạn có thể làm một cái gì đó như thế này:

pivot.Select(g => string.Format("{0} {1}", g.Key, string.Join("\t", g.Select(x => string.Format("{0} {1}", x.Country, x.Value))))); 
+0

kết quả cuối cùng có 3 thuộc tính: 'Quốc gia',' Năm', 'Giá trị' không thực sự là điều OP muốn. Những gì anh ta muốn là cái gì đó có thuộc tính 'n + 1', trong khi' n' là số lượng các quốc gia. Nó rất năng động. –

+0

@KingKing: Không đúng. Vui lòng đọc mô tả về những gì 'pivot' chứa. –

+0

@DanielHilgarth - bạn có nghĩ "mẹo" của tôi bằng cách sử dụng 'DataTable' là" sạch hơn "không? Nó không yêu cầu chức năng trợ giúp. – Hogan

4

Cập nhật

Dưới đây là bây giờ bạn sử dụng mã dưới đây để tạo ra một bảng dữ liệu:

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(); 

Và làm thế nào mà trông:

table


này là những gì linq có thể làm, bạn có thể biến điều này thành một bảng dữ liệu đủ dễ dàng, một danh sách theo năm vị trí và giá trị của chúng.

Làm phẳng đầu vào và sau đó nhóm theo. Chọn những gì bạn muốn. Như thế này

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 })}); 

Đây là kết xuất trông giống như trong LinqPad.

dump

Đây là mã kiểm tra đầy đủ

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; } 
} 
+0

Điều này trông giống như câu trả lời hiện có và có cùng một vấn đề. – Servy

+0

@Servy - mã ví dụ để thêm bảng: D – Hogan

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 
Các vấn đề liên quan