Reputation: 187
Although the title looks easy, I have not found an answer here on StackOverflow. My problem is: I have a list of some elements. And each elemnt of the list also contains a list filled with some strings. That list inside list contains values that are empty (''), not null. I would like to remove the rows inside that list that are empty. How one can do it? I guess lapply would do the trick but I am not sure how to use it there.
Would be grateful for an advice! Thanks a lot!
EDIT: So my output looks like this
$'1'
1 text1
2 text2
3
4 text3
5 text4
$'2'
1 text1
2
3 text2
4
5 text3
My goal is to delete those rows that are empty so that it looked like this:
$'1'
1 text1
2 text2
3 text3
4 text4
$'2'
1 text1
2 text2
3 text3
Upvotes: 2
Views: 2077
Reputation: 35314
This is a surprisingly tough problem. First I synthesized some test data:
l <- list(list('a','b','','c','','d'),list('','e',''));
l;
## [[1]]
## [[1]][[1]]
## [1] "a"
##
## [[1]][[2]]
## [1] "b"
##
## [[1]][[3]]
## [1] ""
##
## [[1]][[4]]
## [1] "c"
##
## [[1]][[5]]
## [1] ""
##
## [[1]][[6]]
## [1] "d"
##
##
## [[2]]
## [[2]][[1]]
## [1] ""
##
## [[2]][[2]]
## [1] "e"
##
## [[2]][[3]]
## [1] ""
##
##
Here's my solution:
matches <- do.call(rbind,lapply(seq_along(l),function(li) cbind(li,which(do.call(c,l[[li]])==''))));
matches;
## li
## [1,] 1 3
## [2,] 1 5
## [3,] 2 1
## [4,] 2 3
invisible(apply(matches[rev(seq_len(nrow(matches))),],1,function(lri) l[[lri]] <<- NULL));
l;
## [[1]]
## [[1]][[1]]
## [1] "a"
##
## [[1]][[2]]
## [1] "b"
##
## [[1]][[3]]
## [1] "c"
##
## [[1]][[4]]
## [1] "d"
##
##
## [[2]]
## [[2]][[1]]
## [1] "e"
##
##
This uses recursive list indexing. This type of indexing is documented in Extract or Replace Parts of an Object:
[[ can be applied recursively to lists, so that if the single index i is a vector of length p, alist[[i]] is equivalent to alist[[i1]]...[[ip]] providing all but the final indexing results in a list.
This solution also leverages the fact that assigning NULL
to a list component deletes that component. From the same documentation page:
Note that in all three kinds of replacement, a value of NULL deletes the corresponding item of the list. To set entries to NULL, you need x[i] <- list(NULL).
It was also important to delete components with higher indexes first; otherwise the precomputed higher indexes would be invalidated by the deletion of lower indexes. Hence I ran apply()
over matches[rev(seq_len(nrow(matches))),]
instead of matches
.
Actually there's another easier way which involves rebuilding the list, omitting undesired elements:
lapply(l,function(l2) l2[do.call(c,l2)!=''])
## [[1]]
## [[1]][[1]]
## [1] "a"
##
## [[1]][[2]]
## [1] "b"
##
## [[1]][[3]]
## [1] "c"
##
## [[1]][[4]]
## [1] "d"
##
##
## [[2]]
## [[2]][[1]]
## [1] "e"
##
##
Upvotes: 0
Reputation: 162
You're correct in that you can use lapply
here.
test <- list(c("text1", "text2", "", "text3", "text4"),
c("text1", "", "text2", "", "text3"))
lapply(1:length(test), function(x) test[[x]][test[[x]] != ""])
[[1]]
[1] "text1" "text2" "text3" "text4"
[[2]]
[1] "text1" "text2" "text3"
Upvotes: 7