22 April 2010

Building a map from a list by executing functions on its elements

Valid since: op4j 1.0

Description
Build a map by iterating the elements of a list and executing two functions on each of them: one for obtaining the key of the corresponding entry, and another one for obtaining the value.

Scenario
Our Country class looks like this:
public class Country {
    private final String name;
    private final Integer population;
    ...
    public String getName() {
        return this.name;
    }
    public Integer getPopulation() {
        return this.population;
    }
}
And we have a List<Country> countries variable containing data about some countries:
// countries == LIST [
//                     COUNTRY [ "Spain"; 45989016 ],
//                     COUNTRY [ "France"; 65447374 ],
//                     COUNTRY [ "Portugal"; 10707924 ],
//                     COUNTRY [ "United Kingdom"; 62041708 ],
//                     COUNTRY [ "Ireland"; 4459300 ],
//                   ]
...which we want to transform into a Map<String,Integer> containing the population of each country, indexed by the country name:
// populationByCountry == MAP [
//                              {"Spain": 45989016},
//                              {"France": 65447374},
//                              {"Portugal": 10707924},
//                              {"United Kingdom": 62041708},
//                              {"Ireland": 4459300},
//                            ]

Recipe
The toMap(keyFunction, valueFunction) action, applied on the list, will let us specify two functions: one for obtaining map keys out of the original list elements, and another one for obtaining their corresponding values. We will use getters for both:

Map<String,Integer> populationByCountry =
    Op.on(countries).
        toMap(Get.attrOfString("name"), Get.attrOfInteger("population")).get();

Comments
And what if we wanted the result map to be ordered by population, from the highest to the lowest figure? Something like:
// populationByCountry == MAP [
//                              {"France": 65447374},
//                              {"United Kingdom": 62041708},
//                              {"Spain": 45989016},
//                              {"Portugal": 10707924},
//                              {"Ireland": 4459300},
//                            ]
We could solve it in two ways:
  1. First sorting the list by population, then creating the map.
  2. First creating the map, then sorting its entries by value.
Let's try ordering the list first:
// INCORRECT: Order will be ascending!
Map<String,Integer> populationByCountry =
    Op.on(countries).
        sortBy(Get.attrOfInteger("population")).
        toMap(Get.attrOfString("name"), Get.attrOfInteger("population")).get();
There is a problem there: sortBy will use the specified function for sorting, but sorting will be made according to natural order, and this will mean that less-populated countries will come first (opposite to what we want). So we will have to reverse the list once sorted:
Map<String,Integer> populationByCountry =
    Op.on(countries).
        sortBy(Get.attrOfInteger("population")).reverse().
        toMap(Get.attrOfString("name"), Get.attrOfInteger("population")).get();
Now it is correct. Finally, let's try the create map first, then sort approach. This time we will have to sort the map by its entry values...
Map<String,Integer> populationByCountry =
    Op.on(countries).
        toMap(Get.attrOfString("name"), Get.attrOfInteger("population")).
        sortBy(Get.attrOfInteger("value")).reverse().get();

21 April 2010

Executing an op4j function directly (without an expression)

Valid since: op4j 1.0

Description
Execute an op4j predefined function (or any other org.op4j.functions.Function<T,R> object) without having to create an operation or function expression.

Scenario
Our value String contains some accented characters, escaped for HTML:
// value == "Saint-&Eacute;tienne est une ville de France"
...and we want to unescape it so that we obtain a String like:
// value == "Saint-Étienne est une ville de France"

Recipe
Of course we could create an operation expression for executing the FnString.unescapeHTML function:

value = Op.on(value).exec(FnString.unescapeHTML()).get();

But, if we prefer, we can directly execute the function on the target String:

value = FnString.unescapeHTML().execute(value);

This is because the function object returned by FnString.unescapeHTML() is Function<String,String> so, in fact, if we look at it step by step:

