user177983
user177983

Reputation:

Groovy list.sort by first, second then third elements

I have a groovy list of lists i.e.

list = [[2, 0, 1], [1, 5, 2], [1, 0, 3]]

I would like sort it by order of the first element, then second, then third.

Expected

assert list == [[1, 0, 3], [1, 5, 2], [2, 0, 1]]

I started with list = list.sort{ a,b -> a[0] <=> b[0] } but that only sorts the first element. How do you finish?

Thanks

Upvotes: 30

Views: 32982

Answers (8)

Daniel Black
Daniel Black

Reputation: 605

I know I'm very late to the party but I wanted a way to sort by multiple keys, asc or desc separately for each field. I came up this this method which seems to work:

public static sortObjectArray(def objects, Map sortFields){
    objects.sort{a,b->
        int matches = 0
        for(def entry in sortFields){

            if(entry.value == 'asc'){
                matches = (a[entry.key] <=> b[entry.key]).toInteger()
            }else{
                matches = (b[entry.key] <=> a[entry.key]).toInteger()
            }

            if(matches != 0){
                break
            }
        }

        return matches
    }
}

It allows for passing an object list and a map, the object list will be sort by the keys of the map, either descending or ascending based on the value of the map entry. Ex:

ArrayList<Map> objects = [
        [field1:1,field2:2,field3:2],
        [field1:1,field2:1,field3:1],
        [field1:1,field2:2,field3:1],
]

println sortObjectArray(objects,[field1:'asc',field2: 'desc',field3: 'asc'])

Outputs:

[[field1:1, field2:2, field3:1], [field1:1, field2:2, field3:2], [field1:1, field2:1, field3:1]]

Upvotes: 0

user303546
user303546

Reputation: 41

you can use kobo-commons' CollectionUtils library.

https://github.com/kobo/kobo-commons/wiki/sort-by-multiple-keys

import org.jggug.kobo.commons.lang.CollectionUtils

CollectionUtils.extendMetaClass()


list = [[2, 0, 1], [1, 5, 2], [1, 0, 3]]
list = list.sort{ [ it[0], it[1], it[2] ]} // sort by multiple keys
assert list == [[1, 0, 3], [1, 5, 2], [2, 0, 1]]

list2 = [ [name:"a", age:13], [name:"a",age:15], [name:"b", age:13] ]
list2 = list2.sort{[it.name, it.age] } // sort by name and age
assert list2 == [[name:"a", age:13], [name:"a", age:15], [name:"b", age:13]]

Upvotes: 4

BahmanM
BahmanM

Reputation: 1445

Done the Groovy way, regardless of the size of sub-lists:

ll2.sort { l1, l2 ->
  e1e2 = [l1, l2].transpose().find { e1, e2 ->
      e1 != e2
  }
  e1e2 ? e1e2[0] <=> e1e2[1] : 0
}

Upvotes: 2

cweston
cweston

Reputation: 11637

Here is another method using Groovy's Spaceship and Elvis operators:

​def list = [[2, 0, 1], [1, 5, 2], [1, 0, 3]]

list.sort { a, b ->
   a[0] <=> b[0] ?: a[1] <=> b[1] ?: a[2] <=> b[2]
}

assert list == [[1, 0, 3], [1, 5, 2], [2, 0, 1]]​

Source: Groovier way of sorting over multiple fields in a list of maps in groovy

Upvotes: 7

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123433

You should be able to iterate through the desired sorting in reverse order:

list = [[2, 0, 1], [1, 5, 2], [1, 0, 3]]

list = list.sort{ a,b -> a[2] <=> b[2] }
list = list.sort{ a,b -> a[1] <=> b[1] }
list = list.sort{ a,b -> a[0] <=> b[0] }

assert list == [[1, 0, 3], [1, 5, 2], [2, 0, 1]]

Each should override the previous just enough to keep the combined sorting intact.


You can also chain them in order with the Elvis operator, ?:, which will defer to the next comparison when the previous are equal (and <=> returns 0):

list.sort { a,b -> a[0] <=> b[0] ?: a[1] <=> b[1] ?: a[2] <=> b[2] }

Upvotes: 36

Michal Zmuda
Michal Zmuda

Reputation: 5621

You can do it in one line:

list.sort { String.format('%010d%010d%010d', it[0], it[1], it[2]) }

Upvotes: 0

Ted Naleid
Ted Naleid

Reputation: 26791

If you want to sort arrays of arbitrary (though homogenous) length, you can use this and it will do it in a single pass:

def list = [[2, 0, 1], [1, 5, 2], [1, 0, 3]]

list.sort { a, b -> 
    for (int i : (0..<a.size())) {
        def comparison = (a[i] <=> b[i])
        if (comparison) return comparison
    } 
    return 0
}

assert list == [[1, 0, 3], [1, 5, 2], [2, 0, 1]]

Upvotes: 9

user177983
user177983

Reputation:

Here's what I came up with, not the most groovy way I suppose..

list = list.sort{ a,b -> 
    if(a[0].compareTo(b[0]) == 0) {
        if(a[1].compareTo(b[1]) == 0) {
            return a[2].compareTo(b[2]);
        } else {
            return a[1].compareTo(b[1]);
        }
    } else {
        return a[0].compareTo(b[0]);
    }
}

Upvotes: 0

Related Questions