5

In a question here on SO (LINK) a poster asked a question and I gave an answer that works but there's a part that bugs me, creating a list from a vector to pass as a list of indices. So les say I have this vector:

n <- 1:10
#> n
# [1]  1  2  3  4  5  6  7  8  9 10

Let's say I want to break it up into a list of vectors and each vector is of length 3. What's the best (shortest amount of code & or fastest) way to accomplish this? We want to toss out item 10 since it there's a remainder of 1 (10 %% 3) from 10/3 (length(n) - 10 %% 3).

This is the desired outcome

list(1:3, 4:6, 7:9)

This will give us the indices of those that can't make a group of three:

(length(n) + 1 - 10 %% 3):length(n)

EDIT

Here's an interesting approach posted by Wojciech Sobala on the other thread this is linked to (I asked them to answer here and if they do I'll remove this edit)

n <- 100
l <- 3
n2 <- n - (n %% l)
split(1:n2, rep(1:n2, each=l, length=n2))

As a function:

indices <- function(n, l){
    if(n > l) stop("n needs to be smaller than or equal to l")
    n2 <- n - (n %% l)
    cat("numbers", (n + 1 - n %% l):n, "did not make an index of length", l)
    split(1:n2, rep(1:n2, each=l, length=n2))
}
Community
  • 1
  • 1
Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
  • All great answers but I think X,He's is the shortest code-wise. Thanks, that was bugging me. All much better approaches than what I was doing. – Tyler Rinker May 19 '12 at 04:20

3 Answers3

5

Not sure if this does the job?

x = function(x, n){ 
    if(n > x) stop("n needs to be smaller than or equal to x")
    output = matrix(1:(x-x%%n), ncol=(x-x%%n)/n, byrow=FALSE)
    output
}

Edit: changed the output to a list

x = function(x, n){ 
    if(n > x) stop("n needs to be smaller than or equal to x")
    output = matrix(1:(x-x%%n), ncol=(x-x%%n)/n, byrow=TRUE)
    split(output, 1:nrow(output))
}

Example:
x(10, 3)
$`1`
[1] 1 2 3

$`2`
[1] 4 5 6

$`3`
[1] 7 8 9
Alex
  • 4,030
  • 8
  • 40
  • 62
4
xx <- 1:10
xxr <- rle(0:(length(1:10)-1) %/% 3)  # creates an rle object
fac3 <- rep( xxr$values[xxr$lengths == 3], each=3)  #selects the one of length 3
                                     # and recreates the shortened grouping vector
tapply(xx[ 1:length(fac3)],          # a shortened original vector
                       fac3, list)   # split into little lists
$`0`                                # Hope you don't mind having names on your list
[1] 1 2 3

$`1`
[1] 4 5 6

$`2`
[1] 7 8 9
Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
IRTFM
  • 258,963
  • 21
  • 364
  • 487
3

It's not the shortest, but here's a little recursive version:

wrap <- function(n,x,lx,y) {
    if (lx < n) return (y)
    z <- x[-(1:n)]
    wrap(n, z, length(z), c(y, list(x[1:n])))
}

wrapit <- function(x,n) {
    wrap(n,x,length(x),list())
}

> wrapit(1:10,3)
[[1]]
[1] 1 2 3

[[2]]
[1] 4 5 6

[[3]]
[1] 7 8 9
dave gibbs
  • 41
  • 4
  • Wow this one hurts my noodle. +1 I'm not even sure how it works (though indices are in reverse order) – Tyler Rinker May 19 '12 at 04:21
  • 1
    To understand recursive functions, sometimes following the chain of function calls with paper and pencil can help. Or put a couple print statements in there to watch the list grow and the vector shrink. – dave gibbs May 19 '12 at 14:40