7

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

5
  • 7
    Do it have to be with LINQ? Commented Sep 21, 2010 at 16:07
  • What is that set of numbers? A string? An array? Commented Sep 21, 2010 at 16:09
  • Im open to whatever solution. its a set of bills to be exact like H272 for house bill 272, but essentially a set of strings Commented Sep 21, 2010 at 16:12
  • 1
    What are the "H" characters, and why does only H555 include it in the output? Trying to understand how to get you a proper answer... Commented Sep 21, 2010 at 16:12
  • Removed the H from output I can work that out. Makes it simpler to get a solution Commented Sep 21, 2010 at 16:15

2 Answers 2

8

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); 
Sign up to request clarification or add additional context in comments.

3 Comments

Would have been good until he added "H" to the beginning of each item.
Perhaps use the H as a key in a Dictionary the int as the value, compute the range and then reassemble?
@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?
4

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(", "); } 

2 Comments

@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.
I agree Jon's answer is more performant, but for my case this is exactly what I was looking for.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.