Rick

Rick
Rick

Sunday, March 9, 2014

Boon Sorting for Java, Java collections and JSON

Boon Home | Boon Source | If you are new to boon, you might want to start here. Simple opinionated Java for the novice to expert level Java Programmer. Low Ceremony. High Productivity. A real boon to Java to developers!

Java Boon - Boon Sorting for Java, JSON and Java collections

Many languages have built in support to easily sort lists, maps, JSON and Java objects. Boon adds this to Java.

Boon Sorting Java objects, lists/maps and JSON

Boon allows you to easily sort Java instances or Maps/Lists or JSON.
Let's get a bird's eye view....

sort, sortBy, sortNullFirst, sortDesc (natural and property sort)

sort(employees);              //Sort the employees in natural order (works with arrays, lists, sets, iterable)
sort( employees, "lastName"); //Sorts the employees by last name
sort( departmentList );       //Sorts the department by natural sort order

sort( employees, sortBy( "department.name" ),
                         sortByDescending( "lastName" ),
                         sortBy( "firstName" ) ); //Works with lists, collections, sets, arrays, iterable

sort(employees,
      sortBy("contactInfo.phoneNumbers[0]")); //Sort by path expression. This is very powerful!


sort(employees,
    sortByDescending("contactInfo.phoneNumbers[0]"); //Sorts by path expression. Descending

There are many flavors of sorts, there is sort, sortNullFirst, sortDesc, sortDescNullsFirst, and they work with IterableListCollection and array. There is even a version of sort that allows every field to be sorted in a different manner (ascending null first, descending null last, and so on). You can sort by properties or property paths. You can also sort JSON, List/Maps or Java instances (POJOs) all from Boon. It is truly awesome!
Boon is all about convention over configuration and complicated documentation. For example, the sorting of text always uses collation so you will not get weird garbage. It you specify "this" as property name or you don't specify a property or property path, then the object will be sorted using natural sort order which means the object will be sorted by comparable or if the object does not implement comparable then it will be sorted based on a sortable field names like "name", "orderBy", "title", or a property or field ending in "Name", or the first basic comparable type found in the object. Boon follows the ideology of convention over configuration. Lastly if no suitable natural sort key can be found Boon throws an exception. In practice, it just works! We have an example of sorting an object using a natural sort but without the object being comparable.

min, max, greatest, least, and more

Boon allows you to easily perform the following operations and these build on top of Boon sorting capabilities.
max(employees);               //Sort employees and return the highest by natural order.
greatest(employees, 5);       //Return the highest five employees by natural sort order.
min(employees);               //Sort employees and return the lowest employee by natural sort order.
least(employees, 5);          //Sort the employees and return the lowest five employees by natural sort order.
max(employees, "salary");     //Return the employee with the maximum salary
greatest(employees, "salary", 5);  //Return the top five salaried employees
min(employees, "salary");          //get the lowest paid employee
least(employees, "salary", 5);     //get the lowest five employees
search(employees, employee);       //find an employee
int index = searchForIndex(employees, employee); //find the position of the employee in the list
Boon also has a full in-memory database like utility called data repo that allows you to sort using Java collections as indexes. Boon also has a filtering library. Those are covered in other Boon documents. Boon has a lot of useful features. It is more than the sum of its parts.
Let's set up an example:
First lets create a few test objects:
    static List<Department> departmentsList = list(
            new Department("Engineering").add(
                    new Employee(1, 100, "Rick", "Hightower", "555-555-1000"),
                    new Employee(2, 200, "John", "Smith", "555-555-1215", "555-555-1214", "555-555-1213"),
                    new Employee(3, 300, "Drew", "Donaldson", "555-555-1216"),
                    new Employee(4, 400, "Nick", "LaySacky", "555-555-1217")

            ),
            new Department("HR").add(
                    new Employee(5, 100, "Dianna", "Hightower", "555-555-1218"),
                    new Employee(6, 200, "Derek", "Smith", "555-555-1219"),
                    new Employee(7, 300, "Tonya", "Donaldson", "555-555-1220"),
                    new Employee(8, 400, "Sue", "LaySacky", "555-555-9999")

            ), new Department("Manufacturing").add(),
               new Department("Sales").add(),
               new Department("Marketing").add()

    );
Boon will happily sort domain object like the above, but Boon will also happily sort lists and maps, etc. Boon has added simple utility functions to make the creation of Maps and Lists easy.
    static List<?> departmentObjects = list(
            map("name", "Engineering",
                    "employees", list(
                    map("id", 1, "salary", 100, "firstName", "Rick", "lastName", "Hightower",
                            "contactInfo", map("phoneNumbers",
                            list("555-555-0000")
                    )
                    ),
                    map("id", 2, "salary", 200, "firstName", "John", "lastName", "Smith",
                            "contactInfo", map("phoneNumbers", list("555-555-1215",
                            "555-555-1214", "555-555-1213"))),
                    map("id", 3, "salary", 300, "firstName", "Drew", "lastName", "Donaldson",
                            "contactInfo", map("phoneNumbers", list("555-555-1216"))),
                    map("id", 4, "salary", 400, "firstName", "Nick", "lastName", "LaySacky",
                            "contactInfo", map("phoneNumbers", list("555-555-1217")))

            )
            ),
            map("name", "HR",
                    "employees", list(
                    map("id", 5, "salary", 100, "firstName", "Dianna", "lastName", "Hightower",
                            "contactInfo",
                            map("phoneNumbers", list("555-555-1218"))),
                    map("id", 6, "salary", 200, "firstName", "Derek", "lastName", "Smith",
                            "contactInfo",
                            map("phoneNumbers", list("555-555-1219"))),
                    map("id", 7, "salary", 300, "firstName", "Tonya", "lastName", "Donaldson",
                            "contactInfo", map("phoneNumbers", list("555-555-1220"))),
                    map("id", 8, "salary", 400, "firstName", "Sue", "lastName", "LaySacky",
                            "contactInfo", map("phoneNumbers", list("555-555-9999")))

            )
            ),
            map("name", "Manufacturing", "employees", Collections.EMPTY_LIST),
            map("name", "Sales", "employees", Collections.EMPTY_LIST),
            map("name", "Marketing", "employees", Collections.EMPTY_LIST)
            );



        Object jsonObject = fromJson(json);
        List<?> jsonDepartments = (List<?>) jsonObject;
        List<?> jsonEmployees = (List<Employee>) atIndex(jsonDepartments, "employees");


Here is the JSON that we will be working with:
[
    {
        "name": "Engineering",
        "employees": [
            {
                "id": 1,
                "salary": 100,
                "firstName": "Rick",
                "lastName": "Hightower",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-0000"
                    ]
                }
            },
            {
                "id": 2,
                "salary": 200,
                "firstName": "John",
                "lastName": "Smith",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-1215",
                        "555-555-1214",
                        "555-555-1213"
                    ]
                }
            },
            {
                "id": 3,
                "salary": 300,
                "firstName": "Drew",
                "lastName": "Donaldson",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-1216"
                    ]
                }
            },
            {
                "id": 4,
                "salary": 400,
                "firstName": "Nick",
                "lastName": "LaySacky",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-1217"
                    ]
                }
            }
        ]
    },
    {
        "name": "HR",
        "employees": [
            {
                "id": 5,
                "salary": 100,
                "firstName": "Dianna",
                "lastName": "Hightower",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-1218"
                    ]
                }
            },
            {
                "id": 6,
                "salary": 200,
                "firstName": "Derek",
                "lastName": "Smith",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-1219"
                    ]
                }
            },
            {
                "id": 7,
                "salary": 300,
                "firstName": "Tonya",
                "lastName": "Donaldson",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-1220"
                    ]
                }
            },
            {
                "id": 8,
                "salary": 400,
                "firstName": "Sue",
                "lastName": "LaySacky",
                "contactInfo": {
                    "phoneNumbers": [
                        "555-555-9999"
                    ]
                }
            }
        ]
    },
    {
        "name": "Manufacturing",
        "employees": [

        ]
    },
    {
        "name": "Sales",
        "employees": [

        ]
    },
    {
        "name": "Marketing",
        "employees": [

        ]
    }
]

