How to merge 2 vectors alternating indexes?
Asked Answered
T

6

83

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 ?

Twofold answered 21/9, 2014 at 17:25 Comment(1)
Some approaches hereOvert
I
129

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 

Explanation

This works because R stores arrays in column-major order.

When you rbind() the two vectors, you get:

rbind_result <- rbind(a, b)
rbind_result
#   [,1] [,2] [,3]
# a    1    2    3
# b   11   12   13

Then c() coerces rbind_result into a column-wise flattened vector:

merged <- c(rbind_result)
merged
# [1] 1 11 2 12 3 13
Ironmonger answered 21/9, 2014 at 17:32 Comment(5)
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).Mikkanen
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.Corinecorinna
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))Ironmonger
@Corinecorinna I added an edit to explain the intuition for this solution. rbind() does a row-wise combination of the two vectors into a matrix. Then c() converts the matrix into an atomic vector in column-wise order.Violent
Wonderful! as.vector() also works fine.Birthday
Q
29

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
Quartered answered 21/9, 2014 at 17:49 Comment(3)
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?Plectron
@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.Quartered
c(sapply(seq_along(a), function(i) c(a[i], b[i]))) seems to workAnaliese
S
17

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.

Sigismondo answered 9/5, 2017 at 17:28 Comment(1)
Just c(a,b)[order(c(seq_along(a),seq_along(b)))] should do it I think. No need for the odd/even calculations.Kieserite
H
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

Hetero answered 15/7, 2016 at 18:42 Comment(0)
O
3

A tidyverse approach is vctrs::vec_interleave:

vctrs::vec_interleave(a, b)
#[1]  1 11  2 12  3 13
Ore answered 23/12, 2022 at 10:29 Comment(0)
J
1

@MBo's answer to my question at https://mcmap.net/q/244098/-create-boolean-vector-of-length-n-with-k-true-values-well-dispersed 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)
Jinks answered 8/11, 2019 at 21:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.