Function<String,String> fn = FnString.unescapeHTML();
value = fn.execute(value);


Comments
All Function objects in op4j allow direct execution by means of their execute(...) method.

18 April 2010

Grouping a list of objects by the value of one of their attributes

Valid since: op4j 1.0

Description
Group a list of objects by the value one of their attributes, effectively creating a Map<?,List<?>> containing the distinct values for that attribute as keys, and the objects returning the same value for that attribute in the corresponding value lists.

Scenario
Our City class looks like this:
public class City {
    private String country;
    private String name;
    ...
    public String getCountry() {
        return this.country;
    }
    public String getName() {
        return this.name;
    }
}
And we have a List<City> cities variable containing some City objects:
// cities == LIST [
//                  CITY [ country="Spain";name="Santiago" ],
//                  CITY [ country="France";name="Marseille" ],
//                  CITY [ country="Portugal";name="Porto" ],
//                  CITY [ country="Spain";name="Barcelona" ],
//                  CITY [ country="Portugal";name="Lisboa" ],
//                  CITY [ country="Portugal";name="Viseu" ]
//                ]
Which we want to group by country, so that we get a Map<String,List<City>> just like:
// citiesByCountry == 
//           MAP [
//                 {
//                   "Spain" : 
//                   LIST [
//                     CITY [ country="Spain";name="Santiago" ],
//                     CITY [ country="Spain";name="Barcelona" ]
//                   ]
//                 }
//                 {
//                   "France" : 
//                   LIST [
//                     CITY [ country="France";name="Marseille" ]
//                   ]
//                 }
//                 {
//                   "Portugal" : 
//                   LIST [
//                     CITY [ country="Portugal";name="Porto" ],
//                     CITY [ country="Portugal";name="Lisboa" ],
//                     CITY [ country="Portugal";name="Viseu" ]
//                   ]
//                 }
//               ]

Recipe
What we need to do is create a map from our list by zipping the map keys (our existing cities will be the values) and then grouping values with the same keys into lists.

So the action we will execute is zipAndGroupKeysBy(function), and the function we will use to obtain the map keys will be an attribute getter, provided by the Get function hub:

Let's see it in action:

Map<String,List<City>> citiesByCountry =
    Op.on(cities).zipAndGroupKeysBy(Get.attrOfString("country")).get();


Comments
Let's have a look at the equivalent non-op4j Java code:
Map<String,List<City>> citiesByCountry =
    new LinkedHashMap<String,List<City>>();
for (City city : cities) {
    List citiesForCountry = citiesByCountry.get(city.getCountry());
    if (citiesForCountry == null) {
        citiesForCountry = new ArrayList<City>();
        citiesByCountry.put(city.getCountry(), citiesForCountry);
    }
    citiesForCountry.add(city);
}

Executing an OGNL expression on an array of Strings

Valid since: op4j 1.0

Description
Execute an OGNL (Object-Graph Navigation Language) expression on each of the elements of an array, obtaining another array with the results of these executions.

Scenario
Our String[] riverNames variable contains the names of several rivers in a remote part of the world:
// riverNames == ARRAY [ "Eume", "Ulla", "Tambre" ]
But we want to display these rivers in our interface with the "River" prefix, so we need to create another array containing Strings like:
// rivers == ARRAY [ "River Eume", "River Ulla", "River Tambre" ]

Recipe
We can execute an OGNL expression on each array element in order to create the Strings we really want. This can be easily done by mapping a function from the op4j-ognl extension:

String[] rivers = 
    Op.on(riverNames).map(FnOgnl.evalForString("'River ' + #target")).get();

When executing an OGNL expression, FnOgnl sets the target object as the root of the expression, but it also makes it available at the predefined #target variable (as you can see above). If the expression needed any parameters, the #param array variable would have contained them. Finally, an #index variable contains the iteration index in case you need it.

