How to classify an ordered ascending {0, 5, 5, 10, 19, 22, 23} into several classes {{0,9},{10,19}, ...}?

data = {0, 5, 5, 10, 19, 22, 23, 24, 25, 33, 34, 40, 40, 42, 53, 62, 69, 74, 91, 91};

GatherBy

GatherBy[data, Quotient[#, 10] &]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53},
{62, 69}, {74}, {91, 91}}

Split

Split[data, SameQ @@ Quotient[{##}, 10] &]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53},
{62, 69}, {74}, {91, 91}}

GroupBy

Values @ GroupBy[Quotient[#, 10] &] @ data
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53},
{62, 69}, {74}, {91, 91}}

BinLists

DeleteCases[{}] @ BinLists[data, 10]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53},
{62, 69}, {74}, {91, 91}}

Or use the second column of you class as bin limits:

binlims = {Join[{-Infinity}, class[[All, 2]], {Infinity}]};
DeleteCases[{}] @ BinLists[data, binlims]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 42}, {53},
{62, 69}, {74}, {91, 91}}

  • # always binds itself to the most direct &, and is equivalent to #1, so #[[1]] <= # <= #[[2]] is erroneous.
  • TakeWhile scans from the first element of the list. You're giving every class the same list. As a result, e.g., for{10, 19}, since the first element 0 is not within this range, it immediately halts and returns {}.

As an alternative, you should take elements and drop (pass) the rest to next class:

FoldPairList[
 TakeDrop[
   #1, LengthWhile[#1, u \[Function] Between[u, #2]]
                (*     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                   or `Between[#2]                 `, the operator form *)
   ] &, data, class]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 
  42}, {53}, {62, 69}, {74}, {}, {91, 91}}

Other approaches

Level[SplitBy[data, Between /@ class], {-2}]
{{0, 5, 5}, {10, 19}, {22, 23, 24, 25}, {33, 34}, {40, 40, 
  42}, {53}, {62, 69}, {74}, {91, 91}}

For Edit

We can get classes from lower bounds l:

class = Partition[Flatten[{l, \[Infinity]}], 2, 1]
{{17, 24}, {24, 31}, {31, 38}, {38, 45}, {45, 52}, {52, 59}, {59, 
  66}, {66, \[Infinity]}}

Then similarly,

FoldPairList[
 TakeDrop[#1,
   LengthWhile[#1, u \[Function] #2[[1]] <= u < #2[[2]]]
   ] &, data, class]
{{17, 18, 19, 20, 20, 20, 21, 21, 22, 22, 22, 22, 23}, {24, 24, 26, 
  27, 28, 28, 29, 29, 29, 30, 30}, {32, 32, 33, 34, 34, 34, 34, 34, 
  34, 35, 35, 35, 36, 36}, {38, 38, 39, 39, 41, 42, 42, 42, 43, 44, 
  44, 44}, {45, 45, 45, 46, 46, 47, 47, 49, 49, 51, 51, 51, 51, 
  51}, {52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 
  55, 55, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58}, {59, 60, 61, 
  61, 62, 62, 63}, {69}}

Of course, it's always better to use built-in functions than implementing by yourself.


Just a quick benchmark of kglr's methods in version 10.1.

f1[data_] := GatherBy[data, Quotient[#, 10] &];
f2[data_] := Split[data, SameQ @@ Quotient[{##}, 10] &];
f3[data_] := Values@GroupBy[Quotient[#, 10] &]@data;
f4[data_] := DeleteCases[{}]@BinLists[data, 10];
f5[data_] := SplitBy[data, Quotient[#, 10] &];

Needs["GeneralUtilities`"]

BenchmarkPlot[{f1, f2, f3, f4, f5}, Sort@RandomInteger[5 #, #] &, 
 "IncludeFits" -> True]

enter image description here