I have two vectors:
old <- c("a", "b", "c")
new <- c("1", "2", "3")
I want to combine them, so that the elements of the new vector var_names are 'a' = '1', 'b' = '2', 'c' = '3'.
I tried something like this:
for (i in length(new)){
var_names[i] <- paste(old[i], "=", new[i])
}
But it is not working properly. What am I doing wrong here?
EDIT
I was a bit unclear about this. But what I am trying to achieve is;
var_names<- c('a' = '1',
'b' = '2',
'c' = '3')
Reason: https://vincentarelbundock.github.io/modelsummary/articles/modelsummary.html#coef-map
Specifically if you want quotes around a and b
paste0("'",old,"'"," = ","'",new,"'")
[1] "'a' = '1'" "'b' = '2'" "'c' = '3'"
If you want it all in one string
paste0("'",old,"'"," = ","'",new,"'",collapse=", ")
[1] "'a' = '1', 'b' = '2', 'c' = '3'"
Edit: regarding your edit, did you mean this?
names(new)=old
new
a b c
"1" "2" "3"
Update
According to your update, you can use setNames
> setNames(new, old)
a b c
"1" "2" "3"
There are two places you have syntax/logic errors:
You didn't initialize a vector var_name
In for loop, you should use 1:length(new)
Below is a correction and it works
var_names <- c()
for (i in 1:length(new)) {
var_names[i] <- paste(old[i], "=", new[i])
}
and you will see
> var_names
[1] "a = 1" "b = 2" "c = 3"
A more efficient way to achieve the same result is using paste or paste0, e.g.,
> paste(old, new, sep = " = ")
[1] "a = 1" "b = 2" "c = 3"
> paste0(old, " = ", new)
[1] "a = 1" "b = 2" "c = 3"
Related
I learnt how to subset a list based on the values of another list. Am however finding a a challenge when I try to replicate the code in a different context: ( I only need to retain the elements with ":" i.e. a, b and e) How would I have gone about it?
library(stringr)
list1 <- list("a" = "Variable label a: Docket",
"b" = "Variable label b: Boset",
"c" = "Variable label c",
"d" = "Variable label d: Kamba",
"e" = "Variable label e"
)
list2 <- vector("list")
for (i in list1){
if(str_detect(i, ":")){
list2[[i]] <- i
}
}
list1 |> purrr::keep(names(list1) %in% (names(list2) |> stringr::str_sub(-1,-1))) # Thanks to Julian
How about
> Filter(Negate(is.na),lapply(list1,function(x){ifelse(grepl(":",x),x,NA)}))
$a
[1] "Variable label a: Docket"
$b
[1] "Variable label b: Boset"
$d
[1] "Variable label d: Kamba"
R's strsplit drops the last element if "empty" (example 2) but not when occurring first (example 3) or in the middle of the vector to split (example 4).
> unlist(strsplit(x = "1,4", split = ",")) #Example 1
[1] "1" "4"
> unlist(strsplit(x = ",4", split = ",")) #Example 2
[1] "" "4"
> unlist(strsplit(x = "1,", split = ",")) #Example 3
[1] "1"
> unlist(strsplit(x = "1,,,4", split = ",")) #Example 4
[1] "1" "" "" "4"
Is there a way to parse strings that allows keeping the last element if empty after split :
> strmagic(x = "1,", split = ",") #strmagic being the wanted function
[1] "1" ""
A solution with other packages is here (is seems). Can it be done in base R?
UPDATE
Will adding a filler element be necessary ed then a la:
strmagic <- function(v, sep)lapply(v, function(x)head(unlist(strsplit(paste(x, "-", sep = sep), split = sep)), -1))
Weird. This works but is not the most efficient. ZAQ is just a set of random characters
sp <- function( X ){
X <- paste0( X, "ZAQ" )
X <- unlist(strsplit(x = X, split = ","))
X <- gsub( "ZAQ" ,"" ,X)
X
}
sp("1,4")
sp(",4")
sp("1,")
sp("1,,,4")
strmagic <- function(x) unlist(strsplit(sub(",$",",,",x), split = ","))
I saw the clever code submitted by Gabor G. in response to this question about disambiguation of strings. His answer, slightly modified, is:
uniqName <- function(x){
thenames <- ave(x,x,FUN = function(z){
znam <- if (length(z) == 1) z else sprintf("%s%02d", z, seq_along(z))
return(znam)
})
return(thenames)
}
I wanted to go for an "invisible" version of that, and tried to come up with a compact function that would append N spaces to the (N+1)th occurrence of a name.
(Gabor's code calculates an integer and appends that, so the number of characters appended is constant). The best I could do was the following clunky function ("fatit")
spacify <- function (x){
fatit <-function(x){
k = vector(length=length(x))
for(jp in 1:length(x)){
k[jp]=sprintf('%s%s',x[jp],paste0(rep(' ',jp),collapse=''))
}
return(k)
}
spaceOut <- ave(x,x, FUN = function(z) if (length(z) == 1) z else fatit(z) )
return(spaceOut)
}
Is there some cleaner, more compact, way to set the number of characters to append based on length(z) in the fatit function ?
Note:
uniqName(foo)
[1] "a01" "b01" "c01" "a02" "b02" "a03" "c02" "d" "e"
spacify(foo)
[1] "a " "b " "c " "a " "b " "a " "c " "d" "e"
We can take advantage of make.unique by striping the numbers that make the characters unique, and using them (... + 1) as reference as to how many characters to append, i.e.
i1 <- as.numeric(gsub('\\D+', '', make.unique(x)))
i1[is.na(i1)] <- 0 #because where there is no number it returns NA
paste0(x, sapply(i1 + 1, function(i) paste(rep(' ', each = i), collapse = '')))
#[1] "a " "b " "c " "a " "b " "a " "c " "d " "e "
We can take advantage of the stri_pad_right function from stringi:
library(stringi)
f <- function(x){
ave(x, x, FUN = function(z){
if(length(z) == 1) z else stri_pad_right(z, nchar(z[1]) + seq_along(z))
})
}
x <- c('a', 'b', 'c', 'a', 'b', 'a', 'c', 'd', 'e')
f(x)
# [1] "a " "b " "c " "a " "b " "a " "c " "d" "e"
Using stringr::str_pad(..., side = 'right') is conceptually similar.
I was looking for method to iterate over two or more character vectors/list in R simultaneously ex. is it some way to do something like:
foo <- c('a','c','d')
bar <- c('aa','cc','dd')
for(i in o){
print(o[i], p[i])
}
Desired result:
'a', 'aa'
'c', 'cc'
'd', 'dd'
In Python we can do simply:
foo = ('a', 'c', 'd')
bar = ('aa', 'cc', 'dd')
for i, j in zip(foo, bar):
print(i, j)
But can we do this in R?
Like this?
foo <- c('a','c','d')
bar <- c('aa','cc','dd')
for (i in 1:length(foo)){
print(c(foo[i],bar[i]))
}
[1] "a" "aa"
[1] "c" "cc"
[1] "d" "dd"
Works under the condition that the vectors are the same length.
In R, you rather iterate based on the indices than on vectors directly:
for (i in 1:(min(length(foo), length(bar)))){
print(foo[i], bar[i])
}
Another option is to use mapply. This wouldn't make a lot of sense for printing, but I'm assuming you have an interest in doing this for something more interesting than print
foo <- c('a','c','d')
bar <- c('aa','cc','dd')
invisible(
mapply(function(f, b){ print(c(f, b))},
foo, bar)
)
Maybe someone arriving based on the title makes good use of this:
foo<-LETTERS[1:10]
bar<-LETTERS[1:3]
i = 0
for (j in 1:length(foo)){
i = i + 1
if (i > length(bar)){
i = 1
}
print(paste(foo[j],bar[i]) )
}
[1] "A A"
[1] "B B"
[1] "C C"
[1] "D A"
[1] "E B"
[1] "F C"
[1] "G A"
[1] "H B"
[1] "I C"
[1] "J A"
which is "equivalent" to: (using for eases assignments)
suppressWarnings(invisible(
mapply(function(x, y){
print(paste(x, y))},
foo, bar)
))
I have a list in R:
a <- list(n1 = "hi", n2 = "hello")
I would like to append to this named list but the names must be dynamic. That is, they are created from a string (for example: paste("another","name",sep="_")
I tried doing this, which does not work:
c(a, parse(text="paste(\"another\",\"name\",sep=\"_\")=\"hola\"")
What is the correct way to do this? The end goal is just to append to this list and choose my names dynamically.
You could just use indexing with double brackets. Either of the following methods should work.
a <- list(n1 = "hi", n2 = "hello")
val <- "another name"
a[[val]] <- "hola"
a
#$n1
#[1] "hi"
#
#$n2
#[1] "hello"
#
#$`another name`
#[1] "hola"
a[[paste("blah", "ok", sep = "_")]] <- "hey"
a
#$n1
#[1] "hi"
#
#$n2
#[1] "hello"
#
#$`another name`
#[1] "hola"
#
#$blah_ok
#[1] "hey"
You can use setNames to set the names on the fly:
a <- list(n1 = "hi", n2 = "hello")
c(a,setNames(list("hola"),paste("another","name",sep="_")))
Result:
$n1
[1] "hi"
$n2
[1] "hello"
$another_name
[1] "hola"