C#: 合并相邻范围

4
作为合并重叠范围方法的后续,我想尝试创建一个将相邻范围组合的方法。
基本上,在运行折叠方法后,您可能会得到例如1到5和6到10。我想将它们组合成一个范围,即1到10。
到目前为止,这是我想出的方法,但它并不真正有效。有人发现我的问题或有更好的替代方案吗?
    public static IEnumerable<Range<T>> MergeAdjacent<T>(this IEnumerable<Range<T>> source, Func<T, T, bool> isAdjacent)
    {
        using (var sourceIterator = source.GetEnumerator())
        {
            if (!sourceIterator.MoveNext())
                yield break;

            var first = sourceIterator.Current;

            while (sourceIterator.MoveNext())
            {
                var second = sourceIterator.Current;

                if (isAdjacent(first.End, second.Start))
                {
                    yield return Range.Create(first.Start, second.End);
                }
                else
                    yield return first;

                first = second;
            }

            yield return first;
        }
    }

@Svish,你检查过我的解决方案了吗?这不是你想要的吗? - bruno conde
还没有测试任何答案。星期一回去上班时再测试。=) - Svish
2个回答

2
我已经得出了这个解决方案。一个前提是根据Func函数,范围被升序或降序排序。它将合并相邻的范围,仍然是延迟执行。我没有进行太多的测试,所以可能会有一些边缘情况会破坏它。请温柔点:-)
编辑:代码稍微简化了一下。就我所看到的,它可以工作。省略了null检查。
    public static IEnumerable<Range<T>> MergeAdjacent<T>(this IEnumerable<Range<T>> source, Func<T, T, bool> isAdjacent)
    {
        using (var it = source.GetEnumerator())
        {
            if (!it.MoveNext())
                yield break;

            var item = it.Current;

            while (it.MoveNext())
                if (isAdjacent(item.End, it.Current.Start))
                {
                    item = Range.Create(item.Start, it.Current.End);
                }
                else
                {
                    yield return item;
                    item = it.Current;
                }

            yield return item;
        }
    }

    static void Main(string[] args)
    {
        var ranges = new List<Range<int>>
        {
            Range.Create(1,3), Range.Create(4,5), Range.Create(7,10), 
            Range.Create(11,17), Range.Create(20,32), Range.Create(33,80), 
            Range.Create(90,100), 
        };

        foreach (var range in ranges.MergeAdjacent((r1, r2) => r1 + 1 == r2))
            Console.WriteLine(range);
    }

    // Result: 1-5, 7-20, 25-80, 90-100

最后的 else yield break; 真的必要吗? - Svish
最终使用了这个,但稍微缩短了一下。 - Svish

1

它只会合并相邻的两个范围,而不是三个或更多。保留最后一个范围,直到找到间隙或列表末尾。

public static IEnumerable<Range<T>> MergeAdjacent<T>(this IEnumerable<Range<T>> source, Func<T, T, bool> isAdjacent)
{
    Range<T> current = null;

    foreach (Range<T> item in source)
    {
        if (current == null)
        {
            current = item;
        }
        else
        {
            if (isAdjacent(current.End, item.Start))
            {
                current = Range.Create(current.Start, item.End);
            }
            else 
            {
                yield return current;
                current = item;
            }
        }
    }

    if (current != null) 
        yield return current;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接