Underscore-java

Underscore-java is a port of Underscore.js for Java. It is a utility-belt library that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby).

Underscore-java provides dozens of functions that support both the usual functional suspects: map, select, invoke - as well as more specialized helpers: function binding, sorting, deep equality testing, and so on. It delegates to built-in functions where applicable.

Underscore-java is compatible with Java 11 and later.

The project is hosted on GitHub. Contributions are welcome.

Download

Version 1.100 - ~217Kb, Includes all development files

Collection Functions (Arrays, Structs, Queries, or Objects)

each U.each(collection, iterator)
Iterates over a collection of elements, yielding each in turn to an iterator function. The iterator is bound to the context object (component or struct), if one is passed. Each invocation of iterator is called with argument: (element).

U.each(asList(1, 2, 3), item -> System.out.print(item + ","));
// 1,2,3,
Live example

final int[] index = {0};
U.each(asList(1, 2, 3), item -> {
    System.out.print(index[0] + "=" + (item * multiplier) + ",");
    index[0] += 1;
});
// 0=2,1=4,2=6,
Live example

map U.map(collection, iterator)
Produces a new array of values by mapping each value in collection through a transformation function (iterator).

U.map(asList(1, 2, 3), item -> item * 3);
// [3, 6, 9]
Live example

U.map(new LinkedHashMap<Integer, String>() {{ put(1, "one"); put(2, "two"); put(3, "three"); }}
                .entrySet(),
        item -> item.getKey() * 3);
// [3, 6, 9]
Live example

reduce U.reduce(collection, iterator, memo)
Also known as inject and foldl, reduce boils down a collection of values into a single value. Memo is the initial state of the reduction, and each successive step of it should be returned by iterator.

U.reduce(asList(1, 2, 3),
        (item1, item2) -> item1 + item2,
        0
);
// 6

reduceRight U.reduceRight(collection, iterator, memo)

U.reduceRight(asList(asList(0, 1), asList(2, 3), asList(4, 5)),
        (BiFunction<List<Integer>, List<Integer>, List<Integer>>) (item1, item2) -> {
            List<Integer> list = new ArrayList<>(item1);
            list.addAll(item2);
            return list;
        },
        Collections.emptyList()
);
// [4, 5, 2, 3, 0, 1]

find U.find(collection, iterator)
Looks through each value in the collection, returning the first one that passes a truth test (iterator). The function returns as soon as it finds an acceptable element, and doesn't traverse the entire collection.

U.find(asList(1, 2, 3, 4, 5, 6),
        item -> item % 2 == 0).get();
// 2

filter U.filter(collection, iterator)
Looks through each value in the collection, returning an array of all the values that pass a truth test (iterator).

U.filter(asList(1, 2, 3, 4, 5, 6),
        item -> item % 2 == 0);
// [2, 4, 6]

where U.where(list, properties)
Looks through each value in the list, returning an array of all the values that contain all of the key-value pairs listed in properties.

class Book {
    public final String title;
    public final String author;
    public final Integer year;
    public Book(final String title, final String author, final Integer year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }
    public String toString() {
        return "title: " + title + ", author: " + author + ", year: " + year;
    }
}
List<Book> listOfPlays =
    new ArrayList<Book>() { {
      add(new Book("Cymbeline2", "Shakespeare", 1614));
      add(new Book("Cymbeline", "Shakespeare", 1611));
      add(new Book("The Tempest", "Shakespeare", 1611));
    } };
U.where(listOfPlays, asList(
            Map.<String, Object>entry("author", "Shakespeare"),
            Map.<String, Object>entry("year", Integer.valueOf(1611))));
// [title: Cymbeline, author: Shakespeare, year: 1611,
title: The Tempest, author: Shakespeare, year: 1611]

findWhere U.findWhere(collection, properties)
Looks through the collection and returns the first value that matches all of the key-value pairs listed in properties.

class Book {
    public final String title;
    public final String author;
    public final Integer year;
    public Book(final String title, final String author, final Integer year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }
    public String toString() {
        return "title: " + title + ", author: " + author + ", year: " + year;
    }
}
List<Book> listOfPlays =
    new ArrayList<Book>() { {
      add(new Book("Cymbeline2", "Shakespeare", 1614));
      add(new Book("Cymbeline", "Shakespeare", 1611));
      add(new Book("The Tempest", "Shakespeare", 1611));
    } };