Comments
But let's go further... what if we wanted to convert this expression we have just created into a function called riverize so that we can apply it to any array of river names we might get?

Easy. Let's first create the river function by using a function expression (as opposite to the usual operation expressions):
Function<String[],String[]> riverize = 
    Fn.onArrayOf(Types.STRING).
        map(FnOgnl.evalForString("'River ' + #target")).get();
Our riverize function is of type Function<String[],String[]>, which means that it receives a String[] input (first type parameter) and returns another String[] object as a result (second type parameter). Let's execute it:
// moreRiverNames == ARRAY [ "Eo", "Sil" ]
String[] moreRivers = riverize.execute(moreRiverNames);
// moreRivers == ARRAY [ "River Eo", "River Sil" ]
Perfect!

op4j 1.0 RELEASED

Big news! The op4j project has today published its first stable version: op4j 1.0.

The new distribution is already downloadable from both Sourceforge and the Maven central repositories.

Get all the info at the project's home page at: http://www.op4j.org

The team wish to thank all the people who have been testing the 3 beta versions released during the last weeks. We hope this first stable version meets the expectations of everyone!

17 April 2010

Creating a list with the results of calling a method on each element of another list

Valid since: op4j 1.0

Description
Extract one attribute from each of the objects in a list and create another list with the results.

Scenario
Our User class is a value object class like:
public class User {
    private String name;
    ...
    public String getName() {
        return this.name;
    }
}
Given the List<User> users variable, containing:
// users == LIST [ 
//                 USER [ ...; name="James Cheddar"; ...]
//                 USER [ ...; name="Richard Stilton"; ...]
//                 USER [ ...; name="Bernard Brie"; ...]
//                 USER [ ...; name="Antonio Cabrales"; ...]
//               ]
...we want to obtain a List<String> with all the names of the users, like:
// users == LIST [ "James Cheddar", "Richard Stilton", 
//                 "Bernard Brie", "Antonio Cabrales" ]

Recipe
Map (iterate + execute) a Get function, which will call the getName() method:

List<String> names =
    Op.on(users).map(Get.attrOfString("name")).get();

Get.attrOfString(...) allows you to call the getter method for the name attribute (which is a String), and is equivalent in fact to calling the getName() method using a Call function:

List<String> names =
    Op.on(users).map(Call.methodForString("getName")).get();

Comments
Of course, mapping is equivalent to iterating and executing, so this will be equivalent to:
List<String> names =
    Op.on(users).forEach().exec(Get.attrOfString("name")).get();
Now, let's see the equivalent non-op4j Java code:
List<String> names = new ArrayList<String>();
for (User user : users) {
    names.add(user.getName());
}

But... what if users had been an array instead of a list? Well, because arrays need to be instantiated using their specific class in Java (and not a generic one like java.util.List<?>), our map action will need to specify the type of the result coming from the Get function, like:
...map(Types.STRING, Get.attrOfString("name"))...
}
And also, because of User not being a standard basic Java class like String or Integer, but instead a user-defined one, if we create our operation expression with:
Op.on(userArray)...
// User is not a basic Java class, so our operator will be limited!
...we will get a limited operator, which will let us do some things (like converting to a list or set, for instance), but not iterating or executing functions on its elements. For that, instead, we will need to specify the Type of the array's elements as a javaRuntype type (similar to that Types.STRING, but for our own User class):
Type<User> userType = Types.forClass(User.class);
Op.onArrayOf(userType, usersArray)...
Finally, putting it all together:
Type<User> userType = Types.forClass(User.class);
String[] names =
    Op.onArrayOf(userType, usersArray).
        map(Types.STRING, Get.attrOfString("name")).get();
Which compares to the equivalent non-op4j Java code:
List<String> namesList = new ArrayList<String>();
for (User user : usersArray) {
    namesList.add(user.getName());
}
String[] names = namesList.toArray(new String[namesList.size()]);

10 April 2010

Creating a map from its elements

