Wednesday, January 16, 2013

Scala: Collections 1

This post contains some info on Scala's collections.

Problem?

We want a function that will take an List of Rugby players as input and return those players names that play for Leinster and can run the 100 meters from the fastest to the slowest.

 

Step 1: Have a representation for a Rugby player.

Ok so it's obvious we want something like a POJO to represent a Rugby player.  This representation should have a player's name, their team and the time they can the 100 meters in.  Let's use Scala case class construct which removes the need for boiler plate code.
case class RugbyPlayerCaseClass(team: String, sprintTime100M: BigDecimal, name: String)

 

Step 2: Create some rugby players

val lukeFitzGerald = RugbyPlayerCaseClass("Leinster", 10.2, "Luke Fitzgerald");
val fergusMcFadden = RugbyPlayerCaseClass("Leinster", 10.1, "Fergus McFadden");
val rog = RugbyPlayerCaseClass("Leinster", 12, "Ronan O'Gara");
val tommyBowe = RugbyPlayerCaseClass("Ulster", 10.3, "Tommy Bowe");
val leoCullen = RugbyPlayerCaseClass("Leinster", 15, "Leo Cullen");

The code above should be self explanatory. The various rugby players are instantiated.  Note the inferred typing. There is no need to declare any of the rugby players as RugbyPlayers types. Instead, it is inferred.  Another thing that is interesting is the keyword val is used.  This means the reference is immutable  It is the equivalent to final in Java.

 

Step 3: Write the function

def filterValidPlayers(in: List[RugbyPlayerCaseClass]) = 
     in.filter(_.team == "Leinster").sortWith(_.sprintTime100M < _.sprintTime100M).map(_.name);

