29 March 2010

Adding an element to an array at a specific position

Valid since: op4j 1.0

Description
Insert a new element into an array at a specific position.

Scenario
Our minerals variable, of type String[], contains:
// minerals == ARRAY [ "Talc", "Quartz", "Diamond" ]
...and we want to add "Fluorite" at the second position of the array, as it is harder than talc but softer than quartz:
// minerals == ARRAY [ "Talc", "Fluorite", "Quartz", "Diamond" ]

Recipe
Use the insert(...) action on the array, specifying the position at which the new element will be added to the array. Positions start with zero.

minerals = Op.on(minerals).insert(1 ,"Fluorite").get();

Comments
This recipe is also valid for lists, maps and sets.

If we wanted to add more than one element, we could have done:
minerals = 
    Op.on(minerals).insertAll(1, "Fluorite", "Apatite").get();

Let's see the equivalent non-op4j Java code:
minerals = Arrays.copyOf(minerals, minerals.length + 1);
for (int i = (minerals.length - 1), z = 1; i > z; i--) {
    minerals[i] = minerals[i - 1];
}
minerals[1] = "Fluorite";

Adding an element to an array

Valid since: op4j 1.0

Description
Add a new element at the end of an array. This will mean resizing the array.

Scenario
Our ingredients variable, of type String[], contains:
// ingredients == ARRAY [ "Lettuce", "Tomato", "Onion" ]
...and we would like to add a new ingredient, Olive Oil, so that we get:
// ingredients == ARRAY [ "Lettuce", "Tomato", "Onion", "Olive Oil" ]

Recipe
Just use the add(...) action on the array:

ingredients = Op.on(ingredients).add("Olive Oil").get();

Comments
This recipe is also valid for lists, maps and sets.

If we wanted to add more than one element, we could have done:
ingredients = 
    Op.on(ingredients).addAll("Olive Oil", "Balsamic Vinegar").get();

And finally, let's look at the non-op4j Java equivalent code:
ingredients = Arrays.copyOf(ingredients, ingredients.length + 1);
ingredients[ingredients.length - 1] = "Olive Oil";

28 March 2010

Sorting an array

Valid since: op4j 1.0

Description
Sort an array using the natural order of their elements.

Scenario
Our oceans variable contains an unordered array of Strings with the names of the Earth's oceans:
// oceans == ARRAY [ "Pacific", "Atlantic", "Indian", "Arctic", "Southern" ]
...and we want to order it by Strings' natural order, which is alphabetic:
// oceans == ARRAY [ "Arctic", "Atlantic", "Indian", "Pacific", "Southern" ]

Recipe
Just apply the sort() action to the array:

oceans = Op.on(oceans).sort().get();

Comments
This sort() action can also be applied to lists or sets, and there are variants to sort using a comparator (sort(Comparator)) or even sort by the result of executing a function on the elements (sortBy(IFunction)). Maps can also be ordered by their keys.

Let's have a look at the equivalent non-op4j Java code:
List<String> oceansList = Arrays.asList(oceans);
Collections.sort(oceansList);
oceans = oceansList.toArray(new String[oceansList.size()]);

Removing duplicates from an array (or list)

Valid since: op4j 1.0

Description
Remove duplicate elements from an array (or list).

Scenario
We have a String[] capitals variable, such as:
// capitals == ARRAY [ "Lisboa", "Madrid", "Paris", "Lisboa", "Bruxelles" ]
...and we want to remove duplicate elements in order to get:
// capitals == ARRAY [ "Lisboa", "Madrid", "Paris", "Bruxelles" ]

Recipe
Operate on the array and use the distinct() action:

capitals = Op.on(capitals).distinct().get();

Comments
If we had a list instead of an array, our code would have been just the same:
// capitalList == LIST [ "Lisboa", "Madrid", "Paris", "Lisboa", "Bruxelles" ]
capitalList = Op.on(capitalList).distinct().get();

Finally, let's compare with the equivalent non-op4j Java code:
Set<String> capitalSet = new LinkedHashSet<String>();
for (String capital : capitals) {
    capitalSet.add(capital);
}
capitals = capitalSet.toArray(new String[capitalSet.size()]);

27 March 2010

Converting a list into an array

Valid since: op4j 1.0

Description
Convert a list of objects (List<T>) into an array of the same type (T[]).

Scenario
We have a List<String> variable called elementList, containing:
// elementList == LIST [ "earth", "air", "fire", "water" ]
...and we want to convert this variable into a String[] object.

