# Selecting a sublist based on Length

If you have a simple list of lists as follows:

test = {{1, 2}, {4, 5, 6, 7}, {5, 4, 3}}


How do you ask Mathematica to return the sublist of greatest length?

I've been trying to write a Select command using pure functions without success.

If you only want one item from the resulting list, you can use the two-argument form of Ordering instead of Sort to be a bit more efficient:

test[[Ordering[test, -1]]]


biglist =
Table[RandomInteger[10, RandomInteger[100]], {10^5}];

Timing[biglist[[Ordering[biglist, -1]]]]

(*
==> {0.006476, {{10, 10, 10, 3, 4, 7, 4, 3, 9, 8, 8, 1, 2, 1, 5,
10, 10, 10, 9, 4, 6, 6, 9, 1, 2, 10, 8, 3, 0, 9, 1, 2, 5, 1, 1, 2,
7, 8, 9, 10, 8, 4, 8, 4, 7, 9, 3, 4, 5, 1, 6, 6, 4, 5, 8, 6, 3, 2,
6, 4, 9, 9, 9, 7, 1, 10, 4, 2, 10, 8, 0, 8, 1, 0, 9, 10, 7, 4, 5,
3, 6, 6, 6, 4, 2, 3, 1, 4, 9, 6, 5, 1, 8, 10, 0, 1, 3, 5, 10, 4}}}
*)

Timing[Last@Sort@biglist]

(*
==> {0.170369, {10, 10, 10, 3, 4, 7, 4, 3, 9, 8, 8, 1, 2, 1, 5,
10, 10, 10, 9, 4, 6, 6, 9, 1, 2, 10, 8, 3, 0, 9, 1, 2, 5, 1, 1, 2,
7, 8, 9, 10, 8, 4, 8, 4, 7, 9, 3, 4, 5, 1, 6, 6, 4, 5, 8, 6, 3, 2,
6, 4, 9, 9, 9, 7, 1, 10, 4, 2, 10, 8, 0, 8, 1, 0, 9, 10, 7, 4, 5, 3,
6, 6, 6, 4, 2, 3, 1, 4, 9, 6, 5, 1, 8, 10, 0, 1, 3, 5, 10, 4}}
*)

• How could I forget about this one? +2 if I could! (It should be noted that this only returns one list and not ties.) Commented Feb 5, 2012 at 3:30
• This returns only one list of the highest length as R.M.'s and Spartacus' ones do, so these are not the full solutions. Commented Feb 5, 2012 at 10:49

One possibility:

test = {{1, 2}, {4, 5, 6, 7}, {5, 4, 3}};
lengths = Length /@ test;
max = Max[lengths];
pos = Position[lengths, max];
Extract[test, pos]


gives:

{{4, 5, 6, 7}}


If there are two or more sublists that are of 'greatest length' those will also be returned.

• This is faster than I thought it would be, slightly faster than Last@SortBy[test, {Length}] on my test data, and quite a bit faster than the infix thing. Commented Feb 5, 2012 at 2:26
• Your solution is the best, though not in price, see mathematica.stackexchange.com/questions/1342/… Commented Feb 6, 2012 at 11:14

Sort automatically sorts by length, so it is as simple as

Last@Sort@test

• Ah, very nice! It's going to be slower because of all the tiebreaking, but very elegant. Commented Feb 5, 2012 at 2:16

Reasonably fast and quite direct, but returns only one list if there are ties:

Last@SortBy[test, {Length}]


More whimsical but catching ties (warning: infix ahead):

test ~SortBy~ Length ~SplitBy~ Length // Last


Since Arnoud's method tests the fastest for functions that include ties, here is my terse version of it:

