How to Correctly Use Lists in R?
Brief background: Many (most?) contemporary programming languages in widespread use have at least a handful of ADTs [abstract data types] in common, in particular,
string (a sequence comprised of characters)
list (an ordered collection of values), and
map-based type (an unordered array that maps keys to values)
In the R programming language, the first two are implemented as character
and vector
, respectively.
When I began learning R, two things were obvious almost from the start: list
is the most important data type in R (because it is the parent class for the R data.frame
), and second, I just couldn't understand how they worked, at least not well enough to use them correctly in my code.
For one thing, it seemed to me that R's list
data type was a straightforward implementation of the map ADT (dictionary
in Python, NSMutableDictionary
in Objective C, hash
in Perl and Ruby, object literal
in Javascript, and so forth).
For instance, you create them just like you would a Python dictionary, by passing key-value pairs to a constructor (which in Python is dict
not list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
And you access the items of an R List just like you would those of a Python dictionary, e.g., x['ev1']
. Likewise, you can retrieve just the 'keys' or just the 'values' by:
names(x) # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"
unlist(x) # fetch just the 'values' of an R list
# ev1 ev2 rv
# "10" "15" "Group 1"
x = list("a"=6, "b"=9, "c"=3)
sum(unlist(x))
# [1] 18
but R list
s are also unlike other map-type ADTs (from among the languages I've learned anyway). My guess is that this is a consequence of the initial spec for S, i.e., an intention to design a data/statistics DSL [domain-specific language] from the ground-up.
three significant differences between R list
s and mapping types in other languages in widespread use (e.g,. Python, Perl, JavaScript):
first, list
s in R are an ordered collection, just like vectors, even though the values are keyed (ie, the keys can be any hashable value not just sequential integers). Nearly always, the mapping data type in other languages is unordered.
second, list
s can be returned from functions even though you never passed in a list
when you called the function, and even though the function that returned the list
doesn't contain an (explicit) list
constructor (Of course, you can deal with this in practice by wrapping the returned result in a call to unlist
):
x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x) # returns 'list', not a vector of length 2
# [1] list
A third peculiar feature of R's list
s: it doesn't seem that they can be members of another ADT, and if you try to do that then the primary container is coerced to a list
. E.g.,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
my intention here is not to criticize the language or how it is documented; likewise, I'm not suggesting there is anything wrong with the list
data structure or how it behaves. All I'm after is to correct is my understanding of how they work so I can correctly use them in my code.
Here are the sorts of things I'd like to better understand:
What are the rules which determine when a function call will return a
list
(e.g.,strsplit
expression recited above)?If I don't explicitly assign names to a
list
(e.g.,list(10,20,30,40)
) are the default names just sequential integers beginning with 1? (I assume, but I am far from certain that the answer is yes, otherwise we wouldn't be able to coerce this type oflist
to a vector w/ a call tounlist
.)Why do these two different operators,
[]
, and[[]]
, return the same result?x = list(1, 2, 3, 4)
both expressions return "1":
x[1]
x[[1]]
why do these two expressions not return the same result?
x = list(1, 2, 3, 4)
x2 = list(1:4)
Please don't point me to the R Documentation (?list
, R-intro
)--I have read it carefully and it does not help me answer the type of questions I recited just above.
(lastly, I recently learned of and began using an R Package (available on CRAN) called hash
which implements conventional map-type behavior via an S4 class; I can certainly recommend this Package.)