Recipe
Execute the toArrayOf(Type) action on the list:

String[] elementArray =
    Op.on(elementList).toArrayOf(Types.STRING).get();


Comments
In the example above, it was necessary to specify the type of the array elements (String) with a javaRuntype Type because Java does not create arrays by instantiating a generic class like java.util.List or java.util.Set, but rather by creating an instance of a specific String[] class. This is why op4j needs the class to be used for creating the new array in the form of an org.javaruntype.type.Type object.

This would not be needed, for example, if we wanted to create a set:
Set<String> elementSet =
    Op.on(elementsList).toSet().get();

Let's look at the equivalent non-op4j Java code for the List-to-Array conversion:
String[] elementArray = 
    elementList.toArray(new String[elementList.size()]);

Filtering numbers greater than a value out of a list

Valid since: op4j 1.0

Description
Given a list of numbers, remove all elements greater than a specified value.

Scenario
We have List<Integer> variable called values containing some integer numbers:
// values == LIST [ 6641, 53, 430, 1245 ]
...and we want to remove all the elements greater than 500, so that values looks like this:
// values == LIST [ 53, 430 ]

Recipe
Execute the removeAllTrue(IFunction<T,Boolean>) action, which will remove from the list all the elements for which the specified function returns true. The specified function will be FnNumber.greaterThan(...):

values =
    Op.on(values).removeAllTrue(FnNumber.greaterThan(500)).get();


Comments
op4j includes many boolean comparison function like "eq", "notEq", "greaterThan", "greaterOrEqTo", "lessThan", "lessOrEqTo", etc. They can be applied to a variety of objects like Integers, Longs, BigDecimals, Strings, etc.

Converting an array of Strings into a null-free array of Integers

Valid since: op4j 1.0

Description
Convert an array of String objects representing valid integer numbers into an array of Integer objects, having filtered all null values out of the array.

Scenario
We have a String[] like:
// strArray == ARRAY [ "431", null, "94", "398" ]
And we want to convert it into an Integer[] containing no null elements.

Recipe
Two actions will be needed:
  • First, execute the conversion from String to Integer.
  • Second, remove all nulls from the resulting array.
Integer[] values =
    Op.on(strArray).map(Types.INTEGER, FnString.toInteger()).removeAllNull().get();


Comments
Note that because arrays are not collections, and instantiating them requires creating an instance of a specific class (instead of a generic class like java.util.List or java.util.Set), executing a function that changes the type of the elements of an array requires specifying the new type, which is done with that Types.INTEGER parameter seen above. This is an org.javaruntype.type.Type object, from the javaRuntype library.

Mapping (executed here via a map(...) action) is equivalent to iterating, executing a function, and then ending the iteration. So, the code above is equivalent to:
Integer[] values =
    Op.on(strArray).
        forEach().exec(Types.INTEGER, FnString.toInteger()).endFor().
        removeAllNull().get();

Let's see the equivalent non-op4j Java code:
List valuesList = new ArrayList();
for (String element : strArray) {
    if (element != null) {
        valuesList.add(Integer.parseInt(element));
    }
}
Integer[] values = valuesList.toArray(new Integer[valuesList.size()]);

Parsing a String number into a BigDecimal (no matter the decimal separator)

Valid since: op4j 1.0

Description
Parsing a String containing a decimal number, without having to specify the symbol (comma or point) that has been used as a decimal separator.

Scenario
A form on a web page allows users to input a decimal number. We have two of these numbers in String variables strValue1 and strValue2, and we want to parse these strings into java.math.BigDecimal objects.

But these two numbers have been input by users coming from different countries, and they have used their own different ways of specifying the decimal separator:
// strValue1 == "871,21"
// strValue2 == "421.441"

Recipe
The FnString.toDouble(...) function provides the means of specifying the decimal point being used by using the DecimalPoint enum, one of which values allows both point and comma:

Double value1 =
    Op.on(strValue1).exec(FnString.toDouble(DecimalPoint.CAN_BE_POINT_OR_COMMA)).get();
Double value2 =
    Op.on(strValue2).exec(FnString.toDouble(DecimalPoint.CAN_BE_POINT_OR_COMMA)).get();



Comments
This method can also be used for obtaining Double or Float objects.

Also, note that this method will not work correctly if thousands separators are used, like in "1,423,243.87", because if decimal separators are not used but thousands separators are (like "1,423,243"), the system will not be able of determining which was the decimal separator being used.

26 March 2010