First let's cover the java

        List<Employee> employees = (List<Employee>) atIndex(departmentsList, "employees");

Now we can start working with the employees. Note that puts works like Ruby puts, and putl is like puts but it puts each object on a newline. (The atIndex method uses a boon path expression to grab every employee from every department, read the short tutorial on boon path expression or just trust me that this does what I said or just assume employees is a list of employees).
        putl ( "Max employee by natural order",
                max(employees) );


        putl ( "Greatest top 5 employee by natural order",
                greatest(employees, 5) );


        putl ( "Min employee by natural order",
                min(employees) );


        putl ( "Least top 5 employee by natural order",
                least(employees, 5) );


        putl ( "Who makes the most?",
                max(employees, "salary") );


        putl ( "Who are the top five earners?",
                greatest(employees, "salary", 5) );


        putl ( "Who makes the least?",
                min(employees, "salary") );


        putl ( "Who are the lowest paid?",
                least(employees, "salary", 5) );





        sort(employees);


        sort( employees, "lastName");

        putl( "Sorted employees by lastName", employees);


        sort( departmentList );

        putl( "Sorted department in natural order", departmentList);


        sort( employees, sortBy( "department.name" ),
                         sortByDescending( "lastName" ),
                         sortBy( "firstName" ) );

        putl("Sort employees by department name, descending lastName and" +
                " ascending firstName", employees);


        sort(employees,
                sortBy("contactInfo.phoneNumbers[0]"));


        putl("Sort by phone numbers", employees);


        sort( employees,
                sortByDescending("contactInfo.phoneNumbers[0]") );


        putl("Sort by phone numbers descending", employees);

