0

Firstly, apologies that this is a long post - it's quite detailed and the difference between the result that I would like to achieve and the one I have been able to achieve is subtle but significant.

The core issue is that I want to make a recursive list in R, one in which the list elements increment upwards.

The core reason is that right now I am trying to write a script to automatically create a lookup table variable in Google Tag Manager via the GTM API.

The documentation for creating variables is here: https://developers.google.com/tag-manager/api/v2/reference/accounts/containers/workspaces/variables/create

The desired workflow is this:

  1. Pull mapping table from a Google Sheet using the googlesheets package
  2. Convert relevant columns into list format
  3. Upload final version to GTM via the GTM API

The issue I am having at the moment is step 2, creating the list in the right way. I have figured out a way to reverse engineer the list that the GTM API provides, but the method involves manually adding each row from the values to be mapped as a new list, which makes no sense when you have variable row lengths (besides being a pain in the ass).

This is what a working end result looks like (this was made with the manual method); this is called post_body1:

$name
[1] "test1"

$type
[1] "smm"

$parameter
$parameter[[1]]
$parameter[[1]]$type
[1] "boolean"

$parameter[[1]]$key
[1] "setDefaultValue"

$parameter[[1]]$value
[1] "true"


$parameter[[2]]
$parameter[[2]]$type
[1] "template"

$parameter[[2]]$key
[1] "input"

$parameter[[2]]$value
[1] "{{Event Name}}"


$parameter[[3]]
$parameter[[3]]$type
[1] "list"

$parameter[[3]]$key
[1] "map"

$parameter[[3]]$list
$parameter[[3]]$list[[1]]
$parameter[[3]]$list[[1]]$type
[1] "map"

$parameter[[3]]$list[[1]]$map
$parameter[[3]]$list[[1]]$map[[1]]
$parameter[[3]]$list[[1]]$map[[1]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[1]]$key
[1] "key"

$parameter[[3]]$list[[1]]$map[[1]]$value
[1] "explosion"

$parameter[[3]]$list[[1]]$map[[2]]
$parameter[[3]]$list[[1]]$map[[2]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[2]]$key
[1] "value"

$parameter[[3]]$list[[1]]$map[[2]]$value
[1] "volcano"

$parameter[[3]]$list[[2]]
$parameter[[3]]$list[[2]]$type
[1] "map"

$parameter[[3]]$list[[2]]$map
$parameter[[3]]$list[[2]]$map[[1]]
$parameter[[3]]$list[[2]]$map[[1]]$type
[1] "template"

$parameter[[3]]$list[[2]]$map[[1]]$key
[1] "key"

$parameter[[3]]$list[[2]]$map[[1]]$value
[1] "flood"

$parameter[[3]]$list[[2]]$map[[2]]
$parameter[[3]]$list[[2]]$map[[2]]$type
[1] "template"

$parameter[[3]]$list[[2]]$map[[2]]$key
[1] "value"

$parameter[[3]]$list[[2]]$map[[2]]$value
[1] "tsunami"

$parameter[[3]]$list[[3]]
$parameter[[3]]$list[[3]]$type
[1] "map"

$parameter[[3]]$list[[3]]$map
$parameter[[3]]$list[[3]]$map[[1]]
$parameter[[3]]$list[[3]]$map[[1]]$type
[1] "template"

$parameter[[3]]$list[[3]]$map[[1]]$key
[1] "key"

$parameter[[3]]$list[[3]]$map[[1]]$value
[1] "drought"

$parameter[[3]]$list[[3]]$map[[2]]
$parameter[[3]]$list[[3]]$map[[2]]$type
[1] "template"

$parameter[[3]]$list[[3]]$map[[2]]$key
[1] "value"

$parameter[[3]]$list[[3]]$map[[2]]$value
[1] "heatwave"

Here are some variables:

variable_name <- 'test1'
map_values <- tibble(key=c('explosion','flood','drought'),
                 value = c('volcano','tsunami','heatwave'))
