Leif Andersen
Leif Andersen

Reputation: 22352

Generating named lists in R

If I want to create a named list, where I have named literals, I can just do this:

list(foo=1,bar=2,baz=3)

If instead I want to make a list with arbitrary computation, I can use lapply, so for example:

lapply(list(1,2,3), function(x) x)

However, the list generated by lapply will always be a regular numbered list. Is there a way I can generate a list using a function like lapply with names.

My idea is something along the lines of:

lapply(list("foo","bar","baz"), function(key) {key=5}

==>

list(foo=5,bar=5,baz=5)

That way I don't have to have the keys and values as literals.

I do know that I could do this:

res = list()
for(key in list("foo","bar","baz") {
    res[key] <- 5;
}

But I don't like how I have to create a empty list and mutate it to fill it out.

Edit: I would also like to do some computation based on the key. Something like this:

lapply(c("foo","bar","baz"), function(key) {paste("hello",key)=5})

Upvotes: 4

Views: 1344

Answers (4)

jan-glx
jan-glx

Reputation: 9536

If you really want to set the name of the returned list elements within the code computing each value, you have to return named length-1 lists from that code instead and get rid of the outer list layer. This can readily be achieved using sapply:

x <- list(foo=1, bar=2, baz=3)
x
#> $foo
#> [1] 1
#> 
#> $bar
#> [1] 2
#> 
#> $baz
#> [1] 3
sapply(names(x), function(key) setNames(list(x[[key]]^2), paste0(key, "_squared")), USE.NAMES = FALSE)
#> $foo_squared
#> [1] 1
#> 
#> $bar_squared
#> [1] 4
#> 
#> $baz_squared
#> [1] 9

To get a named list in the first place rlang::dots_list(..., .named=TRUE) can sometimes be helpful (I am still searching for an equivalent in base R):

foo <- 1
bar <- 2
baz <- 3

rlang::dots_list(foo, bar, baz, .named=TRUE)
#> $foo
#> [1] 1
#> 
#> $bar
#> [1] 2
#> 
#> $baz
#> [1] 3

Upvotes: 0

CubicInfinity
CubicInfinity

Reputation: 312

Adapting MrFlick's use of setNames, here is a simple function that can do both:

list_zip = function(names, values) {
  values = rep(values, length.out = length(names))
  setNames(as.list(values), names)
}

list_zip(c("foo", "bar", "baz"), c(1,2,3))

list_zip(c("foo", "bar", "baz"), 5)

Upvotes: 0

MrFlick
MrFlick

Reputation: 206446

If your list has names in the first place, lapply will preserve them

lapply(list(a=1,b=2,c=3), function(x) x)

or you can set names before or after with setNames()

#before
lapply(setNames(list(1,2,3),c("foo","bar","baz")), function(x) x)

#after
setNames(lapply(list(1,2,3), function(x) x), c("foo","bar","baz"))

One other "option" is Map(). Map will try to take the names from the first parameter you pass in. You can ignore the value in the function and use it only for the side-effect of keeping the name

Map(function(a,b) 5, c("foo","bar","baz"), list(1:3))

But names cannot be changed during lapply/Map steps. They can only be copied from another location. if you need to mutate names, you'll have to do that as a separate step.

Upvotes: 3

BrodieG
BrodieG

Reputation: 52677

sapply will use its argument for names if it is a character vector, so you can try:

sapply(c("foo","bar","baz"), function(key) 5, simplify=F)

Which produces:

$foo
[1] 5

$bar
[1] 5

$baz
[1] 5

Upvotes: 5

Related Questions