longest[L_List] := L ~Extract~ Position[#, Max@#] &[Length /@ L]


If you have V10, consider using MaximalBy:

MaximalBy[test, Length, 1]


Notice that MaximalBy[test, Length] returns all of the longest lists. Similarly, there is also MinimalBy.

A solution using Select is :

max = Max[Length /@ test];
Select[test, Length[#] == max &]


This solution and Arnoud's, as well as J.M.'s ones, are better if we have more lists of maximal length. E.g. for

test = {{1, 2}, {4, 5, 6, 7}, {5, 4, 3}, {2, 2, 3, 4}};


this returns

 {{4, 5, 6, 7}, {2, 2, 3, 4}}


Edit

Since one would like to know performance issues of various methods I've made a comparison of presented approaches (only for methods which return all longest sublists) on a very long list from the best to the slowest. On smaller lists proportions of timings may slightly change, but in general, the order is preserved.

longlist = Table[RandomInteger[{-10, 10}, RandomInteger[100]], {10^6}];

{lengths = Length /@ longlist;                                (*Arnoud*)
max = Max[lengths];
pos = Position[lengths, max];
Extract[longlist, pos];} // Timing
(*
==>  {0.422, {Null}}
*)
{max = Max[Length /@ longlist];                               (*Artes*)
Select[longlist, Length[#] == max &];} // Timing
(*
==>  {1.685, {Null}}
*)
Pick[longlist, #, Max[#]] &[Length /@ longlist]; // Timing     (*J.M.*)
(*
==>  {2.012, Null}
*)
longlist~SortBy~Length~SplitBy~Length // Last; // Timing     (*Spartacus*)
(*
==>  {7.098, Null}
*)
allMaxBy[longlist, Length]; // Timing                        (*Szabolcs*)
(*
==>  {7.144, Null}
*)

• Somewhat different results are had with longlist = RandomInteger[99, #] & /@ RandomInteger[{1, 5000}, 15000]; Commented Feb 6, 2012 at 19:06
• For this list your method as well as Szabolcs' one seem to be 7 times faster than J.M.'s approach, though more than 2 times slower than mine and 7 times than Arnoud's Commented Feb 6, 2012 at 19:22

Alternatively:

test = {{2, 3}, {1, 2}, {4, 5, 6, 7}, {5, 4, 3}, {8, 9, 10, 11}};

Pick[test, #, Max[#]] &[Length /@ test]
{{4, 5, 6, 7}, {8, 9, 10, 11}}


I sometimes use a little function MaxBy, made to be analogous with SortBy:

MaxBy[list_, fun_] := list[[First@Ordering[fun /@ list, -1]]]


You need the largest element by length, so you can evaluate

MaxBy[data, Length]


Note: this is based on the same principle as @Brett's solution, but it is slower. @Brett's and @R.M's exploit the fact that Mathematica sorts by length by default, while my solution explicitly uses Length. I still think it's a useful little function, so I shared it again.

The problem with MaxBy is that it only returns a single element, while there may be more than one list of the same length. Here's a somewhat slow but simple implementation that returns all maxima:

allMaxBy[data_, fun_] := Last@SplitBy[SortBy[data, fun], fun]


Nearest has been improved in V10.1. It with Length /@ longlist and MaximalBy compete with the pre-V10 solution by Arnoud. Two ways of using Nearest are presented, although there is not much difference between them If we speed up Arnoud's by compiling Position, they are in a virtual dead heat. For ease of use and elegance of expression, MaximalBy seems the winner.

longlist = Table[RandomInteger[{-10, 10}, RandomInteger[100]], {10^6}];

{lengths = Length /@ longlist;                       (*Arnoud*)
max = Max[lengths];
pos = Position[lengths, max];
Extract[longlist, pos];} // RepeatedTiming
(*  {0.424, {Null}}  *)

MaximalBy[longlist, Length, 1]; // RepeatedTiming    (* mrm *)
(*  {0.396, Null}  *)

Part[longlist,
Nearest[# -> Automatic, Max[#]] &[Length /@ longlist]]; // RepeatedTiming
(*  {0.388, Null}  *)

Nearest[# -> longlist, Max[#]] &[Length /@ longlist]; // RepeatedTiming
(*  {0.392, Null}  *)


Packing the lengths of longlist helps a bit here.

{lengths = DeveloperToPackedArray[Length /@ longlist];   (*Arnoud*)
max = Max[lengths];
pos = Compile[{{lengths, _Integer, 1}, {max, _Integer}},
Position[lengths, max]][lengths, max];
Extract[longlist, pos];} // RepeatedTiming
(*  {0.38, {Null}}  *)

Part[longlist,
Nearest[# -> Automatic, Max[#]] &[
DeveloperToPackedArray[Length /@ longlist]]]; // RepeatedTiming
(*  {0.375, Null}  *)

Nearest[# -> longlist, Max[#]] &[
DeveloperToPackedArray[Length /@ longlist]]; // RepeatedTiming
(*  {0.376, Null}  *)


And packing really helps here (original was 1.26 sec. on my machine):

Pick[longlist, #, Max[#]] &[                              (* Guess... *)
DeveloperToPackedArray[Length /@ longlist]]; // RepeatedTiming
(*  {0.365, Null}  *)


We could also use TakeLargestBy

list = {{1, 2}, {4, 5, 6, 7}, {5, 4, 3}};


Take the largest list by length:

TakeLargestBy[Length, 1] @ list


{{4, 5, 6, 7}}

Take the two largest lists with additional information:

TakeLargestBy[list -> All, Length, 2]


gives

{<|"Element" -> {4, 5, 6, 7, 7}, "Index" -> 2, "Value" -> 5|>,
<|"Element" -> {5, 4, 3}, "Index" -> 3, "Value" -> 3|>}