The above begets the following output:


Max employee by natural order
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'}

Greatest top 5 employee by natural order (natural order for employees is sort by first name)
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 

Min employee by natural order
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'}

Least top 5 employee by natural order
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 

Who makes the most? (sort on salary)
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'}

Who are the top five earners?
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 

Who makes the least?
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'}

Who are the lowest paid?
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 

Sorted employees by lastName
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 

Sorted department in natural order
Department{name='Engineering', employees=[1, 2, 3, 4]} 
Department{name='HR', employees=[5, 6, 7, 8]} 
Department{name='Manufacturing', employees=[]} 
Department{name='Marketing', employees=[]} 
Department{name='Sales', employees=[]} 

Sort employees by department name, descending lastName and ascending firstName
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 

Sort by phone numbers
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 

Sort by phone numbers descending
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 

Working with List/Map and JSON

You can easily sort list of maps and this works out well for JSON objects which can be read in easily and readily as list of maps (Boon ships with the fastest known JSON parser on the JVM--Yep faster that the one your thinking of, and quite a few you never heard of and that other one that is spelled funny too).

Working with lists of maps

        List<?> employeeObjects = (List<?>) atIndex(departmentObjects, "employees");

Working with json list of maps

        Object jsonObject = fromJson(json);
        List<?> jsonDepartments = (List<?>) jsonObject;
        List<?> jsonEmployees = (List<Employee>) atIndex(jsonDepartments, "employees");
The sorting code is the same as before.
Now here is the output:

______________________________ From LIST MAPS ______________________________ 


Who makes the most?
{id=4, salary=400, firstName=Nick, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-1217]}}

Who are the top five earners?
{id=4, salary=400, firstName=Nick, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-1217]}} 
{id=8, salary=400, firstName=Sue, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-9999]}} 
{id=3, salary=300, firstName=Drew, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1216]}} 
{id=7, salary=300, firstName=Tonya, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1220]}} 
{id=2, salary=200, firstName=John, lastName=Smith, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}} 

Who makes the least?
{id=1, salary=100, firstName=Rick, lastName=Hightower, contactInfo={phoneNumbers=[555-555-0000]}}

Who are the lowest paid?
{id=1, salary=100, firstName=Rick, lastName=Hightower, contactInfo={phoneNumbers=[555-555-0000]}} 
{id=5, salary=100, firstName=Dianna, lastName=Hightower, contactInfo={phoneNumbers=[555-555-1218]}} 
{id=2, salary=200, firstName=John, lastName=Smith, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}} 
{id=6, salary=200, firstName=Derek, lastName=Smith, contactInfo={phoneNumbers=[555-555-1219]}} 
{id=3, salary=300, firstName=Drew, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1216]}} 

