Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R - Appending multiple level 2 elements to each level 1 element of a list by name

I have a list called master, which contains three IDs:

master = list(p1 = list(id = 'abc'), p2 = list(id = 'def'), p3 = list(id = 'ghi'))
str(master)
List of 3
 $ p1:List of 1
  ..$ id: chr "abc"
 $ p2:List of 1
  ..$ id: chr "def"
 $ p3:List of 1
  ..$ id: chr "ghi"

To each level 1 element of this list, I would like to append the corresponding value and radius elements from the val and rad lists:

val = list(p1 = list(value = 5), p3 = list(value = 8))
str(val)
List of 2
 $ p1:List of 1
  ..$ value: num 5
 $ p3:List of 1
  ..$ value: num 8

rad = list(p1 = list(radius = 2), p2 = list(radius = 10))
str(rad)
List of 2
 $ p1:List of 1
  ..$ radius: num 2
 $ p2:List of 1
  ..$ radius: num 10

I have to be careful to match the elements by name because val and rad do not have the same structure as master, i.e. val is missing a slot for p2 and rad is missing a slot for p3.

I can use the following to partially achieve the desired result:

master_final = lapply(X=names(master),function(x, master, val, rad) c(master[[x]], val[[x]], rad[[x]]), master, val, rad)
str(master_final)
List of 3
 $ :List of 3
  ..$ id    : chr "abc"
  ..$ value : num 5
  ..$ radius: num 2
 $ :List of 2
  ..$ id    : chr "def"
  ..$ radius: num 10
 $ :List of 2
  ..$ id   : chr "ghi"
  ..$ value: num 8

But I would like each element of the resulting list to have the same structure, i.e. an id, value and radius slot. I am not sure how to do this in a way that generalises to any number of lists? I don't like having to write [[x]] for each list in the lapply function: function(x, master, val, rad) c(master[[x]], val[[x]], rad[[x]]).

like image 236
user51462 Avatar asked Jan 26 '23 21:01

user51462


1 Answers

One way would be to convert the lists to dataframe and do a merge based on list name. We can then split the dataframe based on list_name.

df1 <- Reduce(function(x, y) merge(x, y, all = TRUE, by = "ind"), 
                     list(stack(master), stack(val),stack(rad)))

names(df1) <- c("list_name", "id", "value", "radius")
lapply(split(df1[-1], df1$list_name), as.list)


#$p1
#$p1$id
#[1] "abc"
#$p1$value
#[1] 5
#$p1$radius
#[1] 2


#$p2
#$p2$id
#[1] "def"
#$p2$value
#[1] NA
#$p2$radius
#[1] 10


#$p3
#$p3$id
#[1] "ghi"
#$p3$value
#[1] 8
#$p3$radius
#[1] NA

This keeps NA values in the list as it is, if we want to remove them the code becomes a bit ugly.

lapply(split(df1[-1], df1$list_name), function(x) 
     {inds <- !is.na(x); as.list(setNames(x[inds], names(x)[inds]))})
like image 105
Ronak Shah Avatar answered Jan 28 '23 09:01

Ronak Shah