Parsing a String decimal number into a Double (using locale)

Valid since: op4j 1.0

Description
Obtaining a Double object from a String containing a decimal number written in a locale-specific manner.

Scenario
We have a String variable called value which contains a decimal number written by a Frenchman:
// value == "34,59"
French use the comma sign (",") as a decimal separator, contrary to most English-speaking people and also contrary to the standard way to represent numbers as Strings in Java (which is American English). We will need a locale-sensitive conversion then.


Recipe
Use a version of the FnString.toDouble(...) function that allows a locale to be specified:

Double value =
    Op.on(strValue).exec(FnString.toDouble("fr")).get();


Comments
op4j's number conversion functions come in many flavours, including specifying locales, scales and rounding modes for Integer, Longs, BigDecimals, Doubles, etc.

Equivalent non-op4j Java code:
NumberFormat numberFormat = 
    NumberFormat.getInstance(new Locale("fr"));
Double value = (Double) numberFormat.parse(strValue);

Filtering nulls from an array

Valid since: op4j 1.0

Description
Given an array (or list, or set), remove all its null elements.

Scenario
We have an Integer[] variable called values, like:
// values == ARRAY [ null, 12, 43, 92, null, 34 ]
...and we want to easily remove all null elements from it.

Recipe
Just execute the removeAllNulls() action on the array and return the result:

Integer[] filteredValues =
    Op.on(values).removeAllNull().get();


Comments
There are many remove methods, like for example removeAllTrue(function), which determines whether an element has to be removed or not depending on the result of executing a function passed as a parameter.

Equivalent non-op4j Java code:
List<Integer> filteredValuesList = new ArrayList<Integer>();
for (Integer value : values) {
    if (value != null) {
        filteredValuesList.add(value);
    }
}
Integer[] filteredValues = 
    filteredValuesList.toArray(new Integer[filteredValuesList.size()]);

25 March 2010

Converting a list of Strings into a list of Calendars (using a pattern and a locale)

Valid since: op4j 1.0

Description
Create a List<Calendar> object from a List<String>, by applying a standard Java locale-dependent pattern to each String.

Scenario
We need to build a List of Calendar objects from the values in our dates variable, which contains a couple of Strings representing dates:
// dates == LIST ["12 de octubre, 1492", "06 de diciembre, 1978"]
A valid Java pattern for parsing these dates is "dd 'de' MMMM, yyyy", and we must tell the parser that month names (in long format) are in Spanish.

Recipe
Iterate the list of Strings and execute the FnString.toCalendar(...) function on each element to obtain a Calendar.

As our pattern ("dd 'de' MMMM, yyyy") makes use of textual month names (like jan, may, december...), this function requires a locale to be specified so that the names of the months are correctly understood. In this case, we will specify the Spanish locale (es):

List<Calendar> cals =
    Op.on(dates).map(FnString.toCalendar("dd 'de' MMMM, yyyy", "es")).get();

Comments
This recipe involved mapping (map(...) action), which is equivalent to iterating, then executing, and then ending iteration on an array, list or set. This makes the above example equivalent to:
List<Calendar> cals =
    Op.on(dates).forEach().exec(FnString.toCalendar("dd 'de' MMMM, yyyy", "es")).get();

Let's compare with non-op4j normal Java code:
SimpleDateFormat dateFormat = new SimpleDateFormat(("dd 'de' MMMM, yyyy", "es"));
List<Calendar> cals = new ArrayList<Calendar>();
for (String date : dates) {
    Date dDate = null;
    try {
        dDate = dateFormat.parse(date);
    } catch (ParseException e) {
        throw SomeException(e);
    }
    Calendar cal = Calendar.getInstance();
    cal.setTime(dDate);
    cals.add(cal);
}

Converting a String into a Calendar (using a pattern)

Valid since: op4j 1.0

Description
Create a java.util.Calendar object from a String, by applying a standard Java pattern to this String.

Scenario
Our date variable contains a String in the "dd/MM/yyyy" format, like:
String date = "06/12/1978";
...and we want to obtain a java.util.Calendar for this date.

Recipe
Operate on the String, and apply one of the FnString.toCalendar() variants (specifically, the one receiving a pattern).

Calendar cal =
    Op.on(date).exec(FnString.toCalendar("dd/MM/yyyy")).get();


Comments
This function creates truncated calendar objects, which means that all fields less significant than the least-significant of the specified fields are set to zero. In the example, the least-significant field in the pattern is day, and so hour, minute, second and millisecond are set to zero.

