1
$\begingroup$

I have a list like:

list = {{3, 1, 4}, {1, 5}, {9, 2, 6}, {5, 3}, {5, 8, 9, 7}, {9, 3},
       {2}, {3, 8, 4, 6, 2, 6}, {4, 3, 3}, {8}, {3, 2, 7, 9, 5, 0}}

Now I can select all nested lists of length 6:

Select[list, Length[#] == 6 &]

which gives the following result:

{{3, 8, 4, 6, 2, 6}, {3, 2, 7, 9, 5, 0}}

Now I want to create lists like this for lengths 1, 2, ... maximum length. I understand I'll need

Sort[DeleteDuplicates[Length /@ list]]

which gives

{1, 2, 3, 4, 6}

How do I run over this list with the Select function above?

$\endgroup$
  • 1
    $\begingroup$ Does GatherBy[list,Length] do what you want? $\endgroup$ – N.J.Evans Nov 1 '16 at 13:56
  • $\begingroup$ @n.j.evans - More or less. I want each of the sublists as separate lists, but I think I can handle that. Thanks. $\endgroup$ – stevenvh Nov 1 '16 at 13:59
  • $\begingroup$ Table[Set[x[Last@Dimensions@i], i], {i, GatherBy[list, Length]}] will gather the lists then set them to the values of x[len], where x[1] is the length 1 lists, and x[2] all the length 2 lists etc. $\endgroup$ – N.J.Evans Nov 1 '16 at 14:14
4
$\begingroup$

GatherBy as N.J.Evans suggested:

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

GatherBy[Sort @ list, Length]
{{{2}, {8}},
 {{1, 5}, {5, 3}, {9, 3}},
 {{3, 1, 4}, {4, 3, 3}, {9, 2, 6}},
 {{5, 8, 9, 7}},
 {{3, 2, 7, 9, 5, 0}, {3, 8, 4, 6, 2, 6}}}

Or with GroupBy for explicit length values:

KeySort @ GroupBy[list, Length]
<|1 -> {{2}, {8}},
  2 -> {{1, 5}, {5, 3}, {9, 3}}, 
  3 -> {{3, 1, 4}, {9, 2, 6}, {4, 3, 3}},
  4 -> {{5, 8, 9, 7}}, 
  6 -> {{3, 8, 4, 6, 2, 6}, {3, 2, 7, 9, 5, 0}}|>
$\endgroup$
  • $\begingroup$ Or SplitBy[Sort @ list, Length] $\endgroup$ – corey979 Nov 1 '16 at 16:49
  • $\begingroup$ @corey979 Yes, but oddly SplitBy usually seems to be slower than GatherBy so I tend to default to the latter. $\endgroup$ – Mr.Wizard Nov 2 '16 at 2:07
2
$\begingroup$

If performance is important, you can use Sow and Reap to define two functions: ListsofLength and Lengths. I define them as such:

ListsOfLength[list_, length_] := 
  If[ListQ[#], #, {}] &@(length /. Lengths[list]);
Lengths[list_] := Lengths[list] = Last@Reap[Sow[#, Length[#]] & /@ list;, _, Rule];

Here you call ListOfLengths with your list and the desired lengths as input. It calls Lengths which is the expensive function that uses Sow and tags all sublists with their respective lengths. Reap can then extract the sublists, grouping together the different tags (=lengths) and supplying them as a rule for easy use. Since Lengths is memoized it's only expensive the first time you call ListOfLengths.

For long lists it's about four times faster, see:

list = Table[RandomInteger[{0, 10}, RandomInteger[{1, 5}]], 1000000];
GatherBy[Sort@list, Length][[2]]; // AbsoluteTiming
(*returns 3.16s*)
ListsOfLength[list, 2]; // AbsoluteTiming
(*returns 0.81s*)
$\endgroup$
  • $\begingroup$ Pardon me, but I don't understand the point of this. This only returns a single length rather than all lengths as the OP requested. For a single length of two with your example data Select[list, Length@# == 2 &] is faster than ListsOfLength[list, 2], and Pick[list, Length /@ list, 2] is faster still. $\endgroup$ – Mr.Wizard Nov 2 '16 at 2:11
  • $\begingroup$ I imagined a usecase where Op wants to access some list of sublists individually which he can by using ListsOfLength. So if at one point he's wants all lists of length '5' he calculates the expensive function Lengths[list] (which is a faster version of your GroupBy answer) once and if later he wants lists of length 10 it'll basically be free. Ultimately ListsOfLength is just a fancy wrapper. If that doesn't make sense feel free to downvote and I might delete the answer if it doesn't help. $\endgroup$ – AndreasP Nov 2 '16 at 8:25
1
$\begingroup$

Using Function:

Function[{x}, Select[list, Length[#] == x &]] /@ 
 Sort[DeleteDuplicates[Length /@ list]]
$\endgroup$
1
$\begingroup$

Just to illustrate GroupBy and Reap/Sow:

gb = GroupBy[list, Length]
sw = Last@Reap[Sow[#, Length@#] & /@ list, _, Rule]
fg[n_?(MemberQ[Keys[gb], #] &)] := n /. gb
fg[n_] := "Missing"
fs[n_?(MemberQ[Keys[sw], #] &)] := n /. gb
fs[n_] := "Missing"

So,

With[{rg = Range[6]}, 
 Grid[Transpose[{rg, fg /@ rg, fs /@ rg}], Frame -> All]]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.