mzakaria
mzakaria

Reputation: 649

applying if statement for list within list in r

I am trying to run over a list of lists. Each line has 29 lists, and each list has 6 numbers stored as strings. An example looks like the following

dput(M[6000])

list(list(c("0", "1", "19", "785", "-3150", "0.90"), c("4", "2",  "-1", "5550", "4400", "0.00"), 
    c("1", "3", "6", "3319", "-2558",  "1.49"), c("1", "4", "1", "4573", "-435", "1.24"), 
    c("0", "5",  "6", "1137", "-2828", "2.28"), c("0", "6", "24", "1668", "-1143",  "2.76"), 
    c("1", "7", "2", "2859", "-720", "1.40"), c("1", "8",  "23", "420", "-3346", "1.57"), 
    c("1", "9", "26", "2290", "752",  "1.23"), c("1", "10", "8", "1208", "-2842", "2.14"), 
    c("0", "11",  "11", "-219", "-374", "1.26"), c("0", "12", "3", "-69", "-2403",  "2.24"), 
    c("0", "13", "1", "-3488", "-830", "0.17"), c("1", "14",  "7", "2102", "-1404", "1.24"), 
    c("1", "15", "3", "1746", "-3481",  "1.59"), c("3", "16", "0", "720", "-1425", "0.47"), 
    c("1", "17",  "9", "170", "-2257", "3.14"), c("0", "18", "5", "-351", "-1564",  "1.08"), 
    c("4", "19", "-1", "5550", "4400", "0.00"), c("3", "20",  "1", "3304", "-3448", "1.78"), 
    c("1", "21", "4", "2289", "-1873",  "3.13"), c("0", "22", "2", "175", "-3080", "1.28"), 
    c("1", "23",  "12", "877", "140", "1.52"), c("0", "24", "8", "871", "-1933",  "4.11"), 
    c("0", "25", "9", "3185", "-2548", "1.50"), c("4", "26",  "-1", "5550", "4400", "0.00"), 
    c("3", "27", "2", "-290", "3415",  "0.56"), c("4", "28", "-1", "5550", "4400", "0.00"), 
    c("0", "29",  "32", "2176", "-2145", "1.58")))

For each line, I am trying to run over the 29 lists and save only the lists that has the 3rd element equal to 4. For one line it would be:

if(as.numeric(M[[6000]][[1]][3]) == 4) M[[6000]][[1]] 

I have tried something down the line of

MP4 <- lapply(M, function(x) if(as.numeric(x[[1]][3]) == 4) x[[1]])

without luck.

Upvotes: 0

Views: 151

Answers (3)

alistaire
alistaire

Reputation: 43334

To use base R, you can use Filter:

lapply(M, Filter, f = function(x){x[[3]] == '4'})

## [[1]]
## [[1]][[1]]
## [1] "1"     "21"    "4"     "2289"  "-1873" "3.13" 

To filter out empty elements of a larger list, Filter twice:

# using @apom's data from above
Filter(function(x){length(x) != 0}, 
       lapply(M_2, Filter, f = function(x){x[[3]] == '4'}))

## [[1]]
## [[1]][[1]]
## [1] "1"     "21"    "4"     "2289"  "-1873" "3.13" 

Upvotes: 3

Aur&#232;le
Aur&#232;le

Reputation: 12819

The purrr package is very good at those kinds of problems:

library(purrr)
M %>% 
  map(.f = keep, .p = ~ .x[[3]] == "4")
# [[1]]
# [[1]][[1]]
# [1] "1"     "21"    "4"     "2289"  "-1873" "3.13" 

Edit per your comment:

Let's make another list, M_2, to illustrate the issue:

M_2 <- c(M, list(list()))
M_2 %>% 
  map(.f = keep, .p = ~ .x[[3]] == "4")
# [[1]]
# [[1]][[1]]
# [1] "1"     "21"    "4"     "2289"  "-1873" "3.13" 
# 
# 
# [[2]]
# list()

Then simply discard lists that are equal to list():

M_2 %>% 
  map(.f = keep, .p = ~ .x[[3]] == "4") %>% 
  discard(identical, list())
# [[1]]
# [[1]][[1]]
# [1] "1"     "21"    "4"     "2289"  "-1873" "3.13" 

Upvotes: 4

s_baldur
s_baldur

Reputation: 33488

Here is one way to loop through the list and create a new one which saves elements according to your criteria of third element equal to 4.

new_dl <- list()
j <- 1L
for (l in 1L:length(dl)) {
  new_dl[[l]] <- list()
  for (i in 1L:length(dl[[1]]))
    if (dl[[l]][[i]][3] == 4) {
      new_dl[[l]][[j]] <- dl[[l]][[i]]
      j <- j + 1L
    }
  j <- 1L
}  

Upvotes: 0

Related Questions