Scala – sort/sortWith/sortBy functions

In this post, we are going to see how we can use scala sort related function with examples.

Assume that we have a list of fruits and we want to sort the fruit name in different order.

Refer the below code to know how to do that.



object SortExample {


  def main(args: Array[String]): Unit = {

    val fruits = List("Water Melon", "Banana", "Carrot", "Apple", "Plums", "Orange")

    //To sort a list by ascending order
    println(fruits.sorted)

    //To sort a list by descending order
    println(fruits.sorted.reverse)

    //To sort a list by length
    println(fruits.sortWith(_.length > _.length))

    //To sort a list by length(descending)
    println(fruits.sortWith(_.length < _.length))

  }
}

The output of the above code is given below,


List(Apple, Banana, Carrot, Orange, Plums, Water Melon)
List(Water Melon, Plums, Orange, Carrot, Banana, Apple)
List(Water Melon, Banana, Carrot, Orange, Apple, Plums)
List(Apple, Plums, Banana, Carrot, Orange, Water Melon)

Now, We will see how we can use sortBy function to sort a list.

The definition of the sortBy function is given below,



def sortBy[B](f: (A) ⇒ B)(implicit ord: math.Ordering[B]): List[A]
Sorts this Seq according to the Ordering which results from transforming an implicitly given Ordering with a transformation function.


Refer the below code, where we use sortBy function to sort the employee objects list in three different ways.


object SortByExample {

  case class Employee(name: String,
                      age: Int,
                      designation: String,
                      salary: Int)

  def main(args: Array[String]): Unit = {

    val employees = List(Employee("Bala", 31, "Developer", 500000),
      Employee("John", 33, "Tester", 200000),
      Employee("Bob", 35, "Tech Lead", 300000),
      Employee("Woods", 30, "Manager", 400000),
      Employee("Robert", 30, "Tester", 200000),
      Employee("Jon", 30, "Tester", 100000),
      Employee("Paul", 26, "Sales Person", 100000),
      Employee("Jason", 25, "Sales Executive", 100000),
      Employee("Jill", 25, "Sales Executive", 200000),
      Employee("Steve", 27, "Product Owner", 500000))

    employees
      .sortBy(_.age)
      .foreach(println(_))

    employees
      .sortBy(x => (x.salary, x.age))
      .foreach(println(_))

    val orderForDesignation = Map("Product Owner" -> 0, "Manager" -> 1, "Tech Lead" -> 2, "Developer" -> 3, "Tester" -> 4)

    employees
      .sortBy(x => (orderForDesignation.get(x.designation).orElse(Some(10)), x.name))
     .foreach(println(_))

  }

}

We have Employee case class and it contains name, salary, designation and age fields and we create a list with different details, then we call sortBy function and pass the age field to sort the list by age.

The output looks like below,


Employee(Jason,25,Sales Executive,100000)
Employee(Jill,25,Sales Executive,200000)
Employee(Paul,26,Sales Person,100000)
Employee(Steve,27,Product Owner,500000)
Employee(Woods,30,Manager,400000)
Employee(Robert,30,Tester,200000)
Employee(Jon,30,Tester,100000)
Employee(Bala,31,Developer,500000)
Employee(John,33,Tester,200000)
Employee(Bob,35,Tech Lead,300000)

Now we want to sort the list by salary and then age. To do that we have to pass the salary and age fields to sortBy function in the right order.
The output looks like below,


Employee(Jason,25,Sales Executive,100000)
Employee(Paul,26,Sales Person,100000)
Employee(Jon,30,Tester,100000)
Employee(Jill,25,Sales Executive,200000)
Employee(Robert,30,Tester,200000)
Employee(John,33,Tester,200000)
Employee(Bob,35,Tech Lead,300000)
Employee(Woods,30,Manager,400000)
Employee(Steve,27,Product Owner,500000)
Employee(Bala,31,Developer,500000)


Now, we want to sort it by designation but follow a different order. The order should be like this “Product Owner -> Manager -> Tech Lead -> Developer -> Tester.

To do that we have to maintain a Map contains designation to order mapping and this Map to be used in sortBy function and we also use name as secondary sorting.

The output looks like below,


