Linq查询连接重叠范围并构建新的连续范围

时间:2014-02-20 18:04:44

标签: c# linq

你好伙伴我一直在尝试构建一个Linq查询,它在记录中找到重叠的范围,并构造一个新的单一范围,它将连接两个范围。

public class students
{
    public string course { get; set; }
    public int idStart { get; set;}
    public int idEnd { get; set;}
}

var c1 = new students(){course = "c#", idStart = 1, idEnd = 25};
var j1 = new students(){course = "java",idStart = 50, idEnd = 60};
var c2 = new students(){"c#", 20, 36};
var j2 = new students(){"java", 40, 55};
var c3 = new students(){"c#", 70, 80};

var studentranges = new list<students>;

studentranges.Add(c1);
studentranges.Add(j1);
studentranges.Add(c2);
studentranges.Add(j2);
studentranges.Add(c3);

现在我需要重新格式化List studentranges,以便产生的结果是

studentranges = list<"c#", 1, 36; "Java", 40, 60; "c#", 70, 80>

虽然在这个例子中我只使用了2个范围的C#&amp; Java的。查询需要对N number of Courses and N number of Ranges

具有灵活性

我的代码实现此目的:

var c_range = studentranges.Where(u => u.course == "c#")
                           .OrderBy(u => u.idStart)
                           .ToList();

//assuming c_least is bound to exist for simplicity
var c_least = c_range.first(); 

var c_next = c_range.Where( u=> u.idStart > c_least.idStart && u.idEnd >= c_least.idEnd)
                    .First();

//assuming c_next is not null
c_least.idEnd = c_next.idEnd; 

如何进一步复发?

3 个答案:

答案 0 :(得分:1)

这似乎适用于您的输入,请检查它是否适用于更多类型和输入。

public class Students
    {
        public Students(Course c, int start, int end)
        {
            MyCourse = c;
            idStart = start;
            idEnd = end;
        }
        //        public string course { get; set; }
        public int idStart { get; set; }
        public int idEnd { get; set; }
        public Course MyCourse { get; set; }
    }

    public enum Course { CSharp, Java }

    public class MiscTests
    {
        private List<Students> students;
        private List<Students> result;
        public MiscTests()
        {
            students = new List<Students>
            {
                new Students(Course.CSharp, 1, 25),
                new Students(Course.Java, 50, 60),
                new Students(Course.CSharp, 20, 36),
                new Students(Course.Java, 40, 55),
                new Students(Course.CSharp, 70, 80),
            };

            result = new List<Students>();
        }

        public void Run()
        {
            students = students.OrderBy(s => s.idEnd).ThenBy(s=>s.MyCourse).ToList();
            foreach (var s in students)
            {
                var lastOne = result.LastOrDefault(r=>r.MyCourse == s.MyCourse);
                if (lastOne == null)
                {
                    result.Add(s);
                }
                else
                {
                    var last = result.Last();
                    if (lastOne.MyCourse == last.MyCourse)
                    {
                        lastOne.idEnd = Math.Max(s.idEnd, lastOne.idEnd);
                        lastOne.idStart = Math.Min(s.idStart, lastOne.idStart);
                    }
                    else
                    {
                        result.Add(s);
                    }
                }
            }
        }
    }

答案 1 :(得分:1)

我做了类似日期的事情并确保它们没有重叠。我使用了矩形和Intersect method来确定日期是否重叠。我想你可以做类似的事情来获取你的清单。对于那些与你相交的人,你可以把它们结合在一起。

答案 2 :(得分:1)

使用GroupBy按顺序对记录进行分组,为每个组构建重叠记录,并将所有新记录合并到一个包含SelectMany的列表中:

var result =
    studentranges.GroupBy(s => s.course)
    .SelectMany(GetRangesForGroup)
    .ToList();

GetRangesForGroup使用您尝试的相同基本方法:按idStart排序并找到下一个idEnd。完整代码:

private static IEnumerable<Student> GetRangesForGroup(IGrouping<string, Student> group) {
    var studentEnumerator = group.OrderBy(s => s.idStart).GetEnumerator();

    // move to first record and initialize range variables
    studentEnumerator.MoveNext();
    var idStart = studentEnumerator.Current.idStart;
    var idEnd = studentEnumerator.Current.idEnd;

    // iterate remaining records
    while (studentEnumerator.MoveNext()) {
        if (studentEnumerator.Current.idStart <= idEnd) {
            // current range starts before previous end point -- it overlaps

            // use the farthest end point
            if (studentEnumerator.Current.idEnd > idEnd) {
                idEnd = studentEnumerator.Current.idEnd;
            }
        } else {
            // the current range is non-overlapping

            // output previous range
            yield return new Student() { course = group.Key, idStart = idStart, idEnd = idEnd };

            // reinitialize variables for next range
            idStart = studentEnumerator.Current.idStart;
            idEnd = studentEnumerator.Current.idEnd;
        }
    }

    // output final range
    yield return new Student() { course = group.Key, idStart = idStart, idEnd = idEnd };
}