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$
4
  • 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$ Commented Aug 28, 2017 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$ Commented Aug 28, 2017 at 21:05
  • $\begingroup$ @march sure you can... there's a million different ways you can go about doing it $\endgroup$ Commented Aug 28, 2017 at 21:31
  • 1
    $\begingroup$ Somewhat related: (14688) $\endgroup$ Commented Aug 29, 2017 at 10:02

5 Answers 5

8
$\begingroup$
Cases[lst, {_, _, _, x_, ___} :> x] 

{d, h}

$\endgroup$
2
  • $\begingroup$ Cases[lst, {x__, y_, z___} :> y /; Length[{x}] == 3] $\endgroup$ Commented Aug 28, 2017 at 21:51
  • 3
    $\begingroup$ For generality consider Repeated: Cases[lst, {Repeated[_, {3}], x_, ___} :> x] $\endgroup$ Commented Aug 29, 2017 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$
3
  • 1
    $\begingroup$ Shoot. Eight seconds late. Why do Flatten[lst, {{2}, {1}}] instead of just Flatten[lst, {2}]? $\endgroup$ Commented Aug 28, 2017 at 21:08
  • 2
    $\begingroup$ @march I think using {2} is simpler, but the {{2}, {1}} (or {{2}}) form is better documented. $\endgroup$ Commented Aug 28, 2017 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$ Commented Aug 30, 2017 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$
4
  • $\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$ Commented Aug 29, 2017 at 7:24
  • $\begingroup$ Query[4] /@ lst // DeleteMissing strikes me as a touch simpler. $\endgroup$ Commented Aug 29, 2017 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$ Commented Aug 29, 2017 at 10:01
  • $\begingroup$ Indeed it might :-) $\endgroup$ Commented Aug 29, 2017 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$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.