Question

Is there a lambdaj way of doing this neatly? I know the title sounds complicated but the code fragment below should make it clear:

private List<String[]> getContractLineItemsForDatatables(List<ContractLineItem> contractLineItems) {

    List<String[]> contractLineItemsForDatatables = Lists.newArrayList();

    for (ContractLineItem contractLineItem : contractLineItems) {

        contractLineItemsForDatatables.add( contractLineItem.getDataTablesRow());
    }

    return contractLineItemsForDatatables;
}

There must be a neat way of doing this with Lambdaj to avoid the for loop above but I can't get my head around it. btw contractLineItem.getDatatablesRow() returns a String[].

So what I want to do is:

run getDataTablesRow() on all elements in contractLineItems list, and add them to contactLineItemsForDatatables list.

Any suggestions?

Was it helpful?

Solution 2

How about

List<String[]> items = extract(contractLineItems, on(ContractLineItem.class).getDataTablesRow());

LambaJ comes with a method named extract that is a mapper. You can also read the reference about converting objects with lambdaj.

OTHER TIPS

Most of the wordiness comes from the choice of variable names rather than the Java code.

private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    List<String[]> ret = new ArrayList<>();
    for (ContractLineItem item : items) ret.add(item.getDataTablesRow());
    return ret;
}

If an exception is thrown it would look like

Exception in thread "main" java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:87)
at Main.extractDataTableRows(Main.java:50)
at Main.main(Main.java:27)

Using Guava's lambda

private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    return Lists.transform(items, new Function<ContractLineItem, String[]>() {
        @Override
        public String[] apply(ContractLineItem item) {
            return item.getDataTablesRow();
        }
    });
}

if an exception is thrown it would look like

Exception in thread "main" java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:76)
at Main$1.apply(Main.java:38)
at Main$1.apply(Main.java:35)
at com.google.common.collect.Lists$TransformingRandomAccessList.get(Lists.java:495)
at java.util.AbstractList$Itr.next(AbstractList.java:358)
at java.util.AbstractCollection.toString(AbstractCollection.java:459)
at java.lang.String.valueOf(String.java:2957)
at java.io.PrintStream.println(PrintStream.java:821)
at Main.main(Main.java:31)

Note: the exception is not triggered until the List is used.

This Guava Caveat is clear enough to me.

As of Java 7, functional programming in Java can only be approximated through awkward and verbose use of anonymous classes. This is expected to change in Java 8, but Guava is currently aimed at users of Java 5 and above.

Excessive use of Guava's functional programming idioms can lead to verbose, confusing, unreadable, and inefficient code. These are by far the most easily (and most commonly) abused parts of Guava, and when you go to preposterous lengths to make your code "a one-liner," the Guava team weeps.

Using Java 8.

private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    return items.stream()
            .<String[]>map(ContractLineItem::getDataTablesRow)
            .into(new ArrayList<>());
}

Adding a utility method you can write

public static <E, R> List<R> map(Collection<E> elements, Function<? super E, ? extends R> function) {
    return elements.stream().<R>map(function).into(new ArrayList<R>());
}

// hiding the guff, this is more readable IMHO.
private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    return map(items, ContractLineItem::getDataTablesRow);
}

if an exception is thrown it might look like this

 Exception in thread "main" java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:77)
at Main$$Lambda$1.apply(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:87)
at java.util.Arrays$ArraySpliterator.forEach(Arrays.java:4551)
at java.util.stream.AbstractPipeline$PipelineHelperImpl.into(AbstractPipeline.java:197)
at java.util.stream.op.ForEachOp.evaluateSequential(ForEachOp.java:86)
at java.util.stream.op.ForEachOp.evaluateSequential(ForEachOp.java:37)
at java.util.stream.AbstractPipeline.pipeline(AbstractPipeline.java:336)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:142)
at java.util.Collection.addAll(Collection.java:505)
at java.util.ArrayList.addAll(ArrayList.java)
at java.util.stream.ReferencePipeline.into(ReferencePipeline.java:189)
at Main.map(Main.java:34)
at Main.extractDataTableRows(Main.java:39)
at Main.main(Main.java:29)

While @Edwin's answer is shortest, it is the hardest to debug and maintain as there is allot of "magic" going on to implement this. This is fine for unit tests, but you wouldn't want it in production code IMHO.

 Exception in thread "main" ch.lambdaj.function.argument.InvocationException: Failed invocation of public java.lang.String[] Main$ContractLineItem.getDataTablesRow() on object Main$ContractLineItem@1d724f31 caused by: null
at ch.lambdaj.function.argument.Invocation.invokeOn(Invocation.java:70)
at ch.lambdaj.function.argument.InvocationSequence.invokeOn(InvocationSequence.java:91)
at ch.lambdaj.function.argument.InvocationSequence.invokeOn(InvocationSequence.java:85)
at ch.lambdaj.function.argument.Argument.evaluate(Argument.java:35)
at ch.lambdaj.function.convert.ArgumentConverter.convert(ArgumentConverter.java:36)
at ch.lambdaj.function.convert.ConverterIterator.next(ConverterIterator.java:37)
at ch.lambdaj.Lambda.convert(Lambda.java:986)
at ch.lambdaj.Lambda.extract(Lambda.java:1035)
at Main.extractDataTableRows(Main.java:49)
at Main.main(Main.java:27)
 Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:474)
at ch.lambdaj.function.argument.Invocation.invokeOn(Invocation.java:68)
... 14 more
 Caused by: java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:85)
... 19 more

This syntax takes some getting used but I imagine as they tighten up the syntax and drop some of the boiler plate code, it might be more readable.

Here is a good comparison Java 8 Lambda vs LambdaJ vs Guava vs Iterative approach Originally in Russian so please excuse Google Translate ;)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top