Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting a list in Groovy in an unusual way

Tags:

sorting

groovy

I have a list of, let's say [Cat, Dog, Cow, Horse], that I want to be sorted in the following way

  • if Cat is on the list it should come first
  • if Cow is on the list it should come second
  • The rest of the elements should come after in alphabetic order.

Any suggestions how this could be done in Groovy?

like image 284
bladet Avatar asked Mar 15 '12 11:03

bladet


People also ask

Are Groovy maps ordered?

In Groovy, maps created with the literal notation are ordered. We can expect our output to be in the same order as we defined in our original map.


4 Answers

Tim's answer is pretty clever. I'm personally more a fan of just using list operations as the code it generates is slightly easier to read.

def highPriority = [ 'Cat', 'Cow' ]

def list = [ 'Armadillo', 'Dog', 'Cow', 'Zebra', 'Horse', 'Cow', 'Cat' ]

def remainder = ( list - highPriority ).sort()

list.retainAll( highPriority )

list.sort{ highPriority.indexOf( it ) } + remainder

That will give you Cow twice. If you don't want duplicates, using intersect is fairly simple.

def highPriority = [ 'Cat', 'Cow' ]

def list = [ 'Armadillo', 'Dog', 'Cow', 'Zebra', 'Horse', 'Cow', 'Cat' ]

list.intersect( highPriority ).sort{ highPriority.indexOf( it ) } + ( list - highPriority ).sort()
like image 116
Tomas Lin Avatar answered Sep 24 '22 03:09

Tomas Lin


This should do it:

// Define our input list
def list = [ 'Armadillo', 'Cat', 'Dog', 'Cow', 'Zebra', 'Horse', 'Cow' ]

// Define a closure that will do the sorting
def sorter = { String a, String b, List prefixes=[ 'Cat', 'Cow' ] ->
  // Get the index into order for a and b
  // if not found, set to being Integer.MAX_VALUE
  def (aidx,bidx) = [a,b].collect { prefixes.indexOf it }.collect {
    it == -1 ? Integer.MAX_VALUE : it
  }
  // Compare the two indexes.
  // If they are the same, compare alphabetically
  aidx <=> bidx ?: a <=> b
}

// Create a new list by sorting using our closure
def sorted = list.sort false, sorter

// Print it out
println sorted

That prints:

[Cat, Cow, Cow, Armadillo, Dog, Horse, Zebra]

I've commented it to try and explain each step it takes. By adding the default prefix items as an optional parameter on the sorter closure, it means we can do stuff like this to change the default:

// Use Dog, Zebra, Cow as our prefix items
def dzc = list.sort false, sorter.rcurry( [ 'Dog', 'Zebra', 'Cow' ] )
println dzc

Which then prints the list sorted as:

[Dog, Zebra, Cow, Cow, Armadillo, Cat, Horse]
like image 21
tim_yates Avatar answered Sep 24 '22 03:09

tim_yates


Here's another alternative that feels simpler to me:

// smaller values get sorted first
def priority(animal) {
    animal in ['Cat', 'Cow'] ? 0 : 1
}

def list = [ 'Armadillo', 'Cat', 'Dog', 'Cow', 'Zebra', 'Horse', 'Cow' ]

def sorted = list.sort{ a, b -> priority(a) <=> priority(b) ?: a <=> b }

assert sorted == ['Cat', 'Cow', 'Cow', 'Armadillo', 'Dog', 'Horse', 'Zebra']
like image 37
ataylor Avatar answered Sep 26 '22 03:09

ataylor


If you don't have duplicate elements, you can try this:

def highPriority = [ 'Cat', 'Cow' ]
def list = [ 'Armadillo', 'Dog', 'Cow', 'Zebra', 'Horse', 'Cat' ]
highPriority + list.minus(highPriority).sort()
like image 35
Arturo Herrero Avatar answered Sep 24 '22 03:09

Arturo Herrero