Custom function to manipulate list not working in mutate when it does work in isolation

Viewed 30

I have a data frame where one column is a list of keystrokes. I have then created a custom function to convert the list of keystrokes into the final word. As a concrete example, I start with this data:

dat <- data.frame(word=c(1,1,2,2,2,2),
                  key=c("a","b","a","b","Backspace","c"))

I then call the below to create a column with lists of keystrokes (the repeats are intentional, as I need to preserve every individual keystroke):

dat <- dat %>% 
  group_by(word) %>% 
  mutate(key_list = I(list(key))) %>% 
  ungroup()

This creates a data frame where the first 2 rows have key_list = a,b and the last 4 rows are a,b,Backspace,c. This is to be expected.

I then want to concatenate the key_list into a single string. However, I've created the custom function below because when iterating through the list, if I encounter "Backspace", I need to delete the last keystroke. Therefore, the function looks like:

library(tidyverse)

word_list_to_final_str <- function(word_list) {
  final_list = c()
  for (i in word_list) {
    if (tolower(i) %in% c(letters)) {
      final_list <- c(final_list, tolower(i))
    }
    else if (i == 'Backspace') {
      final_list <- head(final_list, -1)
    }
  }
  final_str <- str_c(final_list,collapse="")
  return(final_str)
}

When I run this function in isolation, it returns the correct result:

19:13:11> word_list_to_final_str(c("a","b","Backspace","c"))
[1] "ac"

However, when I run the function within mutate(), I get an additional column with all "abababbackspacecabbackspacecabbackspacecabbackspacec".

dat <- dat %>% 
  mutate(final_word = word_list_to_final_str(key_list))

Clearly somewhere the function is not clearing the list, but I cannot understand where. How can I change the function?

2 Answers

Since you have a list column, you need to map over that list. You can use purrr to make that pretty easy

dat %>% 
  group_by(word) %>% 
  mutate(key_list = I(list(key))) %>% 
  mutate(final_word = purrr:::map_chr(key_list, word_list_to_final_str))

An every more "tidy" way to do this would be to use tidyr::nest as well

dat %>% 
  tidyr::nest(key_list = c(key)) %>% 
  mutate(final_word = purrr::map_chr(key_list, ~word_list_to_final_str(.x$key)))

Without writing the function, you could do:

dat %>%
  group_by(word)%>%
  mutate(final_word = str_remove(str_c(key, collapse = ''), ".Backspace"))

# A tibble: 6 x 3
# Groups:   word [2]
   word key       final_word
  <dbl> <chr>     <chr>     
1     1 a         ab        
2     1 b         ab        
3     2 a         ac        
4     2 b         ac        
5     2 Backspace ac        
6     2 c         ac 
Related