3

I'm trying to get a list where each element has a name, by applying a function to each row of a data frame, but can't get the right output.

Assuming this is the function that I want to apply to each row:

format_setup_name <- function(m, v, s) {
 a <- list()
 a[[paste(m, "machines and", v, s, "GB volumes")]] <- paste(num_machines,num_volumes,vol_size,sep="-")
 a
}

If this is the input data frame:

df <- data.frame(m=c(1,2,3), v=c(3,3,3), s=c(15,20,30))

I can't get a list that looks like:

$`1-3-15`
[1] "1 machines and 3 15 GB volumes"

$`2-3-20`
[1] "2 machines and 3 20 GB volumes"

$`3-3-30`
[1] "3 machines and 3 30 GB volumes"

Can someone give me hints how to do it?

Why do I need this? Well, I want to populate selectizeInput in shiny using values coming from the database. Since I'm combining several columns, I need a way to match the selected input with the values.

Nikola Knezevic
  • 789
  • 5
  • 20

2 Answers2

3

This is a good use case for setNames which can add the names() attribute to an object, in place. Also, if you use as.list, you can do this in just one line without any looping:

setNames(as.list(paste(df$m, ifelse(df$m == 1, "machine", "machines"), "and", df$v, df$s, "GB volumes")), paste(df$m,df$v,df$s,sep="-"))
# $`1-3-15`
# [1] "1 machine and 3 15 GB volumes"
# 
# $`2-3-20`
# [1] "2 machines and 3 20 GB volumes"
# 
# $`3-3-30`
# [1] "3 machines and 3 30 GB volumes"
Thomas
  • 43,637
  • 12
  • 109
  • 140
  • Thanks, this works nicely. How could I add an `ifelse` to the story, so I could have in `paste` something like: `paste(m, ifelse(m==1,"machine","machines"), ...`? – Nikola Knezevic Apr 07 '15 at 21:56
  • Pretty much just like that, actually. Just replace the current "machines" with that ifelse statement. – Thomas Apr 08 '15 at 05:16
  • I was seeing an error when doing exactly that (something as ' argument "nm" is missing, with no default'), but realized it was due to misplaced parens. Now it works like a charm, thanks. – Nikola Knezevic Apr 08 '15 at 06:25
2

Thomas has already found a pretty neat solution to your problem (and in one line, too!). But I'll just show you how you could have succeeded with the approach you first tried:

# We'll use the same data, this time called "dat" (I avoid calling 
# objects `df` because `df` is also a function's name)
dat <- data.frame(m = c(1,2,3), v = c(3,3,3), s = c(15,20,30))

format_setup_name <- function(m, v, s) {
    a <- list() # initialize the list, all is well up to here

    # But here we'll need a loop to assign in turn each element to the list
    for(i in seq_along(m)) {
        a[[paste(m[i], v[i], s[i], sep="-")]] <- 
                   paste(m[i], "machines and", v[i], s[i], "GB volumes")
    }
    return(a)
}

Note that what goes inside the brackets is the name of the element, while what's at the right side of the <- is the content to be assigned, not the inverse as your code was suggesting.

So let's try it:

my.setup <- format_setup_name(dat$m, dat$v, dat$s)

my.setup

# $`1-3-15`
# [1] "1 machines and 3 15 GB volumes"
#
# $`2-3-20`
# [1] "2 machines and 3 20 GB volumes"
#
# $`3-3-30`
# [1] "3 machines and 3 30 GB volumes"

Everything seems nice. Just one thing to note: with the $ operator, you'll need to use single or double quotes to access individual items by their names:

my.setup$"1-3-15" # my.setup$1-3-15 won't work
# [1] "1 machines and 3 15 GB volumes"

my.setup[['1-3-15']] # equivalent
# [1] "1 machines and 3 15 GB volumes"

Edit: lapply version

Since loops have really fallen out of favor, here's a version with lapply:

format_setup_name <- function(m, v, s) {
    a <- lapply(seq_along(m), function(i) paste(m[i], "machines and", v[i], s[i], "GB volumes"))
    names(a) <- paste(m, v, s, sep="-")
    return(a)
}
Dominic Comtois
  • 10,230
  • 1
  • 39
  • 61