'How to access very first object in differently deep nested lists?

I need to access the first element of a list. The problem is that the lists vary in the way how deep they are nested. Here is an example:

list1 <- list(ts(1:100),
              list(1:19,
                   factor(letters)))

list2 <- list(list(list(ts(1:100), data.frame(a= rnorm(100))),
                   matrix(rnorm(10))),
              NA)

My expected output is to get the time seriests(1:100) for both lists, i.e. list1[[1]] and list2[[1]][[1]][[1]]. I've tried different stuff, among others lapply(list2, `[[`, 1) which here does not work here.



Solution 1:[1]

Another base R solution - you could do this with a recursive function:

list1 <- list(ts(1:100),
              list(1:19,
                   factor(letters)))

list2 <- list(list(list(ts(1:100), data.frame(a= rnorm(100))),
                   matrix(rnorm(10))),
              NA)


recursive_fun <- function(my_list) {
  
  if (inherits(my_list, 'list')) {
    Recall(my_list[[1]])
  } else {
    my_list
  }
  
}

Output:

> recursive_fun(list1)
Time Series:
Start = 1 
End = 100 
Frequency = 1 
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30
 [31]  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60
 [61]  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
 [91]  91  92  93  94  95  96  97  98  99 100

> recursive_fun(list2)
Time Series:
Start = 1 
End = 100 
Frequency = 1 
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30
 [31]  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60
 [61]  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
 [91]  91  92  93  94  95  96  97  98  99 100

Solution 2:[2]

You can use rrapply::rrapply:

library(rrapply)
firstList1 <- rrapply(list1, how = "flatten")[[1]]
firstList2 <- rrapply(list2, how = "flatten")[[1]]

all.equal(firstList1, firstList2)
# [1] TRUE

output

> rrapply(list1, how = "flatten")[[1]]

Time Series:
Start = 1 
End = 100 
Frequency = 1 
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26
 [27]  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52
 [53]  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78
 [79]  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100

Solution 3:[3]

We can combine while loop with purrr::pluck. This avoids an actual recursive function, which could be a problem with deeply nested lists.

library(purrr)

get_list <- function(x){
        while(is.list(x)){
                x <- pluck(x, 1)
                }
        x
}

We can also set the function to be called 'recursively' until it finds a "ts" class object:

get_list <- function(x){
        while(!is(x, 'ts')){
                x <- pluck(x, 1)
                }
        x
}

output


get_list(list2)

Time Series:
Start = 1 
End = 100 
Frequency = 1 
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45
 [46]  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
 [91]  91  92  93  94  95  96  97  98  99 100

Solution 4:[4]

Another possible solution, using purrr::pluck and purrr::vec_depth:

library(tidyverse)

pluck(list1, !!!(rep(1, vec_depth(list1)-2) %>% as.list()))

#> Time Series:
#> Start = 1 
#> End = 100 
#> Frequency = 1 
#>   [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
#>  [19]  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36
#>  [37]  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54
#>  [55]  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72
#>  [73]  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
#>  [91]  91  92  93  94  95  96  97  98  99 100

pluck(list2, !!!(rep(1, vec_depth(list2)-2) %>% as.list()))

#> Time Series:
#> Start = 1 
#> End = 100 
#> Frequency = 1 
#>   [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
#>  [19]  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36
#>  [37]  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54
#>  [55]  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72
#>  [73]  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90
#>  [91]  91  92  93  94  95  96  97  98  99 100

Solution 5:[5]

base R solution I've just got the idea for a pretty simple function. It is a while loop that runs until the element is not a list.

myfun <- function(mylist){
  dig_deeper <- TRUE
  while(dig_deeper){
    mylist<- my_list[[1]]
    dig_deeper <- is.list(mylist)
  }
  return(mylist)
}

It works as expected

> myfun(list1)
Time Series:
Start = 1 
End = 100 
Frequency = 1 
  [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24
 [25]  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48
 [49]  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72
 [73]  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96
 [97]  97  98  99 100

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4 PaulS
Solution 5