U.findWhere(listOfPlays, asList(
    Map.<String, Object>entry("author", "Shakespeare"),
    Map.<String, Object>entry("year", Integer.valueOf(1611)))).get();
// "title: Cymbeline, author: Shakespeare, year: 1611"

reject U.reject(collection, iterator)
Returns the values in collection without the elements that the truth test (iterator) passes. The opposite of filter.

U.reject(asList(1, 2, 3, 4),
        item -> item % 2 == 0);
// [1, 3]

all U.all(collection, iterator)
Returns true if all of the values in the collection pass the iterator truth test.

U.all(asList(1, 2, 3, 4),
        item -> item % 2 == 0);
// false
U.all(asList(1, 2, 3, 4),
        item -> item < 5);
// true

any U.any(collection, iterator)
Returns true if any of the values in the collection pass the iterator truth test. Short-circuits and stops traversing the collection if a true element is found.

U.any(asList(1, 2, 3, 4),
        item -> item % 2 == 0);
// true
U.any(asList(1, 2, 3, 4),
        item -> item == 5);
// false

include U.include(collection, value)
Returns true if the value is present in the collection.

U.include(asList(1, 2, 3), 3); // true

invoke U.invoke(collection, methodName, [arguments])
Calls the method named by methodName on each value in the collection. The arguments struct passed to invoke will be forwarded on to the method invocation.

U.invoke(asList(" foo", " bar "), "trim"); // ["foo", "bar"]

pluck U.pluck(collection, propertyName)
A convenient version of what is perhaps the most common use-case for map: extracting a collection of property values.

class Person {
    public final String name;
    public final Integer age;
    public Person(final String name, final Integer age) {
        this.name = name;
        this.age = age;
    }
};
U.pluck(asList(new Person("moe", 40), new Person("larry", 50), new Person("curly", 40)), "name");
// ["moe", "larry", "curly"]

max U.max(collection, [iterator])
Returns the maximum value in collection. If iterator is passed, it will be used on each value to generate the criterion by which the value is ranked.

U.max(asList(10, 5, 100, 2, 1000));
// 100
U.max(asList(10, 5, 100, 2, 1000),
        new Function<Integer, Integer>() {
    public Integer apply(Integer item) {
        return -item;
    }
});
// 2

min U.min(collection, [iterator])
Returns the minimum value in collection. If iterator is passed, it will be used on each value to generate the criterion by which the value is ranked.

U.min(asList(10, 5, 100, 2, 1000));
// 2
U.min(asList(10, 5, 100, 2, 1000),
        new Function<Integer, Integer>() {
    public Integer apply(Integer item) {
        return -item;
    }
});
// 1000

sortBy U.sortBy(collection, iterator)
Returns a sorted copy of collection, ranked in ascending order by the results of running each value through iterator. Iterator may also be the string name of the object key to sort by.

U.sortBy(asList(1, 2, 3, 4, 5, 6),
        new Function<Integer, Integer>() {
        public Integer apply(Integer item) {
            return Double.valueOf(Math.sin(item) * 1000).intValue();
        }
    });
=> [5, 4, 6, 3, 1, 2]

groupBy U.groupBy(collection, iterator)
Splits a collection into sets, grouped by the result of running each value through iterator. If iterator is a string instead of a function, groups by the property named by iterator on each of the values.

U.groupBy(asList(1.3, 2.1, 2.4),
        new Function<Double, Double>() {
        public Double apply(Double num) {
            return Math.floor(num);
        }
    });
=> {1.0=[1.3], 2.0=[2.1, 2.4]}

countBy U.countBy(collection, iterator)
Sorts a collection into groups and returns a count for the number of objects in each group. Similar to groupBy, but instead of returning a list of values, returns a count for the number of values in that group.

