From Asbjørn, 7 Years ago, written in Delphi (Object Pascal).
Embed
  1. program RangeCombo;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. {$R *.res}
  6.  
  7. uses
  8.   System.SysUtils, System.Math, Generics.Defaults, Generics.Collections;
  9.  
  10. type
  11.   Func<TResult> = reference to function(): TResult;
  12.   Func<T, TResult> = reference to function(const Arg1: T): TResult;
  13.   Func<T1, T2, TResult> = reference to function(const Arg1: T1; const Arg2: T2): TResult;
  14.  
  15.   // various algorithms
  16.   Alg = record
  17.     class function Aggregate<T, TAccumulate>(const Input: TArray<T>; const F: Func<TAccumulate, T, TAccumulate>): TAccumulate; overload; static;
  18.     class function Aggregate<T, TAccumulate>(const Input: TArray<T>; const InitialValue: TAccumulate; const F: Func<TAccumulate, T, TAccumulate>): TAccumulate; overload; static;
  19.     class function Sort<T>(const Input: TArray<T>): TArray<T>; overload; static;
  20.   end;
  21.  
  22.  
  23. { Alg }
  24.  
  25. class function Alg.Aggregate<T, TAccumulate>(const Input: TArray<T>;
  26.   const F: Func<TAccumulate, T, TAccumulate>): TAccumulate;
  27. begin
  28.   result := Aggregate<T, TAccumulate>(Input, Default(TAccumulate), F);
  29. end;
  30.  
  31. class function Alg.Aggregate<T, TAccumulate>(const Input: TArray<T>;
  32.   const InitialValue: TAccumulate;
  33.   const F: Func<TAccumulate, T, TAccumulate>): TAccumulate;
  34. var
  35.   elm: T;
  36. begin
  37.   result := InitialValue;
  38.   for elm in Input do
  39.   begin
  40.     result := F(result, elm);
  41.   end;
  42. end;
  43.  
  44. class function Alg.Sort<T>(const Input: TArray<T>): TArray<T>;
  45. begin
  46.   result := Copy(Input);
  47.   TArray.Sort<T>(result, TComparer<T>.Default);
  48. end;
  49.  
  50.  
  51.  
  52.  
  53. type
  54.   Range = record
  55.     First: integer;
  56.     Last: integer;
  57.  
  58.     class operator Implicit(const Value: integer): Range;
  59.   end;
  60.  
  61. function IsSingularRange(const R: Range): boolean;
  62. begin
  63.   result := (R.First = R.Last);
  64. end;
  65.  
  66. { Range }
  67.  
  68. class operator Range.Implicit(const Value: integer): Range;
  69. begin
  70.   result.First := Value;
  71.   result.Last := Value;
  72. end;
  73.  
  74.  
  75.  
  76. function CombineRanges(const Input: TArray<integer>): TArray<Range>;
  77. var
  78.   sortedInput: TArray<integer>;
  79. begin
  80.   sortedInput := Alg.Sort<integer>(Input);
  81.  
  82.   result := Alg.Aggregate<integer, TArray<Range>>(sortedInput,
  83.     function(const Accumulator: TArray<Range>; const Value: integer): TArray<Range>
  84.     var
  85.       r: Range;
  86.     begin
  87.       if (Length(Accumulator) = 0) then
  88.       begin
  89.         result := [Value];
  90.         exit;
  91.       end;
  92.  
  93.       r := Accumulator[High(Accumulator)];
  94.  
  95.       if (Value > (r.Last + 1)) then
  96.       begin
  97.         // need a new range
  98.         result := Accumulator + [Value];
  99.       end
  100.       else
  101.       begin
  102.         // extend last range
  103.         result := Accumulator;
  104.         result[High(result)].Last := System.Math.Max(r.Last, Value);
  105.       end
  106.     end
  107.   );
  108. end;
  109.  
  110. function ExpandRanges(const Input: TArray<Range>): TArray<integer>;
  111. begin
  112.   result := Alg.Aggregate<Range, TArray<integer>>(Input,
  113.     function(const Accumulator: TArray<integer>; const Value: Range): TArray<integer>
  114.     var
  115.       i: integer;
  116.     begin
  117.       result := Accumulator;
  118.       for i := Value.First to Value.Last do
  119.       begin
  120.         result := result + [i];
  121.       end;
  122.     end
  123.   );
  124. end;
  125.  
  126. var
  127.   input: TArray<integer>;
  128.   ranges: TArray<Range>;
  129.   r: Range;
  130.   values: TArray<integer>;
  131.   i: integer;
  132. begin
  133.   input := [1,2,3,4,6,7,8,13,14];
  134.  
  135.   try
  136.     ranges := CombineRanges(input);
  137.  
  138.     for r in ranges do
  139.     begin
  140.       if (IsSingularRange(r)) then
  141.         WriteLn(r.First)
  142.       else
  143.         WriteLn(r.First, '-', r.Last);
  144.     end;
  145.  
  146.     WriteLn;
  147.     WriteLn;
  148.  
  149.     values := ExpandRanges(ranges);
  150.  
  151.     for i in values do
  152.     begin
  153.       WriteLn(i);
  154.     end;
  155.  
  156.   except
  157.     on E: Exception do
  158.       Writeln(E.ClassName, ': ', E.Message);
  159.   end;
  160.   WriteLn('done');
  161.   ReadLn;
  162. end.