Employee(Steve,27,Product Owner,500000)
Employee(Woods,30,Manager,400000)
Employee(Bob,35,Tech Lead,300000)
Employee(Bala,31,Developer,500000)
Employee(John,33,Tester,200000)
Employee(Jon,30,Tester,100000)
Employee(Robert,30,Tester,200000)
Employee(Jason,25,Sales Executive,100000)
Employee(Jill,25,Sales Executive,200000)
Employee(Paul,26,Sales Person,100000)


Advertisements

Java 8 – groupingBy Collector

In this post, we will see how to use groupingBy collector with an example.

Consider that we have a person class and it contains id, age, fullname and city fields. We have a list of person objects and we want to group by age. Lets see how we can do that with an example.

Here is the Person class.

Person.java



import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class Person {

    private String id;

    private int age;

    private String fullName;

    private String city;

    public Person(String id, int age, String fullName, String city){
        this.id = id;
        this.age = age;
        this.fullName = fullName;
        this.city = city;
    }

    public String getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    public String getFullName() {
        return fullName;
    }

    public String getCity() {
        return city;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        return new EqualsBuilder()
                .append(age, person.age)
                .append(id, person.id)
                .append(fullName, person.fullName)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(id)
                .append(age)
                .append(fullName)
                .toHashCode();
    }
}


To group the person list by age, the code is given below,


 //Group the person list by age
        Map<Integer, List> personListByAge = personList
                .stream()
                .collect(groupingBy(Person :: getAge));

The output will look like below,


{36=[id1,36,Peter,columbus, id3,36,Alex,hopkins], 37=[id2,37,John,columbus], 30=[id4,30,Ram,new york, id5,30,Bala,houston, id5,30,Bala,houston]}

By default, the groupingby collector returns a map with value as list of person objects. If you want to remove duplicates and collect the objects as set, then we have to pass the downstream parameter such as toSet().


  //Group the person list by age and remove duplicates
        Map<Integer, Set> personListByAgeNoDuplicates = personList
                .stream()
                .collect(groupingBy(Person :: getAge, toSet()));

The output will look like below,


{36=[id1,36,Peter,columbus, id3,36,Alex,hopkins], 37=[id2,37,John,columbus], 30=[id5,30,Bala,houston, id4,30,Ram,new york]}


If you want to group a person list by age and then again group the results by city, then we have to pass the groupingby(city) collector inside of the first groupingby.

Note that this will return a Map and an inner map which contains the group by field as key and the list of person objects.


 //Group the person list by age and then group the result of the first group by city

        Map<Integer, Map<String, List>> personListByAgeAndCity = personList
                .stream()
                .collect(groupingBy(Person :: getAge, groupingBy(Person :: getCity)));



The output will look like below,


{36={hopkins=[id3,36,Alex,hopkins], columbus=[id1,36,Peter,columbus]}, 37={columbus=[id2,37,John,columbus]}, 30={houston=[id5,30,Bala,houston, id5,30,Bala,houston], new york=[id4,30,Ram,new york]}}


The code is given below,

GroupByMain.java



import com.google.common.collect.Lists;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toSet;

public class GroupByMain {

    public static void main(String[] args) {

        Comparator comparator = Comparator.comparing(Person :: getAge);
        Person person1 = new Person("id1", 36, "Peter","columbus" );
        Person person2 = new Person("id2", 37, "John", "columbus");
        Person person3 = new Person("id3", 36, "Alex", "hopkins");
        Person person4 = new Person("id4", 30, "Ram", "new york");
        Person person5 = new Person("id5", 30, "Bala", "houston");
        Person person6 = new Person("id5", 30, "Bala", "houston");

        List personList = Lists.newArrayList(person1, person2, person3, person4, person5, person6);

        //Group the person list by age
        Map<Integer, List> personListByAge = personList
                .stream()
                .collect(groupingBy(Person :: getAge));

        System.out.println(personListByAge);

        //Group the person list by age and remove duplicates
        Map<Integer, Set> personListByAgeNoDuplicates = personList
                .stream()
                .collect(groupingBy(Person :: getAge, toSet()));

        System.out.println(personListByAgeNoDuplicates);

        //Group the person list by age and then group the result of the first group by city

        Map<Integer, Map<String, List>> personListByAgeAndCity = personList
                .stream()
                .collect(groupingBy(Person :: getAge, groupingBy(Person :: getCity)));

        System.out.println(personListByAgeAndCity);

    }
}