2

I want to interleave interElem after every 2 list elements.

Data:

listi     <- c(rbind(letters[1:4], list(c(13,37))))

interElem <- c("inter","leavistan")

looks like:

> listi
[[1]]
[1] "a"

[[2]]
[1] 13 37

[[3]]
[1] "b"

[[4]]
[1] 13 37

[[5]]
[1] "c"

[[6]]
[1] 13 37

[[7]]
[1] "d"

[[8]]
[1] 13 37

> 

Desired result (list element numbers are not accurate)

> listi
[[1]]
[1] "a"

[[2]]
[1] 13 37

[[XXX]]
[1] "inter" "leavistan"

[[3]]
[1] "b"

[[4]]
[1] 13 37

[[XXX]]
[1] "inter" "leavistan"

[[5]]
[1] "c"

[[6]]
[1] 13 37

[[XXX]]
[1] "inter" "leavistan"

[[7]]
[1] "d"

[[8]]
[1] 13 37

> 
Andre Elrico
  • 10,956
  • 6
  • 50
  • 69

3 Answers3

1

We can create a grouping variable to split every 2 elements with gl, then append the 'interElem' at the end of each nested list and flatten it with do.call(c

res <- head(do.call(c, lapply(split(listi, as.integer(gl(length(listi), 2, 
           length(listi)))), function(x) c(x, list(interElem )))), -1)
names(res) <- NULL

Or another option is to convert it to matrix, rbind with 'interElem' and concatenate to list

head(c(rbind(matrix(listi, nrow=2), list(interElem))), -1)
#[[1]]
#[1] "a"

#[[2]]
#[1] 13 37

#[[3]]
#[1] "inter"     "leavistan"

#[[4]]
#[1] "b"

#[[5]]
#[1] 13 37

#[[6]]
#[1] "inter"     "leavistan"

#[[7]]
#[1] "c"

#[[8]]
#[1] 13 37

#[[9]]
#[1] "inter"     "leavistan"

#[[10]]
#[1] "d"

#[[11]]
#[1] 13 37

Or we can use append in a for loop

listn <- listi
i1 <- seq(2, length(listi), by = 2)
i2 <- i1 + (seq_along(i1) - 1)
for(i in seq_along(i2)) listn <-  append(listn, list(interElem), after = i2[i])
head(listn, -1)
akrun
  • 874,273
  • 37
  • 540
  • 662
1

Here's the length of the new list

len = length(listi) + max(0, floor((length(listi) - 1) / 2))

and the index of the elements that should be the original values

idx = seq_len(len) %% 3 != 0

Use these to create a new list and insert the old and interstitial values

res = vector("list", len)
res[idx] = l
res[!idx] = list(v)

Package as a function for robustness and reuse.

fun = function(l, v, n = 2) {
    ## validate arguments
    stopifnot(
        is.numeric(n), length(n) == 1, !is.na(n),
        is.list(l), is.vector(v)
    )

    ## length and index for original values
    len = length(l) + max(0, floor((length(l) - 1) / n))
    idx = seq_len(len) %% (n + 1) != 0

    ## create and populate result
    res = vector("list", len)
    res[idx] = l
    res[!idx] = list(v)
    res
}

with

> str(fun(listi, interElem))
List of 11
 $ : chr "a"
 $ : num [1:2] 13 37
 $ : chr [1:2] "inter" "leavistan"
 $ : chr "b"
 $ : num [1:2] 13 37
 $ : chr [1:2] "inter" "leavistan"
 $ : chr "c"
 $ : num [1:2] 13 37
 $ : chr [1:2] "inter" "leavistan"
 $ : chr "d"
 $ : num [1:2] 13 37
Martin Morgan
  • 45,935
  • 7
  • 84
  • 112
  • you function works nice. Yet it won't work with n=3: `Error: length(l)%%n == 0 is not TRUE` I get why you omit it. But it would be nice to also have unbalanced interleaves. – Andre Elrico Mar 22 '18 at 11:43
  • I can just comment out the `length(l) %% n == 0` part in the stopifnot and I get a general solution! – Andre Elrico Mar 22 '18 at 11:56
  • @AndreElrico I updated the way the length of the final list is calculated, so it works for any input list. – Martin Morgan Mar 22 '18 at 13:30
1

This works on your example and on lists with odd length, including 2 an 1. If the length of the list is odd, matrix() and cbind() rise warnings. Empty list as an entry returns insert list. All what I am trying to do here is to form the vector of indices for a following subsetting of the aggregated listiand list(interElem).

l <- length(listi)
i <- rbind(matrix(1:l, nrow = 2), rep(l+1,l%/%2))[1:(l + l%/%2 - (l+1)%%2)]
c(listi, list(interElem))[i]
utubun
  • 4,400
  • 1
  • 14
  • 17