Micro
Micro

Reputation: 10891

Move element from front of array to back of array in R

Is there an elegant way to take a vector like

x = c("a", "b", "c", "d", "e")

and make it be

x = c("b", "c", "d", "e", "a")

I did:

x = c("a", "b", "c", "d", "e")
firstVal = x[1]
x = tail(x,-1)
x[length(x)+1] = firstVal
x
[1] "b" "c" "d" "e" "a"

It works, but kinda ugly.

Upvotes: 6

Views: 5203

Answers (5)

gagolews
gagolews

Reputation: 13046

Elegance is a matter of taste, and de gustibus non est disputandum:

> x <- c("a", "b", "c", "d", "e")
> c(x[-1], x[1])
[1] "b" "c" "d" "e" "a"

Upvotes: 13

Jamie
Jamie

Reputation: 491

The one thing that could potentially be improved with the previous answers is that you have to know the location number of the item you want to move, which in longer vectors could become an issue. You could instead do something like:

x <- c("a", "b", "c", "d", "e")
new_x <- c(x[-which(x == "a")], "a")

Upvotes: 2

Tyler Rinker
Tyler Rinker

Reputation: 109864

I had an idea to use both head and tail that turned out to be a flop when I benchmarked.

c(tail(x, -1), head(x, 1))

I figured I'd share the results as they're informative. I also scaled up for larger vectors and the results were interesting:

x <- c("a", "b", "c", "d", "e")  

gagolews <- function() c(x[-1], x[1])

senoro <- function() x[c(2:length(x), 1)]

tyler <- function() c(tail(x, -1), head(x, 1))

ananda <- function() shifter(x, 1)

user <-function(){
   firstVal = x[1]
   x = tail(x,-1)
   x[length(x)+1] = firstVal
   x
}


library(microbenchmark)
(op <- microbenchmark( 
    gagolews(),
    senoro(),
    tyler(),
    ananda(),
    user(),
times=100L))

## Unit: microseconds
##        expr    min     lq median     uq      max neval
##  gagolews()  1.400  1.867  2.333  2.799    5.132  1000
##    senoro()  1.400  1.867  2.333  2.334   10.730  1000
##     tyler() 37.320 39.187 40.120 41.519  135.287  1000
##    ananda() 39.653 41.519 42.452 43.386   69.043  1000
##      user() 24.259 25.658 26.591 27.058 1757.789  1000

enter image description here Here I scaled up. I only benched on 100 replications because of the size of the vector (1 million characters).

x <- rep(c("a", "b", "c", "d", "e"), 1000000)

## Unit: milliseconds
##        expr      min       lq   median       uq      max neval
##  gagolews() 168.9151 171.3631 179.0490 193.9604 260.5963   100
##    senoro() 192.2669 203.9596 259.1366 272.5570 341.4443   100
##     tyler() 237.4218 246.5368 303.5700 319.3999 347.3610   100
##    ananda() 237.9610 247.2097 303.9898 318.4564 342.2518   100
##      user() 225.4503 234.3431 287.8348 300.8078 319.2051   100

enter image description here

Upvotes: 3

A5C1D2H2I1M1N2O1R2T1
A5C1D2H2I1M1N2O1R2T1

Reputation: 193517

Overkill time: You can consider my shifter or my moveMe function, both of which are part of my GitHub-only SOfun package.

Here are the relevant examples:

shifter

This is basically a head and tail approach:

## Specify how many values need to be shifted
shifter(x, 1)
# [1] "b" "c" "d" "e" "a"
shifter(x, 2)
# [1] "c" "d" "e" "a" "b"

## The direction can be changed too :-)
shifter(x, -1)
# [1] "e" "a" "b" "c" "d"

moveMe

This is fun:

moveMe(x, "a last")
# [1] "b" "c" "d" "e" "a"

## Lots of options to move things around :-)
moveMe(x, "a after d; c first")
# [1] "c" "b" "d" "a" "e"

Upvotes: 7

Se&#241;or O
Se&#241;or O

Reputation: 17412

Agreed with the matter of taste comment. My personal approach would be:

x[c(2:length(x), 1)]

Upvotes: 4

Related Questions