Sorted employees by lastName
{id=3, salary=300, firstName=Drew, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1216]}} 
{id=7, salary=300, firstName=Tonya, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1220]}} 
{id=5, salary=100, firstName=Dianna, lastName=Hightower, contactInfo={phoneNumbers=[555-555-1218]}} 
{id=1, salary=100, firstName=Rick, lastName=Hightower, contactInfo={phoneNumbers=[555-555-0000]}} 
{id=4, salary=400, firstName=Nick, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-1217]}} 
{id=8, salary=400, firstName=Sue, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-9999]}} 
{id=6, salary=200, firstName=Derek, lastName=Smith, contactInfo={phoneNumbers=[555-555-1219]}} 
{id=2, salary=200, firstName=John, lastName=Smith, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}} 

Sorted department in natural order
{name=Engineering, employees=[{id=1, salary=100, firstName=Rick, lastName=Hightower, contactInfo={phoneNumbers=[555-555-0000]}}, {id=2, salary=200, firstName=John, lastName=Smith, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}}, {id=3, salary=300, firstName=Drew, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1216]}}, {id=4, salary=400, firstName=Nick, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-1217]}}]} 
{name=HR, employees=[{id=5, salary=100, firstName=Dianna, lastName=Hightower, contactInfo={phoneNumbers=[555-555-1218]}}, {id=6, salary=200, firstName=Derek, lastName=Smith, contactInfo={phoneNumbers=[555-555-1219]}}, {id=7, salary=300, firstName=Tonya, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1220]}}, {id=8, salary=400, firstName=Sue, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-9999]}}]} 
{name=Manufacturing, employees=[]} 
{name=Sales, employees=[]} 
{name=Marketing, employees=[]} 

Sort by phone numbers
{id=1, salary=100, firstName=Rick, lastName=Hightower, contactInfo={phoneNumbers=[555-555-0000]}} 
{id=2, salary=200, firstName=John, lastName=Smith, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}} 
{id=3, salary=300, firstName=Drew, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1216]}} 
{id=4, salary=400, firstName=Nick, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-1217]}} 
{id=5, salary=100, firstName=Dianna, lastName=Hightower, contactInfo={phoneNumbers=[555-555-1218]}} 
{id=6, salary=200, firstName=Derek, lastName=Smith, contactInfo={phoneNumbers=[555-555-1219]}} 
{id=7, salary=300, firstName=Tonya, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1220]}} 
{id=8, salary=400, firstName=Sue, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-9999]}} 

Sort by phone numbers descending
{id=8, salary=400, firstName=Sue, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-9999]}} 
{id=7, salary=300, firstName=Tonya, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1220]}} 
{id=6, salary=200, firstName=Derek, lastName=Smith, contactInfo={phoneNumbers=[555-555-1219]}} 
{id=5, salary=100, firstName=Dianna, lastName=Hightower, contactInfo={phoneNumbers=[555-555-1218]}} 
{id=4, salary=400, firstName=Nick, lastName=LaySacky, contactInfo={phoneNumbers=[555-555-1217]}} 
{id=3, salary=300, firstName=Drew, lastName=Donaldson, contactInfo={phoneNumbers=[555-555-1216]}} 
{id=2, salary=200, firstName=John, lastName=Smith, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}} 
{id=1, salary=100, firstName=Rick, lastName=Hightower, contactInfo={phoneNumbers=[555-555-0000]}} 

______________________________ From JSON ______________________________ 

Who makes the most?
{id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick}

Who are the top five earners?
{id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick} 
{id=8, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-9999]}, firstName=Sue} 
{id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew} 
{id=7, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1220]}, firstName=Tonya} 
{id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John} 

Who makes the least?
{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick}

Who are the lowest paid?
{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick} 
{id=5, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-1218]}, firstName=Dianna} 
{id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John} 
{id=6, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1219]}, firstName=Derek} 
{id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew} 

Sorted employees by lastName
{id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew} 
{id=7, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1220]}, firstName=Tonya} 
{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick} 
{id=5, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-1218]}, firstName=Dianna} 
{id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick} 
{id=8, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-9999]}, firstName=Sue} 
{id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John} 
{id=6, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1219]}, firstName=Derek} 