Valid since: op4j 1.0

Description
Create a map given its keys and values as separate objects.

Scenario
We want to create a Map<String,String> peopleDept variable containing some people names as keys, and the departments they work for in our company as values:
// peopleDept == MAP [
//                   [ key="James Cheddar"; value="Finance" ]
//                   [ key="Richard Stilton"; value="Engineering" ]
//                   [ key="Bernard Brie"; value="Marketing" ]
//                   [ key="Antonio Cabrales"; value="Sales" ]
//                 ]

Recipe
We can just create a map operator by building the map on an entry-by-entry basis, and return it with get() before executing any other operation:

Map<String,String> peopleDept =
    Op.onMapFor("James Cheddar", "Finance").
            and("Richard Stilton", "Engineering").
            and("Bernard Brie", "Marketing").
            and("Antonio Cabrales", "Sales").get();

This looks good. But instead, as already mentioned in other recipes, because of the fact that this map holds keys and values of the same type (String), we could have created a list with all they keys and values in key, value, key, value, key, value... order and executed the couple() action to create the map:

Map<String,String> peopleDept =
    Op.onListFor(
        "James Cheddar",  "Finance",   "Richard Stilton",  "Engineering",
        "Bernard Brie",   "Marketing", "Antonio Cabrales", "Sales").
        couple().get();

Comments
The difference between these two methods is that the entry-by-entry map building method can be used always (whichever the key and value types), while the coupling method requires the resulting map to hold the same key and value types.

As an example, we could create a Map<String,Integer> peopleYearsInCo containing the relation between these persons and the number of years they have been working for the company:

Map<String,Integer> peopleYearsInCo =
    Op.onMapFor("James Cheddar", 12).
            and("Richard Stilton", 2).
            and("Bernard Brie", 7).
            and("Antonio Cabrales", 9).get();

09 April 2010

Extract some text from a String using a regular expression

Valid since: op4j 1.0

Description
Given a String, apply a regular expression (containing some group definitions) to it and, if it matches, extract one of the matched groups into a different String.

Scenario
Our books variable is an array containing some data about books, extracted from a text file. Our data look like this:
// books == ARRAY [ "Title=The Origin of Species; Price=24.90EUR",
//                  "Title=Odyssey; Price=13.50EUR",
//                  "Title=A Midsummer Night's Dream; Price=18.20EUR" ]
But we are only interested on the titles of those books, and we would like to create a String[] titles variable containing:
// books == ARRAY [ "The Origin of Species",
//                  "Odyssey",
//                  "A Midsummer Night's Dream" ]
For doing so, we define the following regular expression:
// regex == "Title=(.*?); Price(.*)"

Recipe
We should iterate on our books array and apply on each element the FnString.matchAndReplace(...) function, which will apply our regular expression and let us decide which group we want to extract from it (in this case, group number 1):

String[] titles = 
    Op.on(books).forEach().exec(FnString.matchAndExtract(regex, 1)).get();

...which is in fact equivalent to:

String[] titles = 
    Op.on(books).map(FnString.matchAndExtract(regex, 1)).get();


Comments
Let's look instead at a much more complex (and powerful) example: Imagine that we did't need only the book titles, but instead we wanted to create a map for each book containing an entry for each piece of data ("Title" and "Price"). Something like this:
// bookInfo == LIST [ 
//                    MAP [
//                          "Title"="The Origin of Species"
//                          "Price"="24.90EUR"
//                        ],
//                    MAP [
//                          "Title"="Odyssey"
//                          "Price"="13.50EUR"
//                        ],
//                    MAP [
//                          "Title"="A Midsummer Night's Dream"
//                          "Price"="18.20EUR"
//                        ]
//                  ]
What would we need to get this starting from our books variable? First, define a new regular expression able to extract both keys and values:
// regex == "(.*?)=(.*?); (.*?)=(.*?)"
And now let's see the steps:
  1. Convert the array into a List.
  2. Iterate it. For each element:
    1. Apply regular expression and extract groups 1, 2, 3 and 4 into a List<String>.
    2. Couple the four elements in the resulting list into two map entries each so that element 1 is key for the first entry, 2 is value for the first entry, 3 is key for the second entry and 4 is value for the second entry.
