3

I have a list of list of lists in R. I want to remove the ones that are of length zero.

What I have tried so far is:

for (i in 1:length(test)) {
  keep <- rep(T, length(test))
  for (j in 1:length(test[[i]])) {
    if (length(test[[i]][[j]]) == 0) {
      keep[[j]] <- F
    }
  }
  test2[i] <- test2[i][keep]
}

Here is some example data (edited):

test <- list("Section 1" = list("A" = list(), "B" = list("1x1" = "23", "1x2" = "24"), C = list("2x1" = "78")),
         "Section 2" = list("A" = list(), "B" = list("1x1" = "23", "1x2" = "24"), C = list("2x1" = "78")))

I would like a way to remove the "A" list in both section 1 and section 2 since both are length 0

user10256905
  • 185
  • 1
  • 9
  • I assume this is a duplicate of the linked post. I can't be 100% sure since you don't provide sample data (screenshots are always a bad idea for sharing data). If the solution(s) from the dupe target do not solve your issue please clarify your post, include sample data and I'm happy to re-open. – Maurits Evers Feb 19 '19 at 22:37
  • @MauritsEvers i have added some sample data, thanks – user10256905 Feb 19 '19 at 23:13
  • 2
    `Filter(function(x) length(x) > 0, test)` works for the sample data you give. – Maurits Evers Feb 19 '19 at 23:18
  • 2
    @MauritsEvers, no need for the anonymous function: `Filter(length, test)` – Parfait Feb 20 '19 at 01:59

3 Answers3

2

Just to wrap things up, and as per my earlier comment you can do

Filter(function(x) length(x) > 0, test)
#$B
#$B$`1x1`
#[1] "23"
#
#$B$`1x2`
#[1] "24"
#
#
#$C
#$C$`2x1`
#[1] "78"

or shorter still (thanks @Parfait)

Filter(length, test)

For a nested list

test <- list("Section 1" = list("A" = list(), "B" = list("1x1" = "23", "1x2" = "24"), C = list("2x1" = "78")), "Section 2" = list("A" = list(), "B" = list("1x1" = "23", "1x2" = "24"), C = list("2x1" = "78")))
lapply(test, function(x) Filter(length, x))
#$`Section 1`
#$`Section 1`$B
#$`Section 1`$B$`1x1`
#[1] "23"
#
#$`Section 1`$B$`1x2`
#[1] "24"
#
#
#$`Section 1`$C
#$`Section 1`$C$`2x1`
#[1] "78"
#
#
#
#$`Section 2`
#$`Section 2`$B
#$`Section 2`$B$`1x1`
#[1] "23"
#
#$`Section 2`$B$`1x2`
#[1] "24"
#
#
#$`Section 2`$C
#$`Section 2`$C$`2x1`
#[1] "78"
Maurits Evers
  • 49,617
  • 4
  • 47
  • 68
  • can this be used in a for loop to get blank lists out of each section? EX data: test <- list("Section 1" = list("A" = list(), "B" = list("1x1" = "23", "1x2" = "24"), C = list("2x1" = "78")), "Section 2" = list("A" = list(), "B" = list("1x1" = "23", "1x2" = "24"), C = list("2x1" = "78"))) – user10256905 Feb 20 '19 at 20:39
  • 1
    No need for an explicit `for` loop @user10256905; just wrap the `Filter` statement inside `lapply`, see my edit. – Maurits Evers Feb 20 '19 at 20:50
  • this requires all the empty lists to be in the same level, in that if you had `test$s = list()` the lapply function will not be able to capture this – Onyambu Feb 20 '19 at 20:55
  • @Onyambu Yes indeed. This is for the sample data OP gives in the comment above. – Maurits Evers Feb 20 '19 at 21:01
  • I still think this is a duplicate post in one way or the other; e.g. for recursive removal of NULL elements, see [Remove NULL elements from list of lists](https://stackoverflow.com/questions/26539441/remove-null-elements-from-list-of-lists) – Maurits Evers Feb 20 '19 at 21:05
1

You can just write your own function:

check = function(x){
  m = lengths(x)>0
  if(is.list(x[m])) lapply(x[m],check) else x
 }

check(test)
$`Section 1`
$`Section 1`$`B`
$`Section 1`$`B`$`1x1`
[1] "23"

$`Section 1`$`B`$`1x2`
[1] "24"


$`Section 1`$C
$`Section 1`$C$`2x1`
[1] "78"



$`Section 2`
$`Section 2`$`B`
$`Section 2`$`B`$`1x1`
[1] "23"

$`Section 2`$`B`$`1x2`
[1] "24"


$`Section 2`$C
$`Section 2`$C$`2x1`
[1] "78"
Onyambu
  • 67,392
  • 3
  • 24
  • 53
0

Claiming this is a duplicate requires the additional knowledge that lists containing only NULL items have length 0. Also requires assuming that NULL and list() are equivalent. Neither of those are necessarily obvious, although testing that proposition with sapply(list(a=NULL), length) shows the first to be the case. The second however appears not the case. Test: identical(NULL, list()) returns FALSE, as do identical(list(NULL), list()) and is.null( list() ).

The answer from Maurits Evers should succeed. This will also succeed:

 test <- test[ sapply(test, length) >0] # `sapply will return a logical vector

> test
$B
$B$`1x1`
[1] "23"

$B$`1x2`
[1] "24"


$C
$C$`2x1`
[1] "78"
IRTFM
  • 258,963
  • 21
  • 364
  • 487