使用LINQ获取整数范围

7

我正在寻找一种计算给定数字集合范围的方法,例如:

如果我有H555、H567、H589、H590、H591、H592、H593、H594、H595、H596、H597

我希望得到的输出是H555、H567和H589-H597。

我已经查看了相关问题,但没有找到类似于我所需内容的答案。

谢谢


7
需要使用LINQ吗? - Lasse Espeholt
那是一组数字吗?一个字符串?还是一个数组? - Abe Miessler
我对任何解决方案都持开放态度。确切地说,这是一组账单,例如H272代表第272号房屋账单,但本质上是一组字符串。 - Jesse
1
“H”字符是什么,为什么只有H555在输出中包含它?试图理解如何为您提供正确的答案... - Reed Copsey
从输出中删除 H,我可以解决这个问题。这样可以更简单地得到一个解决方案。 - Jesse
2个回答

8
我会这样做:

首先,我会这样:

public sealed class Range
{
    public int Low { get; private set; }
    public int High { get; private set; }

    public Range(int low, int high)
    {
        this.Low = low;
        this.High = high;
    }
}

那么(完全未经测试,甚至可能无法编译,但希望您能理解):

public static IEnumerable<Range> FindRanges(IEnumerable<int> values)
{
    using (IEnumerator<int> iterator = values.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            yield break;
        }
        int low = iterator.Current;
        int high = low;
        while (iterator.MoveNext())
        {
            int next = iterator.Current;
            if (next > high + 1)
            {
                // Previous range (or possibly single value) has finished
                yield return new Range(low, high);
                low = next;
            }
            high = next;
        }
        // Yield trailing range
        yield return new Range(low, high);
    }
}

我认为使用纯LINQ实现这个并不是特别容易,说实话。
编辑:现在需要适应所有以H开头的情况,只需使用:
var numbers = strings.Select(x => int.Parse(x.Substring(1));
var ranges = FindRanges(numbers);

var rangeStrings = ranges.Select(r => r.High == r.Low 
                                   ? "H" + r.Low : "H" + r.Low + "-" + r.High);
var result = string.Join(",", rangeStrings);

本来还不错,直到他在每个项目的开头加上了“H”。 - Abe Miessler
也许可以将 H 作为字典的键,int 作为值,计算范围,然后重新组合? - Jesse
@Jesse:按照您的描述,这将创建一个字典,其中每个键都相同(即不唯一)。为什么不假设 H,然后将整数放入 SortedList 中呢? - Steven Evers

4

我认为在这里使用Linq实在是太过繁琐,但如果你想要用的话,以下是代码:

        int[] arr = { 555, 567, 589, 590, 591, 592, 593, 594, 595, 596, 597 };
        int gr = 0;
        var q = arr
            .Skip(1)
            .Select((x, i) => new { x, group = (x - arr[i]) == 1 ? gr : gr++ })
            .GroupBy( a => a.group)
            .Select(
                a => a.Count() == 1 
                    ? a.First().x.ToString() 
                    : string.Format("{0}-{1}", a.First().x, a.Last().x));
        foreach (var item in q)
        {
            Console.Write(item);
            Console.Write(", ");
        }

2
@Jesse 嗯,就我个人而言,我更喜欢Jon的答案。从CPU和内存的角度来看,它的性能更好。我只是想展示一下如何在Linq中实现它,并不意味着你应该在Linq中实现它。 - Andrey
我同意Jon的答案更高效,但对于我的情况来说,这恰好是我正在寻找的。 - Jesse

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