class Person {
    public final String name;
    public final Integer age;
    public Person(final String name, final Integer age) {
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return name + ", " + age;
    }
}
final Map<String, Integer> result =
U.countBy(asList(new Person("moe", 40), new Person("moe", 50), new Person("curly", 60)),
    new Function<Person, String>() {
    public String apply(Person person) {
        return person.name;
    }
});
=> {moe=2, curly=1}

sortedIndex U.sortedIndex(collection, value, [iterator])
Uses a binary search to determine the index at which the value should be inserted into the collection in order to maintain the collection's sorted order. If an iterator is passed, it will be used to compute the sort ranking of each value.

U.sortedIndex(asList(10, 20, 30, 40, 50), 35);
=> 3

shuffle U.shuffle(array)
Returns a shuffled copy of the array, using a version of the Fisher-Yates shuffle.

U.shuffle(asList(1, 2, 3, 4, 5, 6)
=> [4, 1, 6, 3, 5, 2]

sample U.sample(list, [n])
Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned.

U.sample(asList(1, 2, 3, 4, 5, 6)
=> 4 U.sample(asList(1, 2, 3, 4, 5, 6), 3)
=> [1, 6, 2]

toArray U.toArray(collection)
Converts the collection (object), into an array.

U.<Integer>toArray(asList(1, 2, 3, 4));
=> [1, 2, 3, 4]

toMap U.toMap(collection)
Converts the collection to a map object.

U.toMap((new LinkedHashMap<String, String>() { {
        put("name1", "one");
        put("name2", "two");
    } }).entrySet());
=> {name1=one, name2=two}

size U.size(collection)
Return the number of values in the collection.

U.size(asList(1, 2, 3, 4));
=> 4

Array Functions

first U.first(array, [n])
Returns the first element of an array. Passing n will return the first n elements of the array.

U.first(asList(5, 4, 3, 2, 1));
=> 5

initial U.initial(array, [n])
Returns everything but the last entry of the array. Especially useful on the arguments object. Pass n to exclude the last n elements from the result.

U.initial(asList(5, 4, 3, 2, 1));
=> [5, 4, 3, 2]

last U.last(array, [n])
Returns the last element of an array. Passing n will return the last n elements of the array.

U.last(asList(5, 4, 3, 2, 1));
=> 1

rest U.rest(array, [index])
Returns the rest of the elements in an array. Pass an index to return the values of the array from that index onward.

U.rest(asList(5, 4, 3, 2, 1));
=> [4, 3, 2, 1]

compact U.compact(array)
Returns a copy of the array with all falsy values removed. In Coldfusion, false, 0, and "" are all falsy.

U.compact([0, 1, false, 2, '', 3]);
=> [1, 2, 3]

flatten U.flatten(array, [shallow])
Flattens a nested array (the nesting can be to any depth). If you pass shallow, the array will only be flattened a single level.

U.flatten(asList(1, asList(2, asList(3, asList(asList(4))))));
=> [1, 2, 3, 4];
U.flatten(asList(1, asList(2, asList(3, asList(asList(4))))), true); => [1, 2, [3, [[4]]]];

without U.without(array, [values])
Returns a copy of the array with all instances of the values removed.

U.without(asList(1, 2, 1, 0, 3, 1, 4), 0, 1)
=> [2, 3, 4]

union U.union(*arrays)
Computes the union of the passed-in arrays: the collection of unique items, in order, that are present in one or more of the arrays.

U.union(asList(1, 2, 3), asList(101, 2, 1, 10), asList(2, 1));
=> [1, 2, 3, 101, 10]

intersection U.intersection(*arrays)
Computes the collection of values that are the intersection of all the arrays. Each value in the result is present in each of the arrays.

U.intersection(asList(1, 2, 3), asList(101, 2, 1, 10), asList(2, 1));
=> [1, 2]

difference U.difference(array, others)
Similar to without, but returns the values from array that are not present in the other arrays.

U.difference(asList(1, 2, 3, 4, 5), asList(5, 2, 10));
=> [1, 3, 4]

uniq U.uniq(array, [iterator])
Produces a duplicate-free version of the array. If you want to compute unique items based on a transformation, pass an iterator function.

U.uniq(asList(1, 2, 1, 3, 1, 4));
=> [1, 2, 3, 4]

zip U.zip(*arrays)
Merges together the values of each of the arrays with the values at the corresponding position. Useful when you have separate data sources that are coordinated through matching array indexes.

U.zip(asList("moe", "larry", "curly"), asList("30", "40", "50"), asList("true", "false", "false"));
=> [[moe, 30, true], [larry, 40, false], [curly, 50, false]]

object U.object(array, [values])
Converts an array into a struct. Pass either a single array of [key, value] pairs, or an array of keys, and an array of values.

U.object(asList("moe", "larry", "curly"), asList("30", "40", "50"));
=> [(moe, 30), (larry, 40), (curly, 50)]

indexOf U.indexOf(array, value)
Returns the index at which value can be found in the array, or -1 if value is not present in the array.

U.indexOf(asList(1, 2, 3), 2);
=> 1

lastIndexOf U.lastIndexOf(array, value)
Returns the index of the last occurrence of value in the array, or -1 if value is not present.

U.lastIndexOf(asList(1, 2, 3, 1, 2, 3), 2);
=> 4

range U.range([start], stop, [step])
A function to create flexibly-numbered arrays of integers, handy for each and map loops. start, if omitted, defaults to 0; step defaults to 1. Returns an array of integers from start to stop, incremented (or decremented) by step, exclusive.

U.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
U.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
U.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
U.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
U.range(0);
=> []

concat U.concat(*arrays)
Concatenates any number of arrays together an returns the result.

U.concat(asList(1, 2), asList(3, 4))
=> [1, 2, 3, 4];

reverse U.reverse(array)
Returns a copy of the array in reverse order.

U.reverse(asList(1, 2, 3))
=> [3, 2, 1]

join U.join(array, [separator])
Returns a string with all array elements joined together. Default separator is a single space.

U.join(asList(1, 2), " and ");
=> "1 and 2"

slice U.slice(array, [from], [to])
Returns a subsection of the array. Negative values for to and from offset from the end of the array.

U.slice(asList(1, 2, 3, 4), 1);
=> [2, 3, 4]
U.slice(asList(1, 2, 3, 4), 3);
=> [3, 4]
U.slice(asList(1, 2, 3, 4), 2, -1);
=> [2, 3]
U.slice(asList(1, 2, 3, 4), -3, -1);
=> [2, 3]

Functions

bind U.bind(function, object, [*arguments])
Bind a function to a structure, meaning that whenever the function is called, the value of "this" will be the structure. Optionally, bind arguments to the function to pre-fill them, also known as partial application.

class GreetingFunction implements Function<String, String> {
    private final String name;
    public GreetingFunction(final String name) {
        this.name = name;
    }
    public String apply(final String greeting) {
        return greeting + ": " + this.name;
    }
}
U.bind(new GreetingFunction("moe")).apply("hi");
=> "hi: moe"

memoize U.memoize(function)
Memoizes a given function by caching the computed result. Useful for speeding up slow-running computations.

class FibonacciFuncion1 extends MemoizeFunction<Integer> {
    public Integer calc(final Integer n) {
        return n < 2 ? n : apply(n - 1) + apply(n - 2);
    }
}
fibonacci = new FibonacciFuncion1().apply(10));
// 55

delay U.delay(function, wait, arguments)
Delays a function for the given number of milliseconds, and then calls it with the arguments supplied in the args struct.

U.delay(new Supplier<String>() {
    public String get() {
        return "hi";
    }
}, 1000);
=> "hi" // appears after one second

once U.once(function)
Returns a function that will be executed at most one time, no matter how often you call it. Useful for lazy initialization.

final Integer[] counter = new Integer[] {0};
Supplier<Integer> incr = new Supplier<Integer>() { public Integer get() {
    counter[0]++; return counter[0]; } };
Supplier<Integer> onceIncr = U.once(incr);
onceIncr.get();
onceIncr.get();
=> counter[0] == 1

debounce U.debounce(function, wait)
Returns a function that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds.

final Integer[] counter = new Integer[] {0};
Supplier<Void> incr = new Supplier<Void>() { public Void get() { counter[0]++; return null; } };
Supplier<Void> debouncedIncr = U.debounce(incr, 50);
debouncedIncr.get();
debouncedIncr.get();
U.delay(debouncedIncr, 16);
U.delay(new Supplier<Void>() {
    public Void get() {
        System.out.println(counter[0]);
        return null;
    }
}, 60);
Thread.sleep(120);
=> function argument is called only once

after U.after(count, function)
Returns a function that will only be executed after being called N times. When count <= 0, the result of calling the function immediately is returned.

final List<Integer> notes = asList(1, 2, 3);
final Supplier<Integer> renderNotes = U.after(notes.size(),
    new Supplier<Integer>() { public Integer get() {
        return 4; } });
final List<Integer> result = new ArrayList<Integer>();
U.<Integer>each(notes, new Consumer<Integer>() {
    public void accept(Integer item) {
        result.add(item);
        Integer afterResult = renderNotes.get();
        if (afterResult != null) {
            result.add(afterResult);
        }
    }
});
=> [1, 2, 3, 4]

before U.before(count, function)
Returns a function that will only be executed after being called N times. When count <= 0, the result of calling the function immediately is returned.

final List<Integer> notes = asList(1, 2, 3);
final Supplier<Integer> renderNotes = U.before(notes.size() - 1,
    new Supplier<Integer>() { public Integer get() {
        return 4; } });
final List<Integer> result = new ArrayList<Integer>();
U.<Integer>each(notes, new Consumer<Integer>() {
    public void accept(Integer item) {
        result.add(item);
        Integer afterResult = renderNotes.get();
        if (afterResult != null) {
            result.add(afterResult);
        }
    }
});
=> [1, 4, 2, 4, 3, 4]

wrap U.wrap(function, wrapper)
Returns the first function passed as an argument to the second, allowing you to adjust arguments, run code before and after, and conditionally execute the original function.

Function<String, String> hello = new Function<String, String>() {
    public String apply(final String name) {
        return "hello: " + name;
    }
};
Function<Void, String> result = U.wrap(hello, new Function<Function<String, String>, String>() {
    public String apply(final Function<String, String> func) {
        return "before, " + func.apply("moe") + ", after";
    }
});
result.apply(null);
=> "before, hello: moe, after"

compose U.compose(*functions)
Returns a function that is the composition of a list of functions, each function consumes the return value of the function that follows. In math terms, composing the functions f(), g(), and h() produces f(g(h())).

Function<String, String> greet = new Function<String, String>() {
    public String apply(final String name) {
        return "hi: " + name;
    }
};
Function<String, String> exclaim = new Function<String, String>() {
    public String apply(final String statement) {
        return statement.toUpperCase() + "!";
    }
};
Function<String, String> welcome = U.compose(greet, exclaim);
welcome.apply("moe");
=> 'hi: MOE!';

Object/Struct Functions

keys U.keys(object)
Retrieve all the names of the object's properties.

U.keys(new LinkedHashMap<String, Object>() { {
    put("one", 1); put("two", 2); put("three", 3); } });
=> ["one", "two", "three"]

values U.values(object)
Return all of the values of the object's own properties.

U.values(new LinkedHashMap<String, Integer>() { {
    put("one", 1); put("two", 2); put("three", 3); } });
=> [1, 2, 3]

pairs U.pairs(object)
Convert an object into a list of [key, value] pairs.

U.pairs(new LinkedHashMap<String, Integer>() { {
    put("one", 1); put("two", 2); put("three", 3); } });
=> [(one, 1), (two, 2), (three, 3)]

invert U.invert(object)
Returns a copy of the object where the keys have become the values and the values the keys. For this to work, all of your object's values should be unique and string serializable.

U.invert(new LinkedHashMap<String, String>() { {
    put("Moe", "Moses"); put("Larry", "Louis"); put("Curly", "Jerome"); } });
=> [(Moses, Moe), (Louis, Larry), (Jerome, Curly)]

functions U.functions(object)
Returns a sorted array of the names of every method in an object -- that is to say, the name of every function property of the object.

U.functions(U.class);
=> ["all", "any", "bind", "clone", "compact", "compose" ...

extend U.extend(destination, *sources)
Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.

U.extend(new LinkedHashMap<String, Object>() { { put("name", "moe"); } },
    new LinkedHashMap<String, Object>() { { put("age", 50); } });
=> {name=moe, age=50}

pick U.pick(object, *keys)
Return a copy of the object, filtered to only have values for the whitelisted keys (or array of valid keys).

U.pick(
    new LinkedHashMap<String, Object>() { { put("name", "moe"); put("age", 50); put("userid", "moe1"); } },
    "name", "age"
);
=> [(name, moe), (age, 50)]

omit U.omit(object, *keys)
Return a copy of the object, filtered to omit the blacklisted keys (or array of keys).

U.omit(
    new LinkedHashMap<String, Object>() { { put("name", "moe"); put("age", 50); put("userid", "moe1"); } },
    "userid"
);
=> [(name, moe), (age, 50)]

defaults U.defaults(object, *defaults)
Fill in missing properties in object with default values from the defaults objects, and return the object. As soon as the property is filled, further defaults will have no effect.

Map<String, String> iceCream = new LinkedHashMap<String, String>() { { put("flavor", "chocolate"); } };
Map<String, String> result = U.defaults(iceCream, new LinkedHashMap<String, String>() { {
    put("flavor", "vanilla"); put("sprinkles", "lots"); } });
=> {flavor=chocolate, sprinkles=lots}

clone U.clone(object)
Create a shallow-copied clone of the object. Any nested structs or objects will be copied by reference, not duplicated.

U.clone(new LinkedHashMap<String, String>() { {
    put("name", "moe"); } });
=> {name=moe}

has U.has(object, key)
Does the object contain the given key?

U.has(new LinkedHashMap<String, Integer>() { {
    put("a", 1); put("b", 2); put("c", 3); } }, "b");
=> true

isEqual U.isEqual(object, other)
Performs a deep comparison between the two objects, to determine if they should be considered equal.

Map<String, Object> stooge = new LinkedHashMap<String, Object>() { {
    put("name", "moe"); put("luckyNumbers", asList(13, 27, 34)); } };
Map<String, Object> clone = new LinkedHashMap<String, Object>() { {
    put("name", "moe"); put("luckyNumbers", asList(13, 27, 34)); } };
=> stooge == clone // false
=> U.isEqual(stooge, clone); // true

isEmpty U.isEmpty(object)
Returns true if object contains no values.

U.isEmpty(asList(1, 2, 3, 4))
=> false
U.isEmpty(new ArrayList<String>())
=> true

isArray U.isArray(object)
Returns true if object is an Array.

U.isArray("string")
=> false
U.isArray(new int[] {1, 2, 3, 4, 5});
=> true

isObject U.isObject(object)
Returns true if value is an Object.

U.isObject(new LinkedHashMap<String, String>())
=> true
U.isObject(null);
=> false

isFunction U.isFunction(object)
Returns true if object is a Function.

U.isFunction(new Function<String, Integer>() {
    public Integer apply(final String arg) { return null; } })
=> true

isString U.isString(object)
Returns true if object is a String. Uses Java String type comparison.

U.isString("moe");
=> true
U.isString(1);
=> false

isNumber U.isNumber(object)
Returns true if object is of a Java numeric type.

U.isNumber(1);
=> true

isBoolean U.isBoolean(object)
Returns true if object is a boolean.

U.isBoolean(false);
=> true

isDate U.isDate(object)
Returns true if object is a date.

U.isDate(new java.util.Date())
=> true

Utility Functions

times U.times(n, iterator)
Invokes the given iterator function n times.

final List<Integer> result = new ArrayList<Integer>();
U.times(3, new Supplier<Integer>() {
    public Integer get() {
        result.add(1);
        return null;
    }
});
=> [1, 1, 1]

random U.random([min], max)
Returns a random integer between min and max, inclusive. If you only pass one argument, it will return a number between 0 and that number.

U.random(0, 100);
=> 42

mixin U.mixin(object)
Allows you to extend Underscore with your own utility functions.

U.mixin("capitalize", new Function<String, String>() {
    public String apply(final String string) {
        return String.valueOf(string.charAt(0)).toUpperCase() + string.substring(1).toLowerCase();
    }
});
new U("fabio").call("capitalize").get()
=> "Fabio"

uniqueId U.uniqueId(prefix)
Generates an identifier that is unique for this instance of Underscore

U.uniqueId("c");
=> c1

uniquePassword U.uniquePassword()
Generates a random text with 8-15 characters length

U.uniquePassword();
=> FKV276qgb-&

escape U.escape(input)
Escapes a string for insertion into HTML, replacing &, <, >, and " characters.

U.escape("Curly, Larry & Moe");
=> "Curly, Larry &amp; Moe"

result U.result(object, property)
If the value of the named property is a function then invoke it; otherwise, return it.

Map<String, Object> object = new LinkedHashMap<String, Object>() { {
    put("cheese", "crumpets");
    put("stuff", new Supplier<String>() { public String get() { return "nonsense"; } });
} };

U.result(object.entrySet(), new Predicate<Map.Entry<String, Object>>() {
    public boolean test(Map.Entry<String, Object> item) {
        return item.getKey().equals("cheese");
    }
});
=> "crumpets"
U.result(object.entrySet(), new Predicate<Map.Entry<String, Object>>() { public boolean test(Map.Entry<String, Object> item) { return item.getKey().equals("stuff"); } }); => "nonsense"

Chaining

You can use Underscore-java in either an object-oriented or a functional style, depending on your preference. The following two lines of code are identical ways to double a list of numbers.

U.map(asList(1, 2, 3), new Function<Integer, Integer>() {
    public Integer apply(Integer item) {
        return item * 2;
    }
});
new U<Integer>(asList(1, 2, 3)).map(new Function<Integer, Integer>() {
    public Integer apply(Integer item) {
        return item * 2;
    }
});

Calling chain will cause all future method calls to return wrapped objects. When you've finished the computation, call item or value to retrieve the final value. Here's an example of chaining together a map/flatten/reduce, in order to get the word count of every word in a song.

final List<Map<String, Object>> lyrics = new ArrayList<Map<String, Object>>() { {
    add(new LinkedHashMap<String, Object>() { {
        put("line", 1); put("words", "I'm a lumberjack and I'm okay"); } });
    add(new LinkedHashMap<String, Object>() { {
        put("line", 2); put("words", "I sleep all night and I work all day"); } });
    add(new LinkedHashMap<String, Object>() { {
        put("line", 3); put("words", "He's a lumberjack and he's okay"); } });
    add(new LinkedHashMap<String, Object>() { {
        put("line", 4); put("words", "He sleeps all night and he works all day"); } });
} };
final String result = U.chain(lyrics)
    .map(
        new Function<Map<String, Object>, List<String>>() {
        public List<String> apply(Map<String, Object> item) {
            return asList(String.valueOf(item.get("words")).split(" "));
        }
    })
    .flatten()
    .reduce(
        new BiFunction<Map<String, Integer>, String, Map<String, Integer>>() {
        public Map<String, Integer> apply(Map<String, Integer> accum, String item) {
            if (accum.get(item) == null) {
                accum.put(item, 1);
            } else {
                accum.put(item, accum.get(item) + 1);
            }
            return accum;
        }
    },
    new LinkedHashMap<String, Integer>()
    )
    .item();

=> {lumberjack: 2, all: 4, night: 2 ... }

chainU.chain(obj)
Returns a wrapped object. Calling methods on this object will continue to return wrapped objects until item or value is called.

final List<Map<String, Object>> stooges = new ArrayList<Map<String, Object>>() { {
    add(new LinkedHashMap<String, Object>() { { put("name", "curly"); put("age", 25); } });
    add(new LinkedHashMap<String, Object>() { { put("name", "moe"); put("age", 21); } });
    add(new LinkedHashMap<String, Object>() { { put("name", "larry"); put("age", 23); } });
} };
final String youngest = U.chain(stooges)
    .sortBy(
        new Function<Map<String, Object>, Integer>() {
        public Integer apply(Map<String, Object> item) {
            return (Integer) item.get("age");
        }
    })
    .map(
        new Function<Map<String, Object>, String>() {
        public String apply(Map<String, Object> item) {
            return item.get("name") + " is " + item.get("age");
        }
    })
    .first().item();

=> "moe is 21"

valueU.chain(obj).value()
Extracts the value of a wrapped object.

U.chain(Arrays.asList(1, 2, 3)).value();
=> [1, 2, 3]

Change Log

1.100 -- April 1st, 2024

1.99 -- March 1st, 2024

1.98 -- February 1st, 2024

1.97 -- January 1st, 2024

1.96 -- December 1st, 2023

1.95 -- November 1st, 2023

1.94 -- October 1st, 2023

1.93 -- September 1st, 2023

1.92 -- August 1st, 2023

1.91 -- July 2nd, 2023

1.90 -- June 1st, 2023

1.89 -- May 1st, 2023

1.88 -- April 1st, 2023

1.87 -- March 1st, 2023

1.86 -- February 1st, 2023

1.85 -- January 2nd, 2023

1.84 -- December 1st, 2022

1.83 -- November 1st, 2022

1.82 -- October 1st, 2022

1.81 -- September 2nd, 2022

1.80 -- August 1st, 2022

1.79 -- July 1st, 2022

1.78 -- June 1st, 2022

1.77 -- May 1st, 2022

1.76 -- April 1st, 2022

1.75 -- March 1st, 2022

1.74 -- February 1st, 2022

1.73 -- January 1st, 2022

1.72 -- December 1st, 2021

1.71 -- November 1st, 2021

1.70 -- October 1st, 2021

1.69 -- September 1st, 2021

1.68 -- July 1st, 2021

1.67 -- June 1st, 2021

1.66 -- May 1st, 2021

1.65 -- April 1st, 2021

1.64 -- March 1st, 2021

1.63 -- February 1st, 2021

1.62 -- January 1st, 2021

1.61 -- December 1st, 2020

1.60 -- November 1st, 2020

1.59 -- October 1st, 2020

1.58 -- September 1st, 2020

1.57 -- August 1st, 2020

1.56 -- July 3rd, 2020

1.55 -- June 1st, 2020

1.54 -- April 1st, 2020

1.53 -- March 1st, 2020

1.52 -- February 1st, 2020

1.51 -- January 2nd, 2020

1.50 -- December 2nd, 2019

1.49 -- November 1st, 2019

1.48 -- October 2nd, 2019

1.47 -- August 1st, 2019

1.46 -- July 1st, 2019

1.45 -- June 1st, 2019

1.44 -- May 1st, 2019

1.43 -- April 1st, 2019

1.42 -- March 1st, 2019

1.41 -- February 1st, 2019

1.40 -- January 1st, 2019

1.39 -- December 1st, 2018

1.38 -- November 1st, 2018

1.37 -- October 1st, 2018

1.36 -- September 1st, 2018

1.35 -- August 3rd, 2018

1.34 -- July 1st, 2018

1.33 -- June 1st, 2018

1.32 -- May 1st, 2018

1.31 -- April 1st, 2018

1.30 -- February 1st, 2018

1.29 -- January 1st, 2018

1.28 -- November 1st, 2017

1.27 -- July 1st, 2017

1.26 -- June 2nd, 2017

1.25 -- May 1st, 2017

1.24 -- Apr 1st, 2017

1.23 -- Feb 2nd, 2017

1.22 -- Jan 2nd, 2017

1.21 -- Dec 1st, 2016

1.20 -- Nov 1st, 2016

1.19 -- Oct 1st, 2016

1.18 -- Sep 1st, 2016

1.17 -- Aug 1st, 2016

1.16 -- Jul 1st, 2016

1.15 -- Jun 1st, 2016

1.14 -- May 1st, 2016

1.13 -- Apr 1st, 2016

1.12 -- Mar 1st, 2016

1.11 -- Feb 4th, 2016

1.10 -- Jan 1st, 2016

1.9 -- Dec 1st, 2015

1.8 -- Nov 1st, 2015

1.7 -- Oct 1st, 2015

1.6 -- Sep 1st, 2015

1.5 -- Aug 1st, 2015

1.4 -- July 1st, 2015

1.3 -- June 1st, 2015

1.2 -- May 1st, 2015

1.1 -- April 4th, 2015

1.0 -- December 27, 2014

Fork me on GitHub