基于两个子列表的连接获取单个列表

时间:2014-06-25 07:52:54

标签: c# linq outer-join

我有一个包含两个List子属性的Items列表,我需要获取所有(唯一)值,这些值是这两个子属性的结果(但只在其父类中加入)。以下是一个人为的例子:

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

我有一个Calendar对象列表,我需要从整个集合中获取月/日组合的列表。我设法通过将两个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

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

2 个答案:

答案 0 :(得分: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 }},
};

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

答案 1 :(得分: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