8

I'm trying to find a way to use a hash map in R, and after some searching I get the R-environment. But how can I iterate through all the items in an environment ? When I run the following code, I was expecting output like this :

1

2

But I get two lines of NULL instead, how can I get what I want ?

map <- new.env(hash=T, parent=emptyenv())
assign('a', 1, map)
assign('b', 2, map)
for (v in ls(map)) {
    print(map$v)
}
smci
  • 32,567
  • 20
  • 113
  • 146
Derrick Zhang
  • 21,201
  • 18
  • 53
  • 73

2 Answers2

13

The use of "$" inside a function where it is desired to interpret the input is a common source of programming error. Use instead the form object[[value]] (without the quotes.)

for (v in ls(map)) {
    print(map[[v]])
}
IRTFM
  • 258,963
  • 21
  • 364
  • 487
7

It depends on what you want to do. I am assuming that your print example above is something that you are doing just as an example but that you may want to do something more than just print!

If you want to get an object based on each element of an environment, then you use eapply(env, function). It works like the other *apply() functions. It returns a list whose objects are the objects you created from the function passed to eapply() and whose names are copied over from the environment.

For example, in your specific case

map <- new.env(hash=T, parent=emptyenv())  
assign('a', 1, map)  
assign('b', 2, map)  

eapply(map, identity)

returns a list of the two elements. It looks a lot like a hash table showing that you could implement a hash table as a list instead of an environment (which is a little unorthodox, but definitely interesting).

To see how this would work for some non-trivial, custom function, here is an example

eapply(map, function(e) {
  # e here stands for a copy of an element of the environment
  e <- my.function(e)
  my.other.function(e)
})

If you instead want to do something for each of the elements of an environment, without returning a list object at the end, you should use a for loop like @DWin did in his answer.

My worry, though, is that you will not really want to just print but that you will eventually create objects based on your "hash table" elements and then stuff them back into a list for further processing. In that instance, you should really use eapply(). The code will be cleaner and it will more closely adhere to R's idioms. It takes care of iterating and creating the list of results for you.

adamleerich
  • 5,741
  • 2
  • 18
  • 20
  • It would be better to provide an example to solve the OP's problem: `eapply(map,identity)` – James Sep 14 '11 at 10:29
  • The OP's problem is that he doesn't know how to iterate through an environment. And I told him about a handy function to do that. One that follows R's idioms rather nicely. – adamleerich Sep 14 '11 at 10:32
  • True, but he also had a specific case use, the answer is better now, so you have a vote from me. Beware though: because of its side effects `print` doesn't always give what you expect when used in `*apply` statements. – James Sep 14 '11 at 10:34
  • @James: yes you are right. In my haste, I missed an opportunity to explain a little more about the extras `eapply()` would give. I revised my answer. – adamleerich Sep 14 '11 at 13:17
  • What's the e in the "function(e)" from your second example though ? It's a key-value pair of some structure ? Or it's the key or the value ? I'm confused. – Derrick Zhang Sep 15 '11 at 04:16
  • It is an element of the environment, a 'value' in your hash table analogy. – adamleerich Sep 15 '11 at 04:23
  • You have asked quite a few questions on SO the past couple of days on this project of yours. You seem to be struggling with some of the basic R patterns. Instead of trying to implement what you are used to in another language, why don't you embrace R's differences? I recommend learning the R way of doing things. Just some friendly advice . . . – adamleerich Sep 15 '11 at 04:42