C# Linq查询连接重叠范围并构造新的连续范围

C# Linq查询连接重叠范围并构造新的连续范围,c#,linq,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, idE

大家好,我一直在尝试构造一个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);
虽然在这个例子中,我只使用了两个C#&Java范围。查询需要灵活处理
N个课程数和N个范围数

实现此目的的代码:

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; 

如何进一步递归?

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

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);
                    }
                }
            }
        }
    }
公共班级学生
{
公立学生(课程c,整数开始,整数结束)
{
MyCourse=c;
idStart=开始;
idEnd=结束;
}
//公共字符串课程{get;set;}
public int idStart{get;set;}
public int idEnd{get;set;}
公共课程MyCourse{get;set;}
}
公共枚举课程{CSharp,Java}
公开课考试
{
私人名单学生;
私有列表结果;
公共测试()
{
学生=新名单
{
新生(Course.CSharp,1,25),
新生(Course.Java,50,60),
新生(Course.CSharp,20,36),
新生(Course.Java,40,55),
新生(Course.CSharp,70,80),
};
结果=新列表();
}
公开募捐
{
students=students.OrderBy(s=>s.idEnd).ThenBy(s=>s.MyCourse.ToList();
foreach(学生中的var s)
{
var lastOne=result.LastOrDefault(r=>r.MyCourse==s.MyCourse);
if(lastOne==null)
{
结果。添加(s);
}
其他的
{
var last=result.last();
如果(lastOne.MyCourse==last.MyCourse)
{
lastOne.idEnd=Math.Max(s.idEnd,lastOne.idEnd);
lastOne.idStart=Math.Min(s.idStart,lastOne.idStart);
}
其他的
{
结果。添加(s);
}
}
}
}
}

我对日期做了类似的处理,确保它们不会重叠。我使用矩形和矩形来确定日期是否重叠。我想你可以做一些类似的事情来得到你的清单。对于相交的记录,您可以将它们合并在一起。

按课程将记录分组,为每个组建立重叠记录,并将所有新记录合并到一个列表中,其中包括:

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 };
}
私有静态IEnumerable GetRangesForGroup(iGroup组){
var studentEnumerator=group.OrderBy(s=>s.idStart).GetEnumerator();
//移动到第一个记录并初始化范围变量
studentEnumerator.MoveNext();
var idStart=studentemulator.Current.idStart;
var idEnd=studentemulator.Current.idEnd;
//迭代剩余记录
while(studentEnumerator.MoveNext()){
if(studentEnumerator.Current.idStart-idEnd){
idEnd=studentemulator.Current.idEnd;
}
}否则{
//当前范围不重叠
//输出上一范围
yield return new Student(){course=group.Key,idStart=idStart,idEnd=idEnd};
//为下一个范围重新初始化变量
idStart=studentEnumerator.Current.idStart;
idEnd=studentemulator.Current.idEnd;
}
}
//输出最终范围
yield return new Student(){course=group.Key,idStart=idStart,idEnd=idEnd};
}

您是否简单地尝试合并两个列表?我想让重叠的ID合并为列表中的一个。我想您的答案是
list1.Union(list2).Distinct(aCustomComparer).ToList()
查看此处了解更多信息尝试时,您遇到了什么问题?我认为你不应该在林克这样做。Foreach在这类问题上做得更好。你的问题难以理解。您应该将其重新格式化以使其更加清晰。当添加第三个重叠范围
{CSharp,20,81}
--将导致单个CSharp范围从1到81时,这似乎不起作用。
var result =
    studentranges.GroupBy(s => s.course)
    .SelectMany(GetRangesForGroup)
    .ToList();
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 };
}