How to merge 2 vectors alternating indexes?

19,619

Solution 1

This will work using rbind :

c(rbind(a, b))

For example:

a = c(1,2,3)
b = c(11,12,13)

c(rbind(a,b))

#[1]  1 11  2 12  3 13 

Solution 2

The rbind() answer by @jalapic is excellent. Here's an alternative that creates a new vector then assigns the alternating values to it.

a <- c(1,2,3)
b <- c(11,12,13)

x <- vector(class(a), length(c(a, b)))
x[c(TRUE, FALSE)] <- a
x[c(FALSE, TRUE)] <- b
x
# [1]  1 11  2 12  3 13

And one more that shows append

c(sapply(seq_along(a), function(i) append(a[i], b[i], i)))
# [1]  1 11  2 12  3 13

Solution 3

Just wanted to add a simpler solution that works for when vectors are unequal length and you want to append the extra data to the end.

> a <- 1:3
> b <- 11:17
> c(a, b)[order(c(seq_along(a)*2 - 1, seq_along(b)*2))]
 [1]  1 11  2 12  3 13 14 15 16 17

Explanation:

  • c(a, b) creates a vector of the values in a and b.
  • seq_along(a)*2 - 1 creates a vector of the first length(a) odd numbers.
  • seq_along(b)*2 creates a vector of the first length(b) even numbers.
  • order(...) will return the indexes of the numbers in the two seq_along vectors such that x[order(x)] is an ordered list. Since the first seq_along contains the even numbers and the second seq_along has the odds, order will take the first element from the first seq_along, then the first elements of the second seq_along, then the second element from the first seq_along, etc. interspersing the two vector indexes and leaving the extra data at the tail.
  • By indexing c(a, b) using the order vector, we will intersperse a and b.

As a note, since seq_along returns numeric(0) when the input is NULL this solution works even if one of the vectors is length 0.

Solution 4

I had to solve a similar problem, but my vectors were of unequal length. And, I didn't want to recycle the shorter vector, but just append the tail of the longer vector.

And the solution for @RichardScriven didn't work for me (though I may have done something wrong and didn't try hard to troubleshoot).

Here is my solution:

#' Riffle-merges two vectors, possibly of different lengths
#'
#' Takes two vectors and interleaves the elements.  If one vector is longer than
#' the other, it appends on the tail of the longer vector to the output vector.
#' @param a First vector
#' @param b Second vector
#' @return Interleaved vector as described above.
#' @author Matt Pettis
riffle <- function(a, b) {
  len_a <- length(a)
  len_b <- length(b)
  len_comm <- pmin(len_a, len_b)
  len_tail <- abs(len_a - len_b)

  if (len_a < 1) stop("First vector has length less than 1")
  if (len_b < 1) stop("Second vector has length less than 1")

  riffle_common <- c(rbind(a[1:len_comm], b[1:len_comm]))

  if (len_tail == 0) return(riffle_common)

  if (len_a > len_b) {
    return(c(riffle_common, a[(len_comm + 1):len_a]))
  } else {
    return(c(riffle_common, b[(len_comm + 1):len_b]))
  }
}

# Try it out
riffle(1:7, 11:13)
  [1]  1 11  2 12  3 13  4  5  6  7

riffle(1:3, 11:17)
   [1]  1 11  2 12  3 13 14 15 16 17

HTH, Matt

Solution 5

@MBo's answer to my question at https://stackoverflow.com/a/58773002/2556061 implies a solution for evenly interlacing vectors of unequal length. I'm reporting it here in for reference.

interleave <- function(x, y)
{
  m <- length(x)
  n <- length(y)
  xi <- yi <- 1
  len <- m + n
  err <- len %/% 2
  res <- vector()
  for (i in 1:len)
  {
    err <- err - m
    if (err < 0)
    {

      res[i] <- x[xi]
      xi <- xi + 1
      err <- err + len
    } else
    {
       res[i] <- y[yi]
      yi <- yi + 1
     }
  }
  res
}

gives

interleave(1:10, 100:120)

c(100, 1, 101, 102, 2, 103, 104, 3, 105, 106, 4, 107, 108, 5, 109, 110, 111, 6, 112, 113, 7, 114, 115, 8, 116, 117, 9, 118, 119, 10, 120)
Share:
19,619
Wicelo
Author by

Wicelo

Updated on June 15, 2022

Comments

  • Wicelo
    Wicelo almost 2 years

    I would like to merge 2 vectors this way :

    a = c(1,2,3)
    b = c(11,12,13)
    merged vector : c(1,11,2,12,3,13)
    

    How could I do it ?

  • Tim
    Tim over 8 years
    This solution would not work for vector that differ in length, solution by @RichardScriven is more robust for such situations (if length(a) is greater than lenght(b) or max of lengths is used for indexing).
  • Patrick Williams
    Patrick Williams over 7 years
    I love your answer to this question (though in the append example, when I have one vector with 2-items, and another with 3-items, I end up with a final vector with an NA at the end). I went with the first option, but don't quite understand what is going on in these lines: x[c(TRUE, FALSE)] <- a x[c(FALSE, TRUE)] <- b Can you explain at all?
  • Rich Scriven
    Rich Scriven over 7 years
    @PatrickWilliams - c(TRUE, FALSE), when used to index, means to take every other value starting with the first. c(TRUE, FALSE) is recycled through the entire length of the vector (so it's like saying "yes, no, yes, no, yes, no" in this example). On the other hand c(FALSE TRUE) takes every other value starting with the second in the same manner.
  • thelatemail
    thelatemail almost 7 years
    Just c(a,b)[order(c(seq_along(a),seq_along(b)))] should do it I think. No need for the odd/even calculations.
  • ColorStatistics
    ColorStatistics almost 4 years
    This is a great solution! Thank you. It would be great if you could add a brief explanation as to why it works as it is not immediately obvious why row binding 2 vectors and then concatenating the resulting vectors would produce the interspersed result. I am still trying to figure this out.
  • jalapic
    jalapic almost 3 years
    One of the side-effects of the function c is that is turns data structures into vectors. So using c here is akin to doing as.vector(rbind(a,b))