简体   繁体   English

以不寻常的方式在Groovy中对列表进行排序

[英]Sorting a list in Groovy in an unusual way

I have a list of, let's say [Cat, Dog, Cow, Horse] , that I want to be sorted in the following way 我有一个列表,让我们说[Cat, Dog, Cow, Horse] ,我希望按以下方式排序

  • if Cat is on the list it should come first 如果Cat在列表中,它应该首先出现
  • if Cow is on the list it should come second 如果Cow在名单上,它应该排在第二位
  • The rest of the elements should come after in alphabetic order. 其余元素应按字母顺序排列。

Any suggestions how this could be done in Groovy? 有什么建议如何在Groovy中完成?

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. 如果你不想重复,使用intersect非常简单。

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

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

list.intersect( highPriority ).sort{ highPriority.indexOf( it ) } + ( list - highPriority ).sort()

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: 通过在sorter闭包上添加默认前缀项作为可选参数,这意味着我们可以执行以下操作来更改默认值:

// 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]

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']

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()

This question is pretty old, but today i found that Groovy has a, rather undocumented, OrderBy comparator that can be used in this case: 这个问题已经很老了,但今天我发现Groovy有一个相当无证的OrderBy比较器,在这种情况下可以使用:

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

def sorted = list.sort new OrderBy([{ -highPriority.indexOf(it) }, { it }])

assert sorted == ['Cat', 'Cow', 'Cow', 'Armadillo', 'Dog', 'Horse', 'Zebra']

The OrderBy comparator first compares the animals using their index in the highPriority list negated (therefore the animals that are not high priority (ie index -1) are moved to the back of the list) and if the indexes are equal it compares them by the identity function {it} , which, as animals are strings, sorts them alphabetically. OrderBy比较器首先使用取消的highPriority列表中的索引来比较动物(因此,不高优先级的动物(即索引-1)被移动到列表的后面),如果索引相等,则将它们与身份函数{it} ,作为动物是字符串,按字母顺序对它们进行排序。

Inspired on tomas' answer : 灵感来自tomas的回答

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

// Group animals by priority.
def groups = list.groupBy { it in highPriority ? it : 'rest' }
// High priority animals are sorted by priority and the rest alphabetically.
def sorted = highPriority.collectMany { groups[it] } + groups['rest'].sort()

assert sorted == ['Cat', 'Cow', 'Cow', 'Armadillo', 'Dog', 'Horse', 'Zebra']

The groups variable is something like [rest:[Armadillo, Dog, Zebra, Horse], Cat:[Cat], Cow:[Cow, Cow]] . groups变量类似于[rest:[Armadillo, Dog, Zebra, Horse], Cat:[Cat], Cow:[Cow, Cow]]

Another, arguably less robust, solution might be: 另一个可能不那么强大的解决方案可能是:

def sorted = list.sort(false) { 
    def priority = highPriority.indexOf(it)
    if (priority == -1) priority = highPriority.size()
    // Sort first by priority and then by the value itself
    "$priority$it"
}

It is less robust in the sense that it sorts by strings like "2Armadillo" , "0Cat" , etc, and won't work if you have 9 or more high priority animals (because "10Alpaca" < "9Eel" . It would be cool if Groovy provided some sort of comparable tuple type, like Python's tuples , so instead of returning "$priority$it" as the comparable key, one could return the tuple (priority, it) . 它不像"2Armadillo""0Cat"等字符串那样排序,如果你有9个或更多的高优先级动物(因为"10Alpaca" < "9Eel" ,它将"10Alpaca" < "9Eel" 。如果Groovy提供了某种类似的元组类型,比如Python的元组 ,那么很酷,所以不是将"$priority$it"作为可比较的键返回(priority, it)可以返回元组(priority, it)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM