Before Mathematica 10.0, when there was no Association, the suggested way of using datastructures map, was to abus arguments of undefined symbols:

foo["a"] = 1
foo["b"] = 1

I would like to explore the keys of the map foo. Defined symbols can be explored by Names command. However, it does not work. I have tried:

Names["foo*"]
Names["foo[*]"]
Names["foo[\"*\"]"]
Names["foo[a]"]

which gives:

{"foo"}

{}

{}

{}

The manual is also clearly wrong:

Names["string"] gives the same list of names as ?string.

While ?"foo" and ?foo lists all my elements, Names["foo"] only returns {"foo"}, instead of {"foo[\"a\"]","foo[\"b\"]"} or something similar.

What is the proper way of extracting all the members of the map foo?

Why do I ask? From time to time I still feel it nice to use the syntax to give my variable names some structure and later go through them using Map for example, if I only had possibility to get the list of keys.

Edit: As I wrote in the very beginning, I am aware of Association. Abusing arguments of symbols is, however, way more flexible. For example, for some arguments, a function could be called instead of a value from a map. See for example see memoization.

(Using Mathematica 11.3)

  • Does DownValues[foo] provide what you want? Or DownValues[foo][[All, 1, 1, 1]]? – Henrik Schumacher Aug 19 at 18:52
  • Since you're talking about keys, you might be interested in Association – Lukas Lang Aug 19 at 18:58
  • @HenrikSchumacher, Yes, perfect! I would accept this answer. ("proper" is so relative :)) – Johu Aug 19 at 19:02
  • @LukasLang, Thanks, but see the edit. – Johu Aug 19 at 19:08
up vote 3 down vote accepted

Turning a comment into an answer.

To get the replacement rules:

DownValues[foo] 

{HoldPattern[foo["a"]] :> 1, HoldPattern[foo["b"]] :> 2}

To get only the "keys":

DownValues[foo][[All, 1, 1, 1]]

{"a", "b"}

Note that this may lead to problems, if DownValues rules with more general patterns are also used in foo. Maybe

Cases[DownValues[foo][[All, 1, 1, 1]], _String]

will circumvent some issues. But in the end, the programmer is in charge of keeping the definitions of foo free from any disturbing DownValues. Another reason (beyond performance) why Association should be prefered if possible. (Towards performance: If you have to make many request into an Association, using Lookup will in general be a lot faster than Mapping the Association.)

Remark towards performance

Evaluate this in your notebook to realize that Mathematica's symbol lookup (or rather lookup of DownValues) is not unbeatably fast.

keys = StringJoin /@ Tuples[Alphabet[], {4}];
values = RandomReal[{-1, 1}, Length[keys]];
queries = RandomChoice[keys, 1000000];

a = AssociationThread[keys, values]; // AbsoluteTiming // First
r1 = Lookup[a, queries]; // AbsoluteTiming // First

ClearAll[f];
SetAttributes[f, Listable];

MapThread[With[{x = f[#1]}, x = #2] &, {keys, values}]; // AbsoluteTiming // First
r2 = Map[f, queries]; // AbsoluteTiming // First
r3 = f[queries]; // AbsoluteTiming // First
r4 = Table[f[x], {x, queries}]; // AbsoluteTiming // First
r1 == r2 == r3 == r4

This is what I get on my machine:

0.33415

0.560602

1.24963

1.24715

1.12814

0.89728

True

  • I appreciate the note on the problem. Regarding the performance: I doubt there is anything faster in Mathematica than symbol lookup like foo["a"]? Obviously Lookup and Map have very different application and should not be compared! – Johu Aug 19 at 20:27
  • 1
    In case of doubts, see my last edit. – Henrik Schumacher Aug 19 at 21:07
  • 1
    Now I see what you meant: Lookup[a, queries] vs Table[f[x], {x, queries}]. I thought you were talking about Lookup[a, RandomChoice[queries]]. Note, that Table[a[x], {x, queries}] is as fast as Table[f[x], {x, queries}] and thus the advantage is really in some bulk pipelining possible only with vectorized Lookup. Thanks! – Johu Aug 20 at 8:13

The output of ?string is either a structured list of names, if more than one name matches string, or first a list of a single name followed by a list of definitions, which may include OwnValues, DownValues, UpValues, SubValues, FormatValues, and so forth. A list of definitions is not the same as a list of names. Names[string] just gives the names and not the definitions. The documentation is not wrong, strictly speaking in light of this distinction, imo; however, without the distinction in mind, the documentation might suggest the equivalence of the two, which compromises its clarity.

The OP's example shows only one name, Global`foo in the blue cell, in the output for ? foo. It is followed by a list of two definitions in the third cell of the image.

enter image description here

You can get the definitions displayed from the undocumented Language`ExtendedDefinition:

Language`ExtendedDefinition[foo]
(*
  Language`DefinitionList[HoldForm[foo] -> 
    {OwnValues -> {},
     SubValues -> {},
     UpValues -> {}, 
     DownValues -> {
       HoldPattern[foo["a"]] -> 1, 
       HoldPattern[foo["b"]] -> 1},
     NValues -> {}, 
     FormatValues -> {},
     DefaultValues -> {},
     Messages -> {}, 
     Attributes -> {}}]
*)

There's another similar function Language`ExtendedFullDefinition[foo].

  • Note, that I was interested in the names and not the definitions. And ? gives me a list containing all definitions, while Name only finds the "head symbol", and this is what I find contradicting with the documentation. – Johu Aug 19 at 20:34
  • @Johu Note that foo["a"] is not a name, but part of the definitions. Perhaps my characterization was too broad, but what you're seeking is part of the definitions. – Michael E2 Aug 19 at 20:39
  • Alright... you might be right about the definition vs name thing. Will try to look it up. Thanks! – Johu Aug 19 at 20:44
  • @Johu If you want more than just the DownValues (and ? shows more when there is more), then Keys /@ Values /@ Last@ First@ Language`ExtendedDefinition[foo] should get you started. Maybe apply Flatten to it. I don't know if you want the HoldPattern wrappers, but they keep foo["a"] etc. from evaluating. – Michael E2 Aug 19 at 20:59

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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