The functions involved will be:
// FnString.matchAndExtractAll(String regex, int... groups) : Function<String, List<String>>
// FnList.ofString().couple() : Function<List<String>, Map<String, String>>
Let's have a look at the resulting code:
List<Map<String,String>> bookInfo = 
    Op.on(books).toList().forEach().
        exec(FnString.matchAndExtractAll(regex, 1,2,3,4)).
        exec(FnList.ofString().couple()).get();
Much easier to write than to think of!

Creating a Calendar from day, month and year

Valid since: op4j 1.0

Description
Create a java.util.Calendar object containing a specific date from its components (day, month and year) in a simple way.

Scenario
We want to create a variable called date, of type java.util.Calendar, containing the following date:
// date == CALENDAR: 12 October, 1492. 00:00:00:000

Recipe
The FnCalendar function hub class from op4j provides us with a useful fieldIntegerListToCalendar() function, which we could use for solving our scenario in a very elegant way, just creating a list with the date components (year, month, day), and then applying the function:

Calendar date = 
    Op.onListFor(1492, 10, 12).exec(FnCalendar.fieldIntegerListToCalendar()).get();

Comments
Let's see the equivalent non-op4j Java code for this:
Calendar date = Calendar.getInstance();
date.clear();
date.set(Calendar.DAY_OF_MONTH, 12);
date.set(Calendar.MONTH, Calendar.OCTOBER);
date.set(Calendar.YEAR, 1492);

And... what if we wanted to be more specific and set the hour and minutes like:
// date == CALENDAR: 12 October, 1492. 02:34:00:000
Easy:
Calendar date = 
    Op.onListFor(1492, 10, 12, 2, 34).exec(FnCalendar.fieldIntegerListToCalendar()).get();

08 April 2010

Removing all accents (and other diacritics) from a String

Valid since: op4j 1.0

Description
Remove all diacritics common in European languages from the characters in a String, converting the text into an ASCII-compatible String. A common operation, for example, in text search comparison scenarios.

Scenario
Our conts variable is an array containing the names of the Earth's continents in Castilian Spanish language:
//conts == ARRAY [ "África", "América", "Antártida", "Asia", "Europa", "Oceanía" ]
...and, knowing that our users might forget to input accents in our application, we need to strip all the accents from those texts so that searches are not influenced by their bad ortography:
//conts == ARRAY [ "Africa", "America", "Antartida", "Asia", "Europa", "Oceania" ]

Recipe
Use the op4j asciify() function in the FnString function hub class, which is able to transform accented characters(and also other diacritics) into their non-accented equivalents.

Also, this function will have to be applied to each element of the array, and so a map(..) action will be needed for executing the function.

conts = Op.on(conts).map(FnString.asciify()).get();

This will be of course less verbose -but equivalent- than:

conts = Op.on(conts).forEach().exec(FnString.asciify()).get();

Comments
But, what if we also wanted an uppercase output? Well, just throw in the FnString.toUpperCase() function:
conts = 
    Op.on(conts).forEach().exec(FnString.asciify()).exec(FnString.toUpperCase()).get();
Or equivalently, we could chain both functions into one:
conts = 
    Op.on(conts).map(FnFunc.chain(FnString.asciify(), FnString.toUpperCase())).get();

[NEWS] op4j 1.0-beta3 RELEASED!

Version 1.0-beta3 of op4j has just been released!

This version adds two new extensions: op4j-ognl and op4j-jodatime, new optimizations and hundreds of new functions and useful actions for your operators.

See the CHANGELOG
Or go to the Project site.