Function Repository Resource:

LookupPart

Source Notebook

Get part of an expression or return a default value if it doesn’t exist

Contributed by: Richard Hennigan (Wolfram Research)

ResourceFunction["LookupPart"][expr,part]

returns expr[[part]] if it exists and a Missing object otherwise.

ResourceFunction["LookupPart"][expr,{p1,p2,}]

gives the list of parts {expr[[p1]],expr[[p2]],}.

ResourceFunction["LookupPart"][expr,Sequence[p1,p2,]]

gives expr[[p1,p2,]] if it exists and a Missing object otherwise.

ResourceFunction["LookupPart"][expr,parts,default]

gives default if the specified part is not found.

ResourceFunction["LookupPart"][parts]

represents an operator form of LookupParts that can be applied to an expression.

Details and Options

In ResourceFunction["LookupPart"][expr,part,default], default is only evaluated if part does not correspond to a subexpression in expr.
Part 0 of an expression is its head.
ResourceFunction["LookupPart"] supports Span specifications for parts, which include:
m;;part m through the end
;;nfrom the beginning to part n
;;,jcolumn j
m1;;n1,m2;;n2submatrix
ResourceFunction["LookupPart"] has the attributes SequenceHold and HoldRest.
Since ResourceFunction["LookupPart"] has the SequenceHold attribute, wrapping parts in Sequence can be used to retrieve nested values.
The form "key" can be used as a part specification to extract a value from an association whose key is a string. Key[k] can be used to extract values with any keys.
"key" and Key[k] can appear anywhere in the specification of parts.

Examples

Basic Examples (9) 

Look up part of an expression:

In[1]:=
ResourceFunction["LookupPart"][{a, b}, 1]
Out[1]=

If the specified part is not found, a Missing object is returned:

In[2]:=
ResourceFunction["LookupPart"][{a, b}, 3]
Out[2]=

Providing a third argument allows the default value to be provided:

In[3]:=
ResourceFunction["LookupPart"][{a, b}, 3, c]
Out[3]=

When the part is present, the default is not evaluated:

In[4]:=
ResourceFunction["LookupPart"][{a, b}, 1, Print["not found"]]
Out[4]=

Look up multiple parts:

In[5]:=
ResourceFunction["LookupPart"][{a, b, c}, {1, 3, 5}]
Out[5]=
In[6]:=
ResourceFunction["LookupPart"][{a, b, c}, {1, 3, 5}, xx]
Out[6]=

Use Span syntax to look up a sequence of parts:

In[7]:=
ResourceFunction["LookupPart"][{a, b, c}, 2 ;; 3]
Out[7]=
In[8]:=
ResourceFunction["LookupPart"][{a, b, c}, 2 ;; 5]
Out[8]=
In[9]:=
ResourceFunction["LookupPart"][{a, b, c}, 2 ;; 5, xx]
Out[9]=

Count from the end of the list:

In[10]:=
ResourceFunction["LookupPart"][{a, b, c}, -1]
Out[10]=
In[11]:=
ResourceFunction["LookupPart"][{a, b, c}, -5]
Out[11]=
In[12]:=
ResourceFunction["LookupPart"][{a, b, c}, -5, xx]
Out[12]=

Use UpTo in a Span specification:

In[13]:=
ResourceFunction["LookupPart"][{a, b, c}, 2 ;; UpTo[5]]
Out[13]=
In[14]:=
ResourceFunction["LookupPart"][{a, b, c, d, e, f}, 2 ;; UpTo[5]]
Out[14]=

Use the operator form:

In[15]:=
ResourceFunction["LookupPart"][1][{a, b, c}]
Out[15]=
In[16]:=
ResourceFunction["LookupPart"][-1][{a, b, c}]
Out[16]=
In[17]:=
ResourceFunction["LookupPart"][1 ;; 2][{a, b, c}]
Out[17]=
In[18]:=
ResourceFunction["LookupPart"][5][{a, b, c}]
Out[18]=

