10

In R, I have a list of nontrivial objects (they aren't simple objects like scalars that R can be expected to be able to define an order for). I want to sort the list. Most languages allow the programmer to provide a function or similar that compares a pair of list elements that is passed to a sort function. How can I sort my list?

Chris
  • 941
  • 9
  • 18
  • can you provide a snippet of this nontrivial element list so others have an idea of what you are talking about? Tips on writing a reproducible question here: http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example – Chase Sep 22 '11 at 13:49

3 Answers3

16

To make this is as simple I can, say your objects are lists with two elements, a name and a value. The value is a numeric; that's what we want to sort by. You can imagine having more elements and needing to do something more complex to sort.

The sort help page tells us that sort uses xtfrm; xtfrm in turn tells us it will use == and > methods for the class of x[i].

First I'll define an object that I want to sort:

xx <- lapply(c(3,5,7,2,4), function(i) list(name=LETTERS[i], value=i))
class(xx) <- "myobj"

Now, since xtfrm works on the x[i]'s, I need to define a [ function that returns the desired elements but still with the right class

`[.myobj` <- function(x, i) {
  class(x) <- "list"
  structure(x[i], class="myobj")
}

Now we need == and > functions for the myobj class; this potentially could be smarter by vectorizing these properly; but for the sort function, we know that we're only going to be passing in myobj's of length 1, so I'll just use the first element to define the relations.

`>.myobj` <- function(e1, e2) {
  e1[[1]]$value > e2[[1]]$value
}

`==.myobj` <- function(e1, e2) {
  e1[[1]]$value == e2[[1]]$value
}

Now sort just works.

sort(xx)

It might be considered more proper to write a full Ops function for your object; however, to just sort, this seems to be all you need. See p.89-90 in Venables/Ripley for more details about doing this using the S3 style. Also, if you can easily write an xtfrm function for your objects, that would be simpler and most likely faster.

Aaron left Stack Overflow
  • 36,704
  • 7
  • 77
  • 142
  • 1
    That is a lot of work to construct a less general solution than can be constructed with regular R bits: These are two uses of `order` sorting on first the value: `xx[order(as.character(sapply(xx, "[", "value")))]` , and then on the name: `xx[order(as.character(sapply(xx, "[", "name")))]` without any reference to the class methods. – IRTFM Sep 22 '11 at 16:04
  • @DWin is absolutely right; if you can do something smarter, you should! – Aaron left Stack Overflow Sep 22 '11 at 16:10
4

The order function will allow you to determine the sort order for character or numeric aruments and break ties with subsequent arguments. You need to be more specific about what you want. Produce an example of a "non-trivial object" and specify the order you desire in some R object. Lists are probably the most non-vectorial objects:

> slist <- list(cc=list(rr=1), bb=list(ee=2, yy=7), zz="ww")
> slist[order(names(slist))]  # alpha order on names()
$bb
$bb$ee
[1] 2
$bb$yy
[1] 7  
$cc
$cc$rr
[1] 1    
$zz
[1] "ww"

slist[c("zz", "bb", "cc")]   # an arbitrary ordering
$zz
[1] "ww"
$bb
$bb$ee
[1] 2
$bb$yy
[1] 7
$cc
$cc$rr
[1] 1
IRTFM
  • 258,963
  • 21
  • 364
  • 487
1

One option is to create a xtfrm method for your objects. Functions like order take multiple columns which works in some cases. There are also some specialized functions for specific cases like mixedsort in the gtools package.

Greg Snow
  • 48,497
  • 6
  • 83
  • 110