Key points regarding this function:
  • The function begins with def keyword signifying a function declartion.
  • A List of RugbyPlayerCaseClass instances are taken in as input. The List type is a Scala type.  
  • The return type is optional. In this case it is not explictly specified as it is inferred.
  • The part to the left of the = is what the function does. In this case the function invokes three difference collection operators.
    • .filter(_.team =="Leinster)  - this iterates over every element in the List. In each iteration the _ is filled in with the current value in the List. If the team property of the current Rugby player is Leinster the element is included in the resulting collection.
    • .sortWith(_.sprintTime100M < _.sprintTime100M) - sortWith is a special method which we can use to sort collections. In this case, we our sorting the output fromthe previous collection operator and we are sorting based on the sprintTime for 100M.
    • .map(_.name) - this maps every element from the output of the sort operator to just ther name property.
  • The function body does not need to be surrounded by {} because it is only one line code.
  • There is no return statement needed. In Scala, whatever the last line evaluates to will be returned. In this example since there only is one line, the last line is the first line.

Finally - put it all together.

object RugbyPlayerCollectionDemos {
  def main(args: Array[String]){
    println("Scala collections stuff!"); 
    showSomeFilterTricks();
  }
  
  // Case class remove need for boiler plater code.
  case class RugbyPlayerCaseClass(team: String, sprintTime100M: BigDecimal, name: String)
      
  def showSomeFilterTricks() {
  
    // team: String, sprintTime100M: Int, name: String
    val lukeFitzGerald = RugbyPlayerCaseClass("Leinster", 10.2, "Luke Fitzgerald");
    val fergusMcFadden = RugbyPlayerCaseClass("Leinster", 10.1, "Fergus McFadden");
    val rog = RugbyPlayerCaseClass("Munster", 12, "Ronan O'Gara");
    val tommyBowe = RugbyPlayerCaseClass("Ulster", 10.3, "Tommy Bowe");
    val leoCullen = RugbyPlayerCaseClass("Leinster", 15, "Leo Cullen");
    
    println(filterValidPlayers(List(lukeFitzGerald, fergusMcFadden, rog, tommyBowe, leoCullen)));

  }
  
  def filterValidPlayers(in: List[RugbyPlayerCaseClass]) = 
     in.filter(_.team == "Leinster").sortWith(_.sprintTime100M < _.sprintTime100M).map(_.name);

}
The above program will output:
Scala collections stuff!
List(Luke Fitzgerald, Fergus McFadden, Leo Cullen) 

Something similar in Java

Pre Java 8, to implement the same functionality in Java would be a lot more code.
public class RugbyPLayerCollectionDemos { 
    public static void main(String args[]) {
     RugbyPLayerCollectionDemos collectionDemos = new RugbyPLayerCollectionDemos();
     collectionDemos.showSomeFilterTricks();
    }
    
    public void showSomeFilterTricks() {
        // team: String, sprintTime100M: Int, name: String
        final RugbyPlayerPOJO lukeFitzGerald = new RugbyPlayerPOJO("Leinster", new BigDecimal(10.2), "Luke Fitzgerald");
        final RugbyPlayerPOJO fergusMcFadden = new RugbyPlayerPOJO("Leinster", new BigDecimal(10.1), "Fergus McFadden");
        final RugbyPlayerPOJO rog = new RugbyPlayerPOJO("Munster", new BigDecimal(12), "Ronan O'Gara");
        final RugbyPlayerPOJO tommyBowe = new RugbyPlayerPOJO("Ulster", new BigDecimal(10.3), "Tommy Bowe");
        final RugbyPlayerPOJO leoCullen = new RugbyPlayerPOJO("Leinster", new BigDecimal(15), "Leo Cullen");
          
        List rugbyPlayers = Arrays.asList(lukeFitzGerald, 
          fergusMcFadden, rog, tommyBowe, leoCullen);
        
        System.out.println(filterRugbyPlayers(rugbyPlayers));
    }
    
    
    /**
     * Return the names of Leinster Rugby players in the order of their sprint times.
     */
    public List filterRugbyPlayers(List pojos) {
        ArrayList leinsterRugbyPlayers = new    ArrayList();
     
        for (RugbyPlayerPOJO pojo: pojos) {
            if (pojo.getTeam().equals("Leinster")) {
               leinsterRugbyPlayers.add(pojo);
            } 
        }
     
        RugbyPlayerPOJO [] rugbyPlayersAsArray = leinsterRugbyPlayers.toArray(new   RugbyPlayerPOJO[0]);
     
        Arrays.sort(rugbyPlayersAsArray, new Comparator() {
            public int compare(RugbyPlayerPOJO rugbyPlayer1, RugbyPlayerPOJO rugbyPlayer2) {
                 return rugbyPlayer1.getSprintTime100M().compareTo(rugbyPlayer2.getSprintTime100M());
           }
        });  
     
        List rugbyPlayersNamesToReturn = new ArrayList();
       
        for (RugbyPlayerPOJO rugbyPlayerPOJO: rugbyPlayersAsArray) {
             rugbyPlayersNamesToReturn.add(rugbyPlayerPOJO.getName());
        }
     
        return rugbyPlayersNamesToReturn;
    }
    
    class RugbyPlayerPOJO {
        private BigDecimal sprintTime100M;
        private String team;
        private String name;
     
        public RugbyPlayerPOJO(String team, java.math.BigDecimal sprintTime100M, String name) {
            this.name = name;
            this.sprintTime100M = sprintTime100M;
            this.team = team;
        } 
     
        public BigDecimal getSprintTime100M() {
            return sprintTime100M;
        }
     
        public String getTeam() {
            return team;
        }
     
        public String getName() {
            return name;
        }
    }
}

Does Java 8 help out?

Yes. According to the Project Lambda specsJava 8 will have similar looking filter,map and sort functions. The functionality in this post in Java 8 would look something like:
List rugbyPlayers = Arrays.asList(lukeFitzGerald, 
  fergusMcFadden, rog, tommyBowe, leoCullen);
//...
//...
List filteredPLayersNames = rugbyPlayers.filter(e -> e.getTeam.equals("Leinster")).
 sorted((a, b) -> a.getSprintTime100M() - b.getSprintTime100M()).mapped(e -> {return e.getName();}).into(new List<>());
So Java 8 is definetly catching up a great deal in this regard. But will it be enough?

No comments:

Post a Comment