tags:

views:

138

answers:

2

I'm looking for a way to compute the range of a give set of number for example.

if I had H555,H567,H589,H590,H591,H592,H593,H594,H595,H596,H597

I would like output of H555,H567,H589-H597.

I have looked through relevant questions and can not find anything like what I'm looking for.

Thanks

+3  A: 

Well, I'd do something like this:

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

Then (completely untested, may not even compile, but hopefully you'll get the drift):

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

I don't think this is particularly easy to do using straight LINQ, to be honest.

EDIT: To adapt this now that everything starts with H, just use:

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);
Jon Skeet
Would have been good until he added "H" to the beginning of each item.
Abe Miessler
Perhaps use the H as a key in a Dictionary the int as the value, compute the range and then reassemble?
Jesse
@Jesse: What you describe would create a dictionary where every key was the same (ie. not unique). Why not assume H, put the ints into a SortedList then?
SnOrfus
+1  A: 

I think that Linq is really overhead here but if you want it here you are:

        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(", ");
        }
Andrey
Think i can get this to work. Thanks
Jesse
@Jesse well, personally i prefer Jon's answer. It is better in terms of performance both CPU and memory. I just wanted to show how you can do it in Linq, it doesn't mean that you should do it in Linq.
Andrey