cylondude
cylondude

Reputation: 1888

remove an element if it exists for all sub-elements of a list

I come across lists that I would like to parse before coercing into a data frame. Sometimes I have lists that have elements I'm not expecting. I'd like to be able to remove all of these unexpected elements by name when they arise. Here is an example of a list with an element wackything that I would like removed without having to call the element index or using a for loop through each sub-element.

my_list <- list(person = list(name = "mike", phone = "111-1111"), 
 person = list(name = "lisa", phone = "222-2222", wackything = "PLEASE REMOVE ME"),
 person = list(name = "kimo", phone = "333-3333"))

I would like my final list to look like this:

final_list <- list(person = list(name = "mike", phone = "111-1111"), 
            person = list(name = "lisa", phone = "222-2222"),
            person = list(name = "kimo", phone = "333-3333"))

so that I can corece it to a data frame using

do.call(rbind, lapply(final_list, rbind))

Upvotes: 6

Views: 1138

Answers (3)

milan
milan

Reputation: 4970

You could try this. The first step is similar to 42-'s answer. We then use sapply, which returns a matrix that we then have to transpose.

my_list <- lapply(my_list, function(x)x[names(x)!= "wackything"])
data.frame(t(sapply(my_list,c)), row.names=NULL)

#  name    phone
#1 mike 111-1111
#2 lisa 222-2222
#3 kimo 333-3333

Another option that gives you the same result.

data.frame(Reduce(rbind, my_list), row.names=NULL)

We use row.names=NULL when building the dataframe. You can do it without but that will result in a warning message related to the duplicate rownames that we have.

Upvotes: 2

IRTFM
IRTFM

Reputation: 263331

I think you have too many rbinds in your anticipated use. See if this is satisfactory:

> rmwac <- function(x) x[ !names(x) %in% "wackything"]
> do.call(rbind, lapply(my_list, rmwac))
       name   phone     
person "mike" "111-1111"
person "lisa" "222-2222"
person "kimo" "333-3333"

Notice that epi10's perfectly fine answer use a negative sign and that was possible because grep returns numeric values and indexing of lists with numeric values is possible. It is not, however, possible to use the negative sign with character and logical values.

Upvotes: 6

eipi10
eipi10

Reputation: 93761

bind_rows from the dplyr package will work even if not all the lists share the same element names. Then you can remove the columns you don't need.

library(dplyr)

df = bind_rows(my_list)
   name    phone       wackything
1  mike 111-1111               NA
2  lisa 222-2222 PLEASE REMOVE ME
3  kimo 333-3333               NA
df = df[ , -grep("wackything", names(df))]
   name    phone
1  mike 111-1111
2  lisa 222-2222
3  kimo 333-3333

Upvotes: 5

Related Questions