2014-06-25 109 views
0

我有一个包含两个列表子属性的项目列表,我需要获取所有这两个子属性被连接的结果(唯一)值(但只在其父级中加入)。下面是一个人为的例子:获取基于两个子列表连接的单个列表

public class Calendar 
{ 
    public List<int> Months { get; set; } 
    public List<int> Days { get; set; } 
    public int Id { get; set; } 

    public Calendar(int id) 
    { 
     Months = new List<int>();  
     Days = new List<int>(); 
     this.Id = id; 
    } 
} 

我有日历对象的名单,我需要从整个集合拿到月/日组合的列表。我已设法通过将两个的SelectMany选项一起做这个:

var years = new List<Calendar>(); 

var cal2001 = new Calendar(1); 
cal2001.Months = new List<int>() { 1, 2}; 
cal2001.Days = new List<int>() { 1, 6 }; 
years.Add(cal2001); 

var cal2002 = new Calendar(2); 
cal2002.Months = new List<int>() { 2, 4}; 
cal2002.Days = new List<int>() { 6, 15 }; 
years.Add(cal2002); 

var items = (from M in years.SelectMany(Y => Y.Months, (Y, result) => new { Month = result, Id = Y.Id }) 
      join D in years.SelectMany(Y => Y.Days, (Y, result) => new { Day = result, Id = Y.Id }) on M.Id equals D.Id 
      select new { Day = D.Day, Month = M.Month }).Distinct(); 

items.Dump(); //Dump items to output in Linqpad. 

这给出了所需的输出:

Day Month 
1 1 
6 1 
1 2 
6 2 
15 2 
6 4 
15 4 

所以这种方法可行,但有没有执行这个查询的更有效的方式?

回答

3

试试这个:

years.SelectMany(y=> from d in y.Days 
        from m in y.Months 
        select new{d,m}) // cross join 
    .Distinct() 
    .Dump(); 

这给了相同的输出。

此外,是您的要求真的你想实现什么?如果您在同一年有多个日历,它会给出时髦的结果。例如,尝试用你的要求:

var years = new []{ 
    new Calendar(2001){ Months = { 1 }, Days= { 2 }}, 
    new Calendar(2001){ Months = { 3 }, Days= { 4 }}, 
}; 

=>这给了(2,1),(4,1),(2,3),(4,3)......这是预期的结果? (如果是,我的请求不起作用)

+0

我知道* *它必须是更容易比我想象。这似乎是一种享受! 实际上,“年份”实际上是一个ID,所以没关系,我会更新示例代码以使其更清晰,但您的解决方案对我的需求来说工作得很好。 –

+0

@ Olivier:你为什么说最后一个例子会给出四个元素。我试了一下,它给出了预期的结果。 'SelectMany'将在年份数组的每个元素上独立操作,并且不会在乎两个或更多元素是否具有相同年份。是的,我实际上运行它来确认,并在我的枚举中得到两个结果。并不是说它看起来与OP有关,但在这种情况下更新答案是更好的。 :) – Chris

+0

@Chris yep,它只给出了我的请求(这是我所期望的)的2个结果,但最初的请求(Obsidian Phoenix给他的问题)产生4个结果! – Olivier

0

如果您实施IEqualityComparer,则可以使用Distinct来清除重复项。我已经推出了名称类DateEntry,它对应于您的匿名类型。为了能够对结果进行排序,我还实施了IComparable

public class DateEntryComparer : IEqualityComparer<DateEntry> 
{ 
    public bool Equals(DateEntry lhs, DateEntry rhs) 
    { 
     return lhs.Day == rhs.Day && lhs.Month == rhs.Month; 
    } 

    public int GetHashCode(DateEntry entry) 
    { 
     return entry.Day.GetHashCode()^entry.Month.GetHashCode(); 
    } 
} 

public class DateEntry : IComparable<DateEntry> 
{ 
    public int Day {get;set;} 
    public int Month {get;set;} 

    public int CompareTo(DateEntry entry) 
    { 
     var result = Month.CompareTo(entry.Month); 

     if(result == 0) 
     { 
      result = Day.CompareTo(entry.Day); 
     } 

     return result; 
    } 
} 

public class Calendar 
{ 
    public List<int> Months { get; set; } 
    public List<int> Days { get; set; } 
    public int Year { get; set; } 

    public Calendar(int year) 
    { 
     Months = new List<int>();  
     Days = new List<int>(); 
     Year = year; 
    } 

    public IEnumerable<DateEntry> GetDateEntries() 
    { 
     return from d in Days 
       from m in Months 
       select new DateEntry { Day = d, Month = m }; 
    } 
} 

然后,你可以这样做:

var items = years.SelectMany(y => y.GetDateEntries()).Distinct(new DateEntryComparer()).OrderBy(x => x); 
    items.Dump(); //Dump items to output in Linqpad. 

输出:

1 1 
6 1 
1 2 
6 2 
15 2 
6 4 
15 4