6
$\begingroup$

Suppose I have a nested list where sublists have unequal length. How can I extract all values of the list at specific level ignoring errors if the value at that level doesn't exist. For example:

lst={{1,2},{a, b, c, d}, {e, f, g, h}, {i, g, k}, {l}}

How can I extract the values at position 4 of each sublist and get {d,h}. No surprise that lst[[All,4]] returns an error, but I don't even know where to start. Any help would be appreciated! Thanks

$\endgroup$
  • 1
    $\begingroup$ you could first pad all your lists to the appropriate length (eg 4) with something like lst=PadRight[#,4,Null]&/@lst and then do lst=Part[lst,All,4]. Then, you could get {d,h} by executing something likeCases[lst, Except[Null],1]. $\endgroup$ – user42582 Aug 28 '17 at 20:59
  • $\begingroup$ @user42582. I think you can just do PadRight[lst], (as long as none of the values you extract from the list are zero). $\endgroup$ – march Aug 28 '17 at 21:05
  • $\begingroup$ @march sure you can... there's a million different ways you can go about doing it $\endgroup$ – user42582 Aug 28 '17 at 21:31
  • 1
    $\begingroup$ Somewhat related: (14688) $\endgroup$ – Mr.Wizard Aug 29 '17 at 10:02
7
$\begingroup$
Cases[lst, {_, _, _, x_, ___} :> x]

{d, h}

$\endgroup$
  • $\begingroup$ Cases[lst, {x__, y_, z___} :> y /; Length[{x}] == 3] $\endgroup$ – user42582 Aug 28 '17 at 21:51
  • 3
    $\begingroup$ For generality consider Repeated: Cases[lst, {Repeated[_, {3}], x_, ___} :> x] $\endgroup$ – Mr.Wizard Aug 29 '17 at 9:44
8
$\begingroup$

You could "transpose" your lst using Flatten (1), and then take the 4 element:

Flatten[lst, {{2}, {1}}][[4]]

{d, h}

  1. Flatten command: matrix as second argument
$\endgroup$
  • 1
    $\begingroup$ Shoot. Eight seconds late. Why do Flatten[lst, {{2}, {1}}] instead of just Flatten[lst, {2}]? $\endgroup$ – march Aug 28 '17 at 21:08
  • 2
    $\begingroup$ @march I think using {2} is simpler, but the {{2}, {1}} (or {{2}}) form is better documented. $\endgroup$ – Carl Woll Aug 28 '17 at 21:17
  • $\begingroup$ @march, that certainly works; in my case, I prefer Carl's version since there is an explicit reminder of which levels are being flattened. $\endgroup$ – J. M. is away Aug 30 '17 at 1:00
6
$\begingroup$

Here's another approach using Query that's perhaps a bit more explicit about what's happening:

Query[DeleteMissing@#&, 4]@lst

The slightly strange @#& part is there to "hide" DeleteMissing from query to make sure it's applied after the selector 4. (In "Query language": we make the typically descending operator DeleteMissing ascending)

$\endgroup$
  • $\begingroup$ my experience with Query, albeit a short one I have to admit, was not a happy one-had trouble wrapping my head around ascending vs descending operators etc-imo it's too much hassle with few rewards $\endgroup$ – user42582 Aug 29 '17 at 7:24
  • $\begingroup$ Query[4] /@ lst // DeleteMissing strikes me as a touch simpler. $\endgroup$ – Mr.Wizard Aug 29 '17 at 9:47
  • 1
    $\begingroup$ @Mr.Wizard True - the other one is contained in an operator though, which might be valuable in some cases... $\endgroup$ – Lukas Lang Aug 29 '17 at 10:01
  • $\begingroup$ Indeed it might :-) $\endgroup$ – Mr.Wizard Aug 29 '17 at 10:03
5
$\begingroup$

Arguably the most pedestrian approach has not yet been posted: pre-filter the list.

Select[lst, Length@# > 3 &][[All, 4]]
{d, h}

A slightly more convenient formulation of the same idea might be:

get[n_][x_] := If[Length@x < n, ## &[], x[[n]]]

get[4] /@ lst
{d, h}
$\endgroup$
3
$\begingroup$

Just for something different:

ReleaseHold@PadRight[lst, Automatic, Hold[Sequence[]]][[All, 4]]
$\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.