How to sort a list by existing properties

前端 未结 3 1542
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-15 00:46

I am using this line here to sort a list based on the object\'s name.

g.V.sort{it.name}

How do I sort it based on \"name\" if it exists, if not

相关标签:
3条回答
  • 2021-02-15 01:01

    You can sorts the Collection using a Comparator.

    g.V.sort { a, b ->
        a.name <=> b.name ?: a.title <=> b.title
    }
    
    0 讨论(0)
  • 2021-02-15 01:02

    This is my totally untested effort which is probably riddled with bugs

    def comparator = {o1, o2 ->
    
      // wording of question suggests title will always exist, if not, add more hasProperty checks  
      def diff = o1.title <=> o2.title
    
      if (o1.hasProperty('name') && o2.hasProperty('name')) {  
        def nameDiff = o1.name <=> o2.name
    
        if (nameDiff != 0) {
          diff = nameDiff
        }
      } 
      diff
    
    } as Comparator
    
    
    def someList = []
    // populate the list...    
    someList.sort(comparator)
    
    0 讨论(0)
  • 2021-02-15 01:15

    I'm not sure I understand your question correctly. Maybe something like this is what you are looking for:

    def things = [
        [name: 'aaa', title: '222'],
        [name: 'aaa', title: '333'],
        [title: '222'],
        [title: '111'],
        [name: 'bbb', title: '111'],
        [title: '333'],
        [name: 'aaa', title: '111'],
    ]
    
    things.sort { a, b ->
        // Compare by name and then by title.
        a.name <=> b.name ?: a.title <=> b.title
    }
    
    assert things == [
        [title: '111'],
        [title: '222'],
        [title: '333'],
        [name: 'aaa', title: '111'],
        [name: 'aaa', title: '222'],
        [name: 'aaa', title: '333'],
        [name: 'bbb', title: '111'],
    ]
    

    What's happening inside that seemingly innocent comparison function is actually quite a lot of Groovy syntax magic. But it's not too difficult to follow.

    First, the sort method is being invoked with a binary function that acts as a comparator (i.e. takes two arguments, a and b and returns -1 if a < b, 1 if a > b and 0 if a == b).

    This anonymous function: { a, b -> a.name <=> b.name ?: a.title <=> b.title } uses the "spaceship operator" (<=>... that's a spaceship man!) to first compare a and b by names.

    If the names are equal (or both null), then the result of a.name <=> b.name is 0, which evaluates falsely in the "Elvis operator" (?:... imagine it as a smiley), so then the result of a.title <=> b.title is returned.

    Otherwise, if the result of the name comparison is not 0, then that evaluates truthfully in the Elvis operator and that value is returned.

    This is all taking into consideration that you can compare null values with strings and that 'any string' > null always holds (which is the same as saying that 'any string' <=> null == 1).

    So the final result is that elements with no name are first and sorted by title, and then the elements with name and title are ordered first by name and then by title.

    I hope that was what you were looking for. If you were expecting a different ordering of the sorted elements feel free to clarify it in the comments :)

    Update

    There is also a not very documented OrderBy object that can be used in this case:

    things.sort new OrderBy([{it.name}, {it.title}])
    
    0 讨论(0)
提交回复
热议问题