Let's compare with non-op4j normal Java code:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date dDate = null;
try {
    dDate = dateFormat.parse(date);
} catch (ParseException e) {
    throw SomeException(e);
}
Calendar cal = Calendar.getInstance();
cal.setTime(dDate);

Creating maps by coupling elements

Valid since: op4j 1.0

Description
Create a java.util.Map object by specifying all its keys and values, alternating them into an array, list or set, and then performing the couple action which will consider all even elements as keys and odd elements as values (starting with 0).

Scenario
We want to create a map relating some countries to their capital cities. Like: {{"Spain"="Madrid"}, {"United Kingdom"="London"}, {"France"="Paris"}}.

Recipe
First specify all the keys and values (alternating them in key-value order) and then execute the couple action:

Map<String,String> capitals =
    Op.onListFor("Spain", "Madrid", "United Kingdom", "London", "France", "Paris").couple().get();


Comments
Coupling needs the resulting map to specify the same type for keys and values (e.g. Map<String,String>, Map<Integer,Integer>, etc.).

Comparing it with non-op4j code...
Map<String,String> capitals = new LinkedHashMap<String,String>();
capitals .put("Spain", "Madrid");
capitals .put("United Kingdom", "London");
capitals .put("France", "Paris");

Creating maps by zipping keys and values

Valid since: op4j 1.0

Description
Create a java.util.Map object by zipping keys and values. Zipping involves having an array, list or set of objects which we want to consider either keys or values for the new map, and then specify another sequence of objects (same number of them as the existing array, list or set) that will be paired one-to-one with the elements of the existing structure so that, either the original element is considered key and the new object is considered value (zip values), or the contrary (zip keys).

Scenario
We want to create a Map<String,Integer> of person names (keys) and ages (values). Specifically we want: {{"John"=27}, {"Mary"=49}, {"Derek"=19}}.

Recipe
We will zip the values (ages), so we will start by creating a list on the keys and then we will zip the corresponding ages:

Map<String,Integer> agesByName =
    Op.onListFor("John", "Mary", "Derek").zipValues(27, 49, 19).get();


Comments
We could have zipped the keys instead (althought it would have been less natural in this specific case):

Map<String,Integer> agesByName =
    Op.onListFor(27, 49, 19).zipKeys("John", "Mary", "Derek").get();

Remember that all maps created by op4j are ordered (java.util.LinkedHashMap), and thus the new map will keep the order of the original list when iterated.

Let's have a look at the equivalent Java code:
Map<String,Integer> agesByName= new LinkedHashMap<String,Integer>();
agesByName.put("John", 27);
agesByName.put("Mary", 49);
agesByName.put("Derek", 19);

24 March 2010

[NEWS] op4j 1.0-beta2 RELEASED!

Version 1.0-beta2 of op4j has just been released!

Lots of new features have been added in this new version, focusing
effort on creating many new useful functions and simplifying
behaviours.

See the CHANGELOG
Or go to the Project site.

23 March 2010

Creating a set from its elements

Valid since: op4j 1.0

Description
Easily create a set containing some objects.

Scenario
Having some objects of type User called user1, user2 and user3, we want to create a Set<User> object containing them.

Recipe
Create a set operator on the objects and don't execute any action on it, just return the result:

Set<User> users = 
    Op.onSetFor(user1, user2, user3).get();

Comments
Sets created by op4j are always ordered sets (java.util.LinkedHashSet).

Compare this to the the non-op4j Java equivalent:

Set<User> users= new LinkedHashSet<User>();
users.add(user1);
users.add(user2);
users.add(user3);

Creating a list from its elements

Valid since: op4j 1.0

Description
Easily create a list containing some objects.

Scenario
Having the strings "hello", "hola", "ola" and "olá", we want to create a List<String> object containing them.

Recipe
Create a list operator on the strings and don't execute any action on it, just return the result:

List<String> stringList = 
    Op.onListFor("hello", "hola", "ola", "olá").get();

Comments
Compare this to the the non-op4j Java equivalent:

List<String> stringList = new ArrayList<String>();
stringList.add("hello");
stringList.add("hola");
stringList.add("ola");
stringList.add("olá");

[NEWS] Welcome to Bending the Java spoon!

This blog is intended for publishing news about the op4j library as well as recipes for its use.

Recipes will come in the form of small practical scenarios solved with code examples, in order to show the power and versatility of op4j.

This is an official blog, published and maintained by the op4j project members and contributors.