Scope (9) 

Using zero as the part specification corresponds to the head of an expression:

In[19]:=
ResourceFunction["LookupPart"][a + b, 0]
Out[19]=

Look up a nested part:

In[20]:=
ResourceFunction["LookupPart"][{{a, b}, {c, d}}, Sequence[2, 1]]
Out[20]=
In[21]:=
ResourceFunction["LookupPart"][{{a, b}, {c, d}}, Sequence[2, 3]]
Out[21]=
In[22]:=
ResourceFunction["LookupPart"][{{a, b}, {c, d}}, Sequence[2, 3], xx]
Out[22]=

Get multiple nested parts:

In[23]:=
ResourceFunction[
 "LookupPart"][{{a, b}, {c, d}}, {Sequence[2, 1], Sequence[2, 3], Sequence[1, 1]}]
Out[23]=
In[24]:=
ResourceFunction[
 "LookupPart"][{{a, b}, {c, d}}, {Sequence[2, 1], Sequence[2, 3], Sequence[1, 1]}, xx]
Out[24]=

Use a mix of part specifications:

In[25]:=
ResourceFunction[
 "LookupPart"][{{a, b}, {c, d}}, {Sequence[2, 1], 2, Sequence[1, 1, 0]}]
Out[25]=

Get the second column of a matrix:

In[26]:=
ResourceFunction["LookupPart"][{{a, b, c}, {d, e, f}, {g, h, i}}, Sequence[All, 2]]
Out[26]=

Get part of an association by key:

In[27]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> 2|>, Key[a]]
Out[27]=
In[28]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> 2|>, Key[c]]
Out[28]=

Get part of an association by position:

In[29]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> 2|>, 2]
Out[29]=
In[30]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> 2|>, 5]
Out[30]=

Get a nested part of an association:

In[31]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> <|c -> 3|>|>, Sequence[Key[b], Key[c]]]
Out[31]=

Or equivalently:

In[32]:=
ResourceFunction["NestedLookup"][<|a -> 1, b -> <|c -> 3|>|>, {b, c}]
Out[32]=

Use Unevaluated to look up part of the expression before it evaluates:

In[33]:=
ResourceFunction["LookupPart"][Unevaluated[1 + 2 + 3], -1]
Out[33]=

Only the returned part will evaluate:

In[34]:=
ResourceFunction["LookupPart"][Unevaluated[Echo[1] + Echo[2]], -1]
Out[34]=
In[35]:=
ResourceFunction["LookupPart"][Unevaluated[Echo[1] + Echo[2]], 3]
Out[35]=

Applications (1) 

Create variants of First and Last that return a Missing object when the list is empty:

In[36]:=
first = ResourceFunction["LookupPart"][1]
Out[36]=
In[37]:=
last = ResourceFunction["LookupPart"][-1]
Out[37]=
In[38]:=
first[{1, 2, 3}]
Out[38]=
In[39]:=
first[{}]
Out[39]=
In[40]:=
last[{1, 2, 3}]
Out[40]=
In[41]:=
last[{}]
Out[41]=

Properties and Relations (9) 

LookupPart operates on the FullForm of expressions:

In[42]:=
ResourceFunction["LookupPart"][x/y, 2]
Out[42]=
In[43]:=
FullForm[x/y]
Out[43]=

LookupPart returns the default value instead of failing if the first argument is atomic:

In[44]:=
ResourceFunction["LookupPart"]["Is this fine?", 1, "This is fine."]
Out[44]=

Compare to Part:

In[45]:=
Part["This is not.", 1]
Out[45]=

LookupPart[0] is equivalent to Head:

In[46]:=
ResourceFunction["LookupPart"][0][a + b]
Out[46]=
In[47]:=
Head[a + b]
Out[47]=

LookupPart[Sequence[]] is equivalent to Identity:

In[48]:=
ResourceFunction["LookupPart"][Sequence[]][a + b]
Out[48]=