var_code <- 'smm'
set_default <- TRUE
map_input <- 'Event Name'

And here's the code for creating the list above - as mentioned, this isn't a scalable solution.

post_body1 <- list(name = variable_name,
              type = var_code,
              parameter = list(
                list(type = 'boolean',
                     key = 'setDefaultValue',
                     value = ifelse(set_default == TRUE,'true','false')),
                list(type = 'template',
                     key = 'input',
                     value = paste('{{',map_input,'}}',sep='')),
                list(type = 'list',
                     key = 'map',
                     list = list(list(
                       type = 'map',
                       map = list(list(
                         type = 'template',
                                 key = 'key',
                                 value = map_values$key[[1]]),
                            list(type = 'template',
                                 key = 'value',
                                 value = map_values$value[[1]])
                )

              ),
              list(type = 'map',
                   map = list(list(
                     type = 'template',
                key = 'key',
                value = map_values$key[[2]]),
              list(type = 'template',
                   key = 'value',
                   value = map_values$value[[2]])
              )
            ),
            list(type = 'map',
                 map = list(list(
                   type = 'template',
              key = 'key',
              value = map_values$key[[3]]),
            list(type = 'template',
                 key = 'value',
                 value = map_values$value[[3]])
            )
          )
              )
                     )
)
)

So I tried to create a new solution that can handle the post_body1$parameter[[3]]$list creation programmatically, and I have almost done it but not quite, and also I think it's not a very elegant solution, and there's probably a better way to create it using something like the purrr package.

test_list <- list()

for (i in 1:nrow(map_values)) {

  newlist <- list(
    type = 'map',
    map = list(list(
      type = 'template',
          key = 'key',
          value = map_values$key[[i]]),
     list(type = 'template',
          key = 'value',
          value = map_values$value[[i]])
  )

  )

  test_list <- c(test_list,newlist)

}

post_body2 <- list(name = variable_name,
              type = var_code,
              parameter = list(
                list(type = 'boolean',
                     key = 'setDefaultValue',
                     value = ifelse(set_default == TRUE,'true','false')),
                list(type = 'template',
                     key = 'input',
                     value = paste('{{',map_input,'}}',sep='')),
                list(type = 'list',
                     key = 'map',
                   list = list(test_list))
)
)

So this method produces something that is quite similar to the brute force method above, with one crucial difference: the index for post_body2$parameter[[3]]$list does not increment in the same way as post_body1$parameter[[3]]$list; here's what the post_body2$parameter[[3]]$list looks like:

$parameter[[3]]$list
$parameter[[3]]$list[[1]]
$parameter[[3]]$list[[1]]$type
[1] "map"

$parameter[[3]]$list[[1]]$map
$parameter[[3]]$list[[1]]$map[[1]]
$parameter[[3]]$list[[1]]$map[[1]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[1]]$key
[1] "key"

$parameter[[3]]$list[[1]]$map[[1]]$value
[1] "explosion"

$parameter[[3]]$list[[1]]$map[[2]]
$parameter[[3]]$list[[1]]$map[[2]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[2]]$key
[1] "value"

$parameter[[3]]$list[[1]]$map[[2]]$value
[1] "volcano"

$parameter[[3]]$list[[1]]$type
[1] "map"

$parameter[[3]]$list[[1]]$map
$parameter[[3]]$list[[1]]$map[[1]]
$parameter[[3]]$list[[1]]$map[[1]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[1]]$key
[1] "key"

$parameter[[3]]$list[[1]]$map[[1]]$value
[1] "flood"


$parameter[[3]]$list[[1]]$map[[2]]
$parameter[[3]]$list[[1]]$map[[2]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[2]]$key
[1] "value"

$parameter[[3]]$list[[1]]$map[[2]]$value
[1] "tsunami"

$parameter[[3]]$list[[1]]$type
[1] "map"

$parameter[[3]]$list[[1]]$map
$parameter[[3]]$list[[1]]$map[[1]]
$parameter[[3]]$list[[1]]$map[[1]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[1]]$key
[1] "key"

$parameter[[3]]$list[[1]]$map[[1]]$value
[1] "drought"

$parameter[[3]]$list[[1]]$map[[2]]
$parameter[[3]]$list[[1]]$map[[2]]$type
[1] "template"

$parameter[[3]]$list[[1]]$map[[2]]$key
[1] "value"

$parameter[[3]]$list[[1]]$map[[2]]$value
[1] "heatwave"

If you are still reading, what is the method by which I can create this list in the desired format?

Edit: on the request of @muddy-cloudskipper I am adding the dput for the desired output, which is this:

list(name = "test1", type = "smm", parameter = list(list(type = "boolean", 
key = "setDefaultValue", value = "true"), list(type = "template", 
key = "input", value = "{{Event Name}}"), list(type = "list", 
key = "map", list = list(list(type = "map", map = list(list(
    type = "template", key = "key", value = "explosion"), 
    list(type = "template", key = "value", value = "volcano"))), 
    list(type = "map", map = list(list(type = "template", 
        key = "key", value = "flood"), list(type = "template", 
        key = "value", value = "tsunami"))), list(type = "map", 
        map = list(list(type = "template", key = "key", value = "drought"), 
            list(type = "template", key = "value", value = "heatwave")))))))

What I want to create is a function that will lead to that output; it would be something like this, although this isn't quite right.

function (variable_name, var_code, set_default, map_input, map_values) 
{
test_list <- list()
for (i in 1:nrow(map_values)) {
    newlist <- list(type = "map", map = list(list(type = "template", 
        key = "key", value = map_values$key[[i]]), list(type = "template", 
        key = "value", value = map_values$value[[i]])))
    test_list <- c(test_list, newlist)
}
post_body <- list(name = variable_name, type = var_code, 
    parameter = list(list(type = "boolean", key = "setDefaultValue", 
        value = ifelse(set_default == TRUE, "true", "false")), 
        list(type = "template", key = "input", value = paste("{{", 
            map_input, "}}", sep = "")), list(type = "list", 
            key = "map", list = list(test_list))))
return(post_body)
}
Randall Helms
  • 849
  • 5
  • 15
  • So as I understand `post_body1` is your desired output, can you share a `dput` of your input as well ? – moodymudskipper Jan 24 '19 at 14:51
  • @Moody_Mudskipper The third code block is the input that creates `post_body1` - is that what you are looking for? Or do you want me to directly input the `dput()` result from the console? To clarify, the desired output is in the first text block, and `post_body1` makes it, but it is not scalable, therefore I want a solution somewhat like `post_body2` (or better!) – Randall Helms Jan 24 '19 at 15:47
  • What I would like is to have a dput of 2 objects, `input` and `output`, so that the problem is how to get `output <- some_function(input)` and the solution is the definition of `some_function` . I think `post_body1` is `output`, but I'm not sure what is `input`. – moodymudskipper Jan 24 '19 at 15:58
  • this might help : https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example – moodymudskipper Jan 24 '19 at 15:59
  • OK, I've updated the post with what I think you are looking for. Let me know if that helps. – Randall Helms Jan 24 '19 at 16:17

1 Answers1

1

I think this should work, I build separately the variable part of the list and then stick everything together :

  sublist <- unname(Map(function(key,value) list(type = "map",
                                                 map=list(
    list(type="template",
         key ="key",
         value = key),
    list(type = "template",
         key = "value",
         value = value))), map_values$key, map_values$value))

  res <- list(name = variable_name,
              type = var_code,
              parameter = c(list(
                list(type = 'boolean',
                     key = 'setDefaultValue',
                     value = ifelse(set_default == TRUE,'true','false')),
                list(type = 'template',
                     key = 'input',
                     value = paste('{{',map_input,'}}',sep=''))),
                list(list(type = "list",
                     key = "map",
                     list=sublist))))

  identical(res,output)
  # [1] TRUE
moodymudskipper
  • 46,417
  • 11
  • 121
  • 167