Sorted department in natural order
{employees=[{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick}, {id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John}, {id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew}, {id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick}], name=Engineering} 
{employees=[{id=5, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-1218]}, firstName=Dianna}, {id=6, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1219]}, firstName=Derek}, {id=7, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1220]}, firstName=Tonya}, {id=8, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-9999]}, firstName=Sue}], name=HR} 
{employees=[], name=Manufacturing} 
{employees=[], name=Sales} 
{employees=[], name=Marketing} 

Sort employees by department name, descending lastName and ascending firstName
{id=6, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1219]}, firstName=Derek} 
{id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John} 
{id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick} 
{id=8, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-9999]}, firstName=Sue} 
{id=5, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-1218]}, firstName=Dianna} 
{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick} 
{id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew} 
{id=7, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1220]}, firstName=Tonya} 

Sort by phone numbers
{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick} 
{id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John} 
{id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew} 
{id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick} 
{id=5, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-1218]}, firstName=Dianna} 
{id=6, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1219]}, firstName=Derek} 
{id=7, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1220]}, firstName=Tonya} 
{id=8, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-9999]}, firstName=Sue} 

Sort by phone numbers descending
{id=8, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-9999]}, firstName=Sue} 
{id=7, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1220]}, firstName=Tonya} 
{id=6, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1219]}, firstName=Derek} 
{id=5, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-1218]}, firstName=Dianna} 
{id=4, lastName=LaySacky, salary=400, contactInfo={phoneNumbers=[555-555-1217]}, firstName=Nick} 
{id=3, lastName=Donaldson, salary=300, contactInfo={phoneNumbers=[555-555-1216]}, firstName=Drew} 
{id=2, lastName=Smith, salary=200, contactInfo={phoneNumbers=[555-555-1215, 555-555-1214, 555-555-1213]}, firstName=John} 
{id=1, lastName=Hightower, salary=100, contactInfo={phoneNumbers=[555-555-0000]}, firstName=Rick} 


Binary search

        //Using Search
        sort(employees);
        Employee employee = employees.get(5);


        Employee employee2 = search(employees, employee);
        int index = searchForIndex(employees, employee);

        putl("This employee was found at ", index, employee2, employee, '\n', employees);

Now let's see the output from the search.
This employee was found at 
5
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'}
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'}


Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 


Works with arrays, iterable and all sorts of fun stuff

Boon can work with Lists (which I prefer for sorting), but it can also work with arrays, collections, and iterables.

Sorting a set

        Set<Employee> employeesSet = new LinkedHashSet<>(employees);

        employeesSet = (Set<Employee>) sort(Employee.class, employeesSet, "id");

        putl("Sort Employee SET by id", employeesSet);
Sort Employee SET by id
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 


Sorting an array

        Employee[] employeesArray = toArray(Employee.class, employees);

        sort(employeesArray, "firstName");

        putl("Sort Employee ARRAY by firstName", employeesArray);
Note Boon has a utility function that converts any collection into an array called toArray. So first we create the employees list into an array and then we sort it by firstName and print out the results with putl.
Sort Employee ARRAY by firstName
Employee{id=6, salary=200, department=HR, phone number=555-555-1219, firstName='Derek', lastName='Smith'} 
Employee{id=5, salary=100, department=HR, phone number=555-555-1218, firstName='Dianna', lastName='Hightower'} 
Employee{id=3, salary=300, department=Engineering, phone number=555-555-1216, firstName='Drew', lastName='Donaldson'} 
Employee{id=2, salary=200, department=Engineering, phone number=555-555-1215, firstName='John', lastName='Smith'} 
Employee{id=4, salary=400, department=Engineering, phone number=555-555-1217, firstName='Nick', lastName='LaySacky'} 
Employee{id=1, salary=100, department=Engineering, phone number=555-555-1000, firstName='Rick', lastName='Hightower'} 
Employee{id=8, salary=400, department=HR, phone number=555-555-9999, firstName='Sue', lastName='LaySacky'} 
Employee{id=7, salary=300, department=HR, phone number=555-555-1220, firstName='Tonya', lastName='Donaldson'} 

