74

I have a character array

cf <- c("V440","V457","V116","V327","V446","V108",
         "V155","V217","V120","V51","V477")

I would like to sort it in descending order so that I will have an output like this:

V51
V108
V116
V120
V155
V217
V327
V440
V446
V457
V477

I have tried sort.list() like this

cf[sort.list(cf)]

and got this answer:

[1] "V108" "V116" "V120" "V155" "V217" "V327" "V440" "V446" "V457" "V477" "V51" 

and also tried order() and got same result.

Can someone help me please

Jaap
  • 81,064
  • 34
  • 182
  • 193
rinzy kutex
  • 1,195
  • 1
  • 12
  • 13

6 Answers6

76

Try mixedsort from the "gtools" package:

> # install.packages("gtools") ## Uncomment if not already installed
> library(gtools)
> mixedsort(cf)
 [1] "V51"  "V108" "V116" "V120" "V155" "V217" "V327" "V440" "V446" "V457" "V477"

If you don't want to use mixedsort (not sure why one wouldn't), and if your vector has a pretty consistent pattern (eg letters followed by numbers), you can also probably try something like this. (Note: Relatively untested.)

newvec <- c("V440", "V457", "V116", "V327", "V446", "V108", "V155", 
            "V217", "V120", "V51", "V477", "B22", "A10", "Z01")

newvec[order(gsub("([A-Z]+)([0-9]+)", "\\1", newvec), 
             as.numeric(gsub("([A-Z]+)([0-9]+)", "\\2", newvec)))]
#  [1] "A10"  "B22"  "V51"  "V108" "V116" "V120" "V155" "V217" "V327" "V440"
# [11] "V446" "V457" "V477" "Z01" 
A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
63

Plenty of right answers here, this is another way, just for fun.

cf[order(nchar(cf), cf)]
# [1] "V51"  "V108" "V116" "V120" "V155" "V217" "V327" "V440" "V446" "V457" "V477"
Matthew Plourde
  • 43,932
  • 7
  • 96
  • 113
  • 2
    Well, this sorts c("ahoy", "hello", "hi") as "hi", "ahoy" and "hello" what is not exactly what a human would expect from a natural comparator – dpelisek May 11 '21 at 05:50
55

One more solution in a line of code using str_sortfunction (from the stringr packg.)

# install.packages("stringr") ## Uncomment if not already installed
library(stringr)

str_sort(cf, numeric = TRUE)

[1] "V51"  "V108" "V116" "V120" "V155" "V217" "V327" "V440" "V446" "V457" "V477"
JDie
  • 651
  • 5
  • 6
12

Just scrape off the preceding "V" character to build a sorting vector. No additional fancy tools required.

vals <- as.numeric(gsub("V","", cf))
cf[order(vals)]

[1] "V51"  "V108" "V116" "V120" "V155" "V217" "V327" "V440" "V446"
[10] "V457" "V477"
David Marx
  • 8,172
  • 3
  • 45
  • 66
5

R correctly orders strings alphabetically, that is why you get that result.

Aside from @Ananda very good answer, if you want to use base R you can use strsplit to remove the "V" from each string and then use as.numeric to cast the strings to integers:

vals <- as.numeric(sapply(cf, FUN=function(x){strsplit(x, "V")[[1]][2]}))

Now you can sort your strings using vals

cf[order(vals)]
nico
  • 50,859
  • 17
  • 87
  • 112
2

Here's a base approach utilizing names and sort (Ananda's was pretty slick):

cf <- c("V440","V457","V116","V327","V446","V108",
         "V155","V217","V120","V51","V477")

cf2 <- as.numeric(gsub("[^[:digit:]]", "", cf))
names(cf2) <- seq_along(cf2)
cf[as.numeric(names(sort(cf2)))]

## > cf[as.numeric(names(sort(cf2)))]
##  [1] "V51"  "V108" "V116" "V120" "V155" "V217" "V327"
##  [8] "V440" "V446" "V457" "V477"
Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519