1

Given is the list below. This list contains character vectors of variable length.

l1 <- list("a" = c("x1", "x2", "x3"),
           "b" = c("x4", "x5"),
           "c" = c("x6", "x7", "x8", "x9"))

> l1
$a
[1] "x1" "x2" "x3"

$b
[1] "x4" "x5"

$c
[1] "x6" "x7" "x8" "x9"

The desired output, let's call it l2, is the following:

$a
[1] 1 1 1

$b
[1] 2 2

$c
[1] 3 3 3 3

This output has the following characteristics:

  • l2 is a named list in which the names of the original list l1 are preserved.
  • The length of list l2 is the same as list l1.
  • The order of list elements in l1 is preserved in l2.
  • l2 contains vectors with repeating integers. The length of each vector in l2 is the same as the corresponding character vector in l1.

Part of solution

I found this post in which the statement below helped me to construct a partial solution.

The usual work-around is to pass it the names or indices of the vector instead of the vector itself.

l2 <- lapply(X = seq_along(l1), 
             FUN = function(x) rep(x, times = length(l1[[x]]))) 
l2
[[1]]
[1] 1 1 1

[[2]]
[1] 2 2

[[3]]
[1] 3 3 3 3

All criteria are met, except that the names are not preserved in l2.

How can I fix this in one go (not using a seperate statement after the lapply statement)?

PeterD
  • 429
  • 2
  • 13

3 Answers3

2

After you run your above code,, just add the code below:-

names(l2) <- names(l1)

This will assign the names of l1 to l2, and hence, you will have the same names.

Edit: You can't achieve this with lapply, but you can do it with sapply by doing the following the following:-

l2 <- sapply(X = names(l1), 
         FUN = function(x) rep(which(names(l1) == x), times = length(l1[[x]])))

l2
$a
[1] 1 1 1

$b
[1] 2 2

$c
[1] 3 3 3 3

Turns out, if X argument of sapply is character vector, it will return the list by using X as names of the returned list.

Shawn Brar
  • 1,346
  • 3
  • 17
2

You can try the following base R option, using lengths + rep + relist like below

> relist(rep(seq_along(l1), lengths(l1)), l1)
$a
[1] 1 1 1

$b
[1] 2 2

$c
[1] 3 3 3 3
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81
1

You can use [] to preserve the names the list.

l1[] <- lapply(seq_along(l1), function(x) rep(x, times = length(l1[[x]]))) 
l1

#$a
#[1] 1 1 1

#$b
#[1] 2 2

#$c
#[1] 3 3 3 3

Another solution with Map.

l1[] <- Map(rep, seq_along(l1), lengths(l1))

In case you want to have another objects l2 keeping l1 as it is, create a copy of l1 in l2 by doing l2 <- l1.

Ronak Shah
  • 377,200
  • 20
  • 156
  • 213