model objects / classes for this example

    public class ContactInfo {
        String address;
        List<String> phoneNumbers;


    }

    public class Employee implements Comparable<Employee> {
        int id;
        int salary;
        String firstName;
        String lastName;

        ContactInfo contactInfo = new ContactInfo();
        Department department;


        public Employee(int id, int salary, String firstName, String lastName,
                        String... phoneNumbers) {
            this.id = id;
            this.salary = salary;
            this.firstName = firstName;
            this.lastName = lastName;

            for (String phone : phoneNumbers) {
                contactInfo.phoneNumbers = lazyAdd(contactInfo.phoneNumbers, phone);
            }
        }

        ...//left off getter / setter / equals and hashCode

        @Override
        public String toString() {
            return "Employee{" +
                    "id=" + id +
                    ", salary=" + salary +
                    ", department=" + (department == null ? "NONE" : department.getName()) +
                    ", phone number=" + atIndex (this, "contactInfo.phoneNumbers[0]") +
                    ", firstName='" + firstName + '\'' +
                    ", lastName='" + lastName + '\'' +
                    "}";
        }

        @Override
        public int compareTo(Employee otherEmployee) {
            return this.firstName.compareTo(otherEmployee.firstName);
        }

        public void setDepartment(Department department) {
            this.department =  department;
        }
    }

    //Notice that Department is not comparable but boon found and used the name property to make it have
   // a "natural" sort order.
    public class Department {
        private String name;

        private List<Employee> employees;

        public Department() {
        }

        public Department(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Department add(Employee... employees) {

            for (Employee employee : employees) {
                employee.setDepartment(this);
            }
            this.employees = lazyAdd(this.employees, employees); 
            return this;
        }

        ... //left off getter / setters equals and friends
        @Override
        public String toString() {
            return "Department{" +
                    "name='" + name + '\'' +
                    ", employees=" + atIndex(employees, "id") +
                    '}';
        }
    }

Complete listing for example

package com.examples;

import static org.boon.core.Conversions.toArray;
import static org.boon.sort.Sort.sortBy;
import static org.boon.sort.Sort.sortByDescending;
import static org.boon.sort.Sorting.*;

import static org.boon.sort.Ordering.*;

import org.junit.Test;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import static org.boon.Boon.*;
import static org.boon.Boon.puts;
import static org.boon.Lists.lazyAdd;
import static org.boon.Lists.list;
import static org.boon.Maps.map;
import static org.boon.Ok.okOrDie;
import static org.boon.core.reflection.BeanUtils.atIndex;
import static org.boon.primitive.Chr.multiply;

public class SortingObjects {


    @Test
    public void test() {
        SortingObjects.main();

    }

    public static class ContactInfo {
        String address;
        List<String> phoneNumbers;


    }

    public static class Employee implements Comparable<Employee> {
        int id;
        int salary;
        String firstName;
        String lastName;

        ContactInfo contactInfo = new ContactInfo();
        Department department;

        public Employee() {
        }

        public Employee(int id, int salary, String firstName, String lastName,
                        String... phoneNumbers) {
            this.id = id;
            this.salary = salary;
            this.firstName = firstName;
            this.lastName = lastName;

            for (String phone : phoneNumbers) {
                contactInfo.phoneNumbers = lazyAdd(contactInfo.phoneNumbers, phone);
            }
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public int getSalary() {
            return salary;
        }

        public void setSalary(int salary) {
            this.salary = salary;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Employee employee = (Employee) o;

            if (id != employee.id) return false;
            if (salary != employee.salary) return false;
            if (firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) return false;
            if (lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = id;
            result = 31 * result + salary;
            result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
            result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
            return result;
        }

        @Override
        public String toString() {
            return "Employee{" +
                    "id=" + id +
                    ", salary=" + salary +
                    ", department=" + (department == null ? "NONE" : department.getName()) +
                    ", phone number=" + atIndex (this, "contactInfo.phoneNumbers[0]") +
                    ", firstName='" + firstName + '\'' +
                    ", lastName='" + lastName + '\'' +
                    "}";
        }

        @Override
        public int compareTo(Employee otherEmployee) {
            return this.firstName.compareTo(otherEmployee.firstName);
        }

        public void setDepartment(Department department) {
            this.department =  department;
        }
    }

    public static class Department {
        private String name;

        private List<Employee> employees;

        public Department() {
        }

        public Department(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Department add(Employee... employees) {

            for (Employee employee : employees) {
                employee.setDepartment(this);
            }
            this.employees = lazyAdd(this.employees, employees);
            return this;
        }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Department that = (Department) o;

            if (employees != null ? !employees.equals(that.employees) : that.employees != null) return false;
            if (name != null ? !name.equals(that.name) : that.name != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (employees != null ? employees.hashCode() : 0);
            return result;
        }

        @Override
        public String toString() {
            return "Department{" +
                    "name='" + name + '\'' +
                    ", employees=" + atIndex(employees, "id") +
                    '}';
        }
    }


    static List<Department> departmentsList = list(
            new Department("Engineering").add(
                    new Employee(1, 100, "Rick", "Hightower", "555-555-1000"),
                    new Employee(2, 200, "John", "Smith", "555-555-1215", "555-555-1214", "555-555-1213"),
                    new Employee(3, 300, "Drew", "Donaldson", "555-555-1216"),
                    new Employee(4, 400, "Nick", "LaySacky", "555-555-1217")

            ),
            new Department("HR").add(
                    new Employee(5, 100, "Dianna", "Hightower", "555-555-1218"),
                    new Employee(6, 200, "Derek", "Smith", "555-555-1219"),
                    new Employee(7, 300, "Tonya", "Donaldson", "555-555-1220"),
                    new Employee(8, 400, "Sue", "LaySacky", "555-555-9999")

            ), new Department("Manufacturing").add(),
               new Department("Sales").add(),
               new Department("Marketing").add()

    );

    static List<?> departmentObjects = list(
            map("name", "Engineering",
                    "employees", list(
                    map("id", 1, "salary", 100, "firstName", "Rick", "lastName", "Hightower",
                            "contactInfo", map("phoneNumbers",
                            list("555-555-0000")
                    )
                    ),
                    map("id", 2, "salary", 200, "firstName", "John", "lastName", "Smith",
                            "contactInfo", map("phoneNumbers", list("555-555-1215",
                            "555-555-1214", "555-555-1213"))),
                    map("id", 3, "salary", 300, "firstName", "Drew", "lastName", "Donaldson",
                            "contactInfo", map("phoneNumbers", list("555-555-1216"))),
                    map("id", 4, "salary", 400, "firstName", "Nick", "lastName", "LaySacky",
                            "contactInfo", map("phoneNumbers", list("555-555-1217")))

            )
            ),
            map("name", "HR",
                    "employees", list(
                    map("id", 5, "salary", 100, "firstName", "Dianna", "lastName", "Hightower",
                            "contactInfo",
                            map("phoneNumbers", list("555-555-1218"))),
                    map("id", 6, "salary", 200, "firstName", "Derek", "lastName", "Smith",
                            "contactInfo",
                            map("phoneNumbers", list("555-555-1219"))),
                    map("id", 7, "salary", 300, "firstName", "Tonya", "lastName", "Donaldson",
                            "contactInfo", map("phoneNumbers", list("555-555-1220"))),
                    map("id", 8, "salary", 400, "firstName", "Sue", "lastName", "LaySacky",
                            "contactInfo", map("phoneNumbers", list("555-555-9999")))

            )
            ),
            map("name", "Manufacturing", "employees", Collections.EMPTY_LIST),
            map("name", "Sales", "employees", Collections.EMPTY_LIST),
            map("name", "Marketing", "employees", Collections.EMPTY_LIST)
            );
    static boolean ok;


    public static void main(String... args) {


        puts(multiply('_', 30), "From JAVA Objects", multiply('_', 30), "\n");

        List<Employee> employees = (List<Employee>) atIndex(departmentsList, "employees");

        sorting(employees, departmentsList);

        puts(multiply('_', 30), "From LIST MAPS", multiply('_', 30), "\n");

        List<?> employeeObjects = (List<?>) atIndex(departmentObjects, "employees");

        sorting(employeeObjects, departmentObjects);


        puts(multiply('_', 30), "From JSON", multiply('_', 30), "\n");


        String json = toJson(departmentObjects);
        puts(json);
        Object jsonObject = fromJson(json);
        List<?> jsonDepartments = (List<?>) jsonObject;
        List<?> jsonEmployees = (List<Employee>) atIndex(jsonDepartments, "employees");

        sorting(jsonEmployees, jsonDepartments);



        //Using Search
        sort(employees);
        Employee employee = employees.get(5);


        Employee employee2 = search(employees, employee);
        int index = searchForIndex(employees, employee);

        putl("This employee was found at ", index, employee2, employee, '\n', employees);

        //Using a Set instead of a list

        Set<Employee> employeesSet = new LinkedHashSet<>(employees);

        employeesSet = (Set<Employee>) sort(Employee.class, employeesSet, "id");

        putl("Sort Employee SET by id", employeesSet);


        Employee[] employeesArray = toArray(Employee.class, employees);

        sort(employeesArray, "firstName");

        putl("Sort Employee ARRAY by firstName", employeesArray);


    }

    private static void sorting(List<?> employees, List<?> departmentList) {



        putl ( "Max employee by natural order",
                max(employees) );


        putl ( "Greatest top 5 employee by natural order",
                greatest(employees, 5) );


        putl ( "Min employee by natural order",
                min(employees) );


        putl ( "Least top 5 employee by natural order",
                least(employees, 5) );


        putl ( "Who makes the most?",
                max(employees, "salary") );


        putl ( "Who are the top five earners?",
                greatest(employees, "salary", 5) );


        putl ( "Who makes the least?",
                min(employees, "salary") );


        putl ( "Who are the lowest paid?",
                least(employees, "salary", 5) );





        sort(employees);


        sort( employees, "lastName");

        putl( "Sorted employees by lastName", employees);


        sort( departmentList );

        putl( "Sorted department in natural order", departmentList);


        sort( employees, sortBy( "department.name" ),
                         sortByDescending( "lastName" ),
                         sortBy( "firstName" ) );

        putl("Sort employees by department name, descending lastName and" +
                " ascending firstName", employees);


        sort(employees,
                sortBy("contactInfo.phoneNumbers[0]"));


        putl("Sort by phone numbers", employees);


        sort( employees,
                sortByDescending("contactInfo.phoneNumbers[0]") );


        putl("Sort by phone numbers descending", employees);
    }


}




There is a lot more to boon than this

If this piqued your interests you should read up on Boon path expressions which are used in this example. You should also read about Boon data repo which is an in-memory query engine for Java class instances and Java collections. Boon has sorting, filtering, validation, path expressions, and an in-memory query engine. Boon also supports JSON via dependency injection, JSON configuration files, JSON parsing, JSON serializing, JSON side-ways serializing for speed (read / write from arrays instead ob JSON objects), and JSON to Java call marshaling. Boon also has a handlebars based template lib. Come explore Boon.

Thoughts

Thoughts? Write me at richard high tower AT g mail dot c-o-m (Rick Hightower).

Further Reading:

If you are new to boon start here:

Why Boon?

Easily read in files into lines or a giant string with one method call. Works with files, URLs, class-path, etc. Boon IO support will surprise you how easy it is. Boon has Slice notation for dealing with Strings, Lists, primitive arrays, Tree Maps, etc. If you are from Groovy land, Ruby land, Python land, or whatever land, and you have to use Java then Boon might give you some relief from API bloat. If you are like me, and you like to use Java, then Boon is for you too. Boon lets Java be Java, but adds the missing productive APIs from Python, Ruby, and Groovy. Boon may not be Ruby or Groovy, but its a real Boon to Java development.

Core Boon Philosophy

Core Boon will never have any dependencies. It will always be able to run as a single jar. This is not just NIH, but it is partly. My view of what Java needs is more inline with what Python, Ruby and Groovy provide. Boon is an addition on top of the JVM to make up the difference between the harder to use APIs that come with Java and the types of utilities that are built into Ruby, Python, PHP, Groovy etc. Boon is a Java centric view of those libs. The vision of Boon and the current implementation is really far apart.

Contact Info
Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training