Reputation: 275
I'd like to make a new list by modifying an existing list of strings. For an element in the list, eg ..0..fishgold..1..2
, I'd like to extract the digits (0
, 1
, 2
), and make a new list in which the string is assigned the values of the extracted digits ("..0..fishgold..1..2" = 0
, "..0..fishgold..1..2" = 1
, "..0..fishgold..1..2" = 2
).
I've made a couple of attempts, see below code, but I can't get my desired result (goal_list
).
# Load packages
library(stringr)
# Load toy data
df = data.frame("dog..0..brown..1.."=c(1,0,1,7,NA), "cat..0..grey..1..2"=c(5,0,2,1,NA), "..0..fishgold..1..2"=c(1,NA,2,9,NA))
# Existing list
my_list = as.list(colnames(df))
# Attempt 1 function for making new list
my_function = function(old_elem, new_elem){
new_elem = paste(old_elem, "=", str_extract_all(old_elem, "[:digit:]+"))
print(new_elem)
}
# Attempt 2 function for making new list
my_function = function(old_elem){
assign(old_elem, str_extract_all(old_elem, "[:digit:]+"))
print(old_elem)
}
# New list
new_list= as.list(lapply(my_list, my_function))
# Desired new list
goal_list = list("dog..0..brown..1.." = 0,
"dog..0..brown..1.." = 1,
"cat..0..grey..1..2" = 0,
"cat..0..grey..1..2" = 1,
"cat..0..grey..1..2" = 2,
"..0..fishgold..1..2" = 0,
"..0..fishgold..1..2" = 1,
"..0..fishgold..1..2" = 2)
Thanks for any help!
Upvotes: 2
Views: 373
Reputation: 887193
Not clear whether the input should be the my_list
. If the intention is to loop over the list
(instead of unlist
ing), then use map
, apply the str_extract_all
(it can be directly applied on the vector
), enframe
into a two column tibble, unnest
the 'value' column, convert it to integer
(as str_extract_all
returns a list
of strings, deframe
the data.frame to a named vector
, and convert it to list
library(tidyverse)
out <- map_df(my_list, ~ set_names(str_extract_all(.x, "\\d+"), .x) %>%
enframe %>%
unnest(value) %>%
mutate(value = as.integer(value))) %>%
deframe %>%
as.list
all.equal(out, goal_list)
#[1] TRUE
Or in another way, use str_count
to get the number of digits in the name, rep
licate the names
of the data.frame based on that, extract the digits from the names
with str_extract_all
, set its names with the replicated names, and convert the vector
to a list
with as.list
as.list(setNames(as.integer(unlist(str_extract_all(names(df), "\\d+"))),
rep(names(df), str_count(names(df), "\\d+"))))
Or using only base R
l1 <- regmatches(names(df), gregexpr("\\d+", names(df)))
as.list(with(transform(stack(setNames(l1, names(df))),
values = as.integer(values)), setNames(values, ind)))
NOTE: assign
is not needed here as it is to assign values to an identifier object
Upvotes: 1