
no.motif.iter.CollectingIterable Maven / Gradle / Ivy
package no.motif.iter;
import static java.util.Collections.sort;
import static java.util.Collections.unmodifiableList;
import static no.motif.Base.toString;
import static no.motif.Iterate.by;
import static no.motif.Singular.optional;
import static no.motif.Strings.concat;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import no.motif.Singular;
import no.motif.Strings;
import no.motif.f.Do;
import no.motif.f.Fn;
import no.motif.f.Fn2;
import no.motif.f.Predicate;
import no.motif.single.Optional;
import no.motif.types.Elements;
/**
* This iterable offers operations which requires collecting (i.e.
* iterating) the containing elements of the iterable. In
* particular, this class holds the methods which "bridges" back
* to the non-lazy Java Collection Framework classes.
*
* @param The type of elements in this iterable.
*/
abstract class CollectingIterable implements Elements, Serializable {
@Override
public final O reduce(O unit, Fn2 super O, ? super T, ? extends O> reducer) {
O reduced = unit;
for (T element : this) reduced = reducer.$(reduced, element);
return reduced;
}
@Override
public final List collect() {
return unmodifiableList(collectIn(new ArrayList()));
}
@Override
public final > C collectIn(C collection) {
for (T t : this) {
collection.add(t);
}
return collection;
}
@Override
public final > List sortedBy(Fn super T, P> property) {
return sorted(by(property));
}
@Override
public final List sorted(Comparator super T> comparator) {
List elements = collectIn(new ArrayList());
sort(elements, comparator);
return unmodifiableList(elements);
}
@Override
public void each(Do super T> sideEffect) {
for (T element : this) sideEffect.with(element);
}
@Override
public Map
> groupBy(Fn super T, P> property) {
Map
> map = new LinkedHashMap<>();
for (T elem : this) {
P key = property.$(elem);
List list;
if (!map.containsKey(key)) {
list = new ArrayList<>();
map.put(key, list);
} else {
list = map.get(key);
}
list.add(elem);
}
return map;
}
@Override
public Map
mapBy(Fn super T, P> uniqueProperty) {
Map
map = new LinkedHashMap<>();
for (T elem : this) {
P key = uniqueProperty.$(elem);
if (!map.containsKey(key)) map.put(key, elem);
else throw new IllegalStateException(
"Cannot create the map since both '" + map.get(key) + "' and '" + elem + "' yields " +
"the same key: '" + key + "'. This is either due to an inconsistency in " +
"your data, or you should use .groupBy(Fn) instead to allow multiple elements " +
"being mapped by the same key.");
}
return map;
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public boolean exists(Predicate super T> predicate) {
for (T t : this) if (predicate.$(t)) return true;
return false;
}
@Override
public Optional head() {
return !isEmpty() ? optional(iterator().next()) : Singular.none();
}
@Override
public Optional last() {
T last = null;
for (T elem : this) last = elem;
return optional(last);
}
@Override
public String join() {
return reduce("", concat);
}
@Override
public String join(String separator) {
if (isEmpty()) return "";
return head().map(toString).append(tail().map(Strings.prepend(separator))).reduce("", concat);
}
/**
* Textual description of the contents of the iterable. Have in mind that for lazy
* implementations, using {@link #toString()} will iterate over the elements to
* actually be able to create the description.
*/
@Override
public String toString() {
return collect().toString();
}
}