LookupPart has the SequenceHold attribute:

In[49]:=
ResourceFunction["LookupPart"][a + b, Sequence[]]
Out[49]=

Part does not, so these are equivalent:

In[50]:=
Part[a + b, Sequence[]]
Out[50]=
In[51]:=
Part[a + b]
Out[51]=

Using a Sequence as a part specification is similar to a Fold over the parts:

In[52]:=
list = {{{a, b}, {c, d}}, {{e, f}, {g, h}}}
Out[52]=
In[53]:=
ResourceFunction["LookupPart"][list, Sequence[2, 1, 2]]
Out[53]=
In[54]:=
Fold[ResourceFunction["LookupPart"], list, {2, 1, 2}]
Out[54]=

When the default value is not specified, LookupPart defaults to a Missing object that contains elements corresponding to what would be issued in a message when using Part:

In[55]:=
ResourceFunction["LookupPart"][{1, 2, 3}, 5]
Out[55]=
In[56]:=
Part[{1, 2, 3}, 5]
Out[56]=
In[57]:=
ResourceFunction["LookupPart"][{{a, b}, {c, d}}, Sequence[2, 3]]
Out[57]=
In[58]:=
Part[{{a, b}, {c, d}}, Sequence[2, 3]]
Out[58]=

Specifying part 0 always* succeeds without evaluating the third argument:

In[59]:=
ResourceFunction["LookupPart"][x, 0, Print["You (almost certainly) can't get here."]]
Out[59]=

* Unless it doesn’t:

In[60]:=
ResourceFunction["LookupPart"][TagSetDelayed[nope, 
Blank[][nope, 0, 
Pattern[default, 
Blank[]]], default]; nope, 0, Print["What have you done?"]]

LookupPart has the NHoldAll attribute, so its operator form won’t be affected by N:

In[61]:=
N[{ResourceFunction["LookupPart"][1], {{1, 2, 3}}}]
Out[61]=
In[62]:=
Apply @@ %
Out[62]=

Possible Issues (2) 

Unlike Lookup, the second argument of LookupPart behaves like the second argument of Part, so keys to associations must be wrapped in Key:

In[63]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> 2|>, Key[a]]
Out[63]=
In[64]:=
Part[<|a -> 1, b -> 2|>, Key[a]]
Out[64]=

Attempting to use the key directly results in an error:

In[65]:=
ResourceFunction["LookupPart"][<|a -> 1, b -> 2|>, a]
Out[65]=

This is also true for Part:

In[66]:=
Part[<|a -> 1, b -> 2|>, a]
Out[66]=

However, if the keys are strings, Key is not necessary:

In[67]:=
ResourceFunction["LookupPart"][<|"a" -> 1, "b" -> 2|>, "a"]
Out[67]=
In[68]:=
Part[<|"a" -> 1, "b" -> 2|>, "a"]
Out[68]=

When using Span or All part specifications, LookupPart does not return partial results if any part of the sequence is missing:

In[69]:=
list = Table[Subscript[f, x, y], {x, 4, 1, -1}, {y, x}]
Out[69]=
In[70]:=
ResourceFunction["LookupPart"][list, Sequence[All, 3]]
Out[70]=

This behavior is consistent with Part:

In[71]:=
Part[list, Sequence[All, 3]]
Out[71]=

In this case, use of Map can yield partial results:

In[72]:=
Map[ResourceFunction["LookupPart"][3], list] // DeleteMissing
Out[72]=

Neat Examples (1) 

Create a new lookup function with a neat default value:

In[73]:=
birdLookup[expr_, part_] := ResourceFunction["LookupPart"][expr, part, ResourceFunction["BirdSay"][
    Row[{"The expression ", expr, " does not have part ", part, "."}]]];
In[74]:=
birdLookup[{a, b}, 2]
Out[74]=
In[75]:=
birdLookup[{a, b}, 3]
Out[75]=

Version History

  • 1.0.0 – 15 October 2019

Related Resources

License Information