All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.servicestack.func.Func Maven / Gradle / Ivy

The newest version!
//  Copyright (c) 2013-present ServiceStack, Inc. All rights reserved.
//  License: https://servicestack.net/bsd-license.txt

package net.servicestack.func;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class Func {

    public static ArrayList toList(int... xs) {
        ArrayList to = new ArrayList<>();
        for (int x : xs) {
            to.add(x);
        }
        return to;
    }

    public static ArrayList toList(double... xs) {
        ArrayList to = new ArrayList<>();
        for (double x : xs) {
            to.add(x);
        }
        return to;
    }

    @SafeVarargs
    public static  ArrayList toList(T... xs) {
        ArrayList to = new ArrayList<>();
        for (T x : xs) {
            to.add(x);
        }
        return to;
    }

    public static  ArrayList toList(Iterable xs) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        for (T x : xs) {
            to.add(x);
        }
        return to;
    }

    public static  T[] toArray(Iterable xs, Class cls) {
        return toArray(toList(xs), cls);
    }

    @SuppressWarnings("unchecked")
    public static  T[] toArray(List list, Class cls) {
        T[] array = (T[]) Array.newInstance(cls, list.size());
        return list.toArray(array);
    }

    public static  HashMap toDictionary(K k1, V v1) {
        HashMap to = new HashMap<>();
        to.put(k1, v1);
        return to;
    }

    public static  HashMap toDictionary(K k1, V v1, K k2, V v2) {
        HashMap to = new HashMap<>();
        to.put(k1, v1);
        to.put(k2, v2);
        return to;
    }

    public static  HashMap toDictionary(K k1, V v1, K k2, V v2, K k3, V v3) {
        HashMap to = toDictionary(k1, v1, k2, v2);
        to.put(k3, v3);
        return to;
    }

    public static  HashMap toDictionary(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
        HashMap to = toDictionary(k1, v1, k2, v2, k3, v3);
        to.put(k4, v4);
        return to;
    }

    public static  HashMap toDictionary(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
        HashMap to = toDictionary(k1, v1, k2, v2, k3, v3, k4, v4);
        to.put(k5, v5);
        return to;
    }

    public static  HashMap toDictionary(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
        HashMap to = toDictionary(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
        to.put(k6, v6);
        return to;
    }

    public static  HashMap toDictionary(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
        HashMap to = toDictionary(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6);
        to.put(k7, v7);
        return to;
    }

    public static  HashMap toDictionary(Tuple... xs) {
        HashMap to = new HashMap<>();
        for (Tuple x : xs) {
            to.put(x.A, x.B);
        }
        return to;
    }

    public static  HashMap toDictionary(Iterable xs, Function f) {
        HashMap to = new HashMap<>();
        for (T x : xs) {
            K key = f.apply(x);
            to.put(key, x);
        }
        return to;
    }

    public static  ArrayList ofType(Iterable xs, Class cls) {
        ArrayList to = new ArrayList();
        for (Object x : xs) {
            if (cls.isInstance(x))
                to.add((T) x);
        }
        return to;
    }

    public static  ArrayList map(T[] xs, Function f) {
        return map(toList(xs), f);
    }

    public static  ArrayList map(Iterable xs, Function f) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        for (T x : xs) {
            R ret = f.apply(x);
            to.add(ret);
        }
        return to;
    }

    public static  ArrayList mapi(T[] xs, FunctionIndex f) {
        return mapi(toList(xs), f);
    }

    public static  ArrayList mapi(Iterable xs, FunctionIndex f) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        int i = 0;
        for (T x : xs) {
            R ret = f.apply(x, i++);
            to.add(ret);
        }
        return to;
    }

    public static  void each(T[] xs, Action f) {
        each(toList(xs), f);
    }

    public static  void each(Iterable xs, Action f) {
        if (xs == null) return;
        for (T x : xs) {
            f.apply(x);
        }
    }

    public static  void forEach(T[] xs, Action f) {
        each(toList(xs), f);
    }

    public static  void forEach(Iterable xs, Action f){
        each(xs, f);
    }

    public static  ArrayList filter(T[] xs, Predicate predicate) {
        return filter(toList(xs), predicate);
    }

    public static  ArrayList filter(Iterable xs, Predicate predicate) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        for (T x : xs) {
            if (predicate.apply(x)) {
                to.add(x);
            }
        }
        return to;
    }

    public static  ArrayList filteri(T[] xs, PredicateIndex predicate) {
        return filteri(toList(xs), predicate);
    }

    public static  ArrayList filteri(Iterable xs, PredicateIndex predicate) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        int i = 0;
        for (T x : xs) {
            if (predicate.apply(x, i++)) {
                to.add(x);
            }
        }
        return to;
    }

    public static  T first(Iterable xs, Predicate predicate) {
        if (xs == null) return null;

        for (T x : xs) {
            if (predicate.apply(x)) {
                return x;
            }
        }
        return null;
    }

    public static  T first(T[] xs) {
        return xs == null || xs.length == 0 ? null : xs[0];
    }

    public static  T first(Iterable xs) {
        return firstOrDefault(xs, (T) null);
    }

    public static  T firstOrDefault(Iterable xs, T defaultValue) {
        if (xs == null)
            return defaultValue;

        for (T x : xs) {
            return x;
        }
        return defaultValue;
    }

    public static  T last(T[] xs, Predicate predicate) {
        return last(toList(xs), predicate);
    }

    public static  T last(Iterable xs, Predicate predicate) {
        if (xs == null) return null;

        for (T x : reverse(xs)) {
            if (predicate.apply(x)) {
                return x;
            }
        }
        return null;
    }

    public static  T last(T[] xs) {
        return xs == null ? null : xs[xs.length - 1];
    }

    public static  T last(Iterable xs) {
        if (xs == null) return null;

        T last = null;
        for (T x : xs) {
            last = x;
        }
        return last;
    }

    public static  boolean contains(T[] xs, Predicate predicate) {
        return contains(toList(xs), predicate);
    }

    public static  boolean contains(Iterable xs, Predicate predicate) {
        return first(xs, predicate) != null;
    }

    public static  ArrayList skip(T[] xs, int skip) {
        return skip(toList(xs), skip);
    }

    public static  ArrayList skip(Iterable xs, int skip) {
        int i = 0;
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        for (T x : xs) {
            if (i++ >= skip) {
                to.add(x);
            }
        }
        return to;
    }

    public static  ArrayList skipWhile(T[] xs, Predicate predicate) {
        return skipWhile(toList(xs), predicate);
    }

    public static  ArrayList skipWhile(Iterable xs, Predicate predicate) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        boolean started = false;
        for (T x : xs) {
            if (!started && predicate.apply(x)) {
                continue;
            }

            started = true;
            to.add(x);
        }
        return to;
    }

    public static  ArrayList skipWhilei(Iterable xs, PredicateIndex predicate) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        int i = 0;
        boolean started = false;
        for (T x : xs) {
            if (!started && predicate.apply(x, i++)) {
                continue;
            }

            started = true;
            to.add(x);
        }
        return to;
    }

    public static  ArrayList takeWhile(T[] xs, Predicate predicate) {
        return takeWhile(toList(xs), predicate);
    }

    public static  ArrayList takeWhile(Iterable xs, Predicate predicate) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        for (T x : xs) {
            if (!predicate.apply(x)) {
                return to;
            }
            to.add(x);
        }
        return to;
    }

    public static  ArrayList takeWhilei(Iterable xs, PredicateIndex predicate) {
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        int i = 0;
        for (T x : xs) {
            if (!predicate.apply(x, i++)) {
                return to;
            }
            to.add(x);
        }
        return to;
    }

    public static  ArrayList take(T[] xs, int take) {
        return take(toList(xs), take);
    }

    public static  ArrayList take(Iterable xs, int take) {
        int i = 0;
        ArrayList to = new ArrayList<>();
        if (xs == null) return to;

        for (T x : xs) {
            if (i++ >= take) {
                return to;
            }
            to.add(x);
        }
        return to;
    }

    public static  boolean any(T[] xs, Predicate predicate) {
        return any(toList(xs), predicate);
    }

    public static  boolean any(Iterable xs, Predicate predicate) {
        if (xs == null) return false;

        for (T x : xs) {
            if (predicate.apply(x)) {
                return true;
            }
        }
        return false;
    }

    public static  boolean all(T[] xs, Predicate predicate) {
        return all(toList(xs), predicate);
    }

    public static  boolean all(Iterable xs, Predicate predicate) {
        if (xs == null) return false;

        for (T x : xs) {
            if (!predicate.apply(x)) {
                return false;
            }
        }
        return true;
    }

    public static  ArrayList expand(Iterable... xss) {
        ArrayList to = new ArrayList<>();
        if (xss == null) return to;

        for (Iterable xs : xss) {
            for (T x : xs) {
                to.add(x);
            }
        }
        return to;
    }

    public static  ArrayList expand(Iterable> xss) {
        ArrayList to = new ArrayList<>();
        if (xss == null) return to;

        for (Iterable xs : xss) {
            for (T x : xs) {
                to.add(x);
            }
        }
        return to;
    }

    public static  T elementAt(T[] xs, int index) {
        return elementAt(toList(xs), index);
    }

    public static  T elementAt(Iterable xs, int index) {
        if (xs == null) return null;

        int i = 0;
        for (T x : xs) {
            if (i++ == index) {
                return x;
            }
        }
        return null;
    }

    public static  ArrayList reverse(T[] xs) {
        return reverse(toList(xs));
    }

    public static  ArrayList reverse(Iterable xs) {
        if (xs == null) return new ArrayList();

        ArrayList clone = toList(xs);
        Collections.reverse(clone);
        return clone;
    }

    //Already cloned
    private static  ArrayList asReversed(ArrayList xs) {
        if (xs == null) return new ArrayList();
        Collections.reverse(xs);
        return xs;
    }

    public static  E reduce(T[] xs, E initialValue, Reducer reducer) {
        return reduce(toList(xs), initialValue, reducer);
    }

    public static  E reduce(Iterable xs, E initialValue, Reducer reducer) {
        if (xs == null) return initialValue;

        E currentValue = initialValue;
        for (T x : xs) {
            currentValue = reducer.reduce(currentValue, x);
        }
        return currentValue;
    }

    public static  E reduceRight(T[] xs, E initialValue, Reducer reducer) {
        return reduceRight(toList(xs), initialValue, reducer);
    }

    public static  E reduceRight(Iterable xs, E initialValue, Reducer reducer) {
        return reduce(reverse(xs), initialValue, reducer);
    }

    public static  String join(T[] xs, String separator) {
        return join(toList(xs), separator);
    }

    public static  String join(Iterable xs, String separator) {
        StringBuilder sb = new StringBuilder();
        if (xs == null) return sb.toString();

        for (T x : xs) {
            if (sb.length() > 0)
                sb.append(separator);
            sb.append(x);
        }
        return sb.toString();
    }

    public static > ArrayList orderBy(T[] xs) {
        return orderBy(toList(xs));
    }

    public static > ArrayList orderBy(Iterable xs) {
        return orderBy(toList(xs));
    }

    private static > ArrayList orderBy(ArrayList cloned) {
        Collections.sort(cloned);
        return cloned;
    }

    public static > ArrayList orderBy(T[] xs, final Function f) {
        return orderBy(toList(xs), f);
    }

    public static > ArrayList orderBy(Iterable xs, final Function f) {
        return orderBy(toList(xs), f);
    }

    private static > ArrayList orderBy(ArrayList cloned, final Function f) {
        Collections.sort(cloned, new Comparator() {
            @Override
            public int compare(T a, T b) {
                R aVal = f.apply(a);
                R bVal = f.apply(b);

                if (aVal == null && bVal == null)
                    return 0;
                if (aVal == null)
                    return -1;
                if (bVal == null)
                    return 1;

                return aVal.compareTo(bVal);
            }
        });

        return cloned;
    }

    public static  ArrayList orderBy(T[] xs, Comparator comparer) {
        return orderBy(toList(xs), comparer);
    }

    public static  ArrayList orderBy(Iterable xs, Comparator comparer) {
        return orderBy(toList(xs), comparer);
    }

    private static  ArrayList orderBy(ArrayList cloned, Comparator comparer) {
        Collections.sort(cloned, comparer);
        return cloned;
    }

    public static > ArrayList orderByDesc(T[] xs, final Function f) {
        return asReversed(orderBy(toList(xs), f));
    }

    public static > ArrayList orderByDesc(Iterable xs, final Function f) {
        return asReversed(orderBy(toList(xs), f));
    }

    public static > List orderByDesc(T[] xs) {
        return asReversed(orderBy(toList(xs)));
    }

    public static > List orderByDesc(Iterable xs) {
        return asReversed(orderBy(toList(xs)));
    }

    public static  List orderByDesc(T[] xs, Comparator comparer) {
        return asReversed(orderBy(toList(xs), comparer));
    }

    public static  List orderByDesc(Iterable xs, Comparator comparer) {
        return asReversed(orderBy(toList(xs), comparer));
    }

    @SafeVarargs
    public static  ArrayList orderByAll(T[] xs, Comparator... comparers) {
        return orderByAll(toList(xs), comparers);
    }

    @SafeVarargs
    public static  ArrayList orderByAll(Iterable xs, Comparator... comparers) {
        return orderByAll(toList(xs), comparers);
    }

    @SafeVarargs
    private static  ArrayList orderByAll(ArrayList cloned, final Comparator... comparers) {
        Collections.sort(cloned, new Comparator() {
            @Override
            public int compare(T a, T b) {
                for (Comparator c : comparers) {
                    int cmp = c.compare(a, b);
                    if (cmp != 0) {
                        return cmp;
                    }
                }
                return 0;
            }
        });
        return cloned;
    }

    public static  ArrayList> groupBy(
            Iterable xs,
            Function f) {
        return groupBy(xs, f, null, null);
    }

    public static  ArrayList> groupBy(
            Iterable xs,
            Function f,
            Predicate2 matchWith) {
        return groupBy(xs, f, matchWith, null);
    }

    public static  ArrayList> groupBy(
            Iterable xs,
            Function f,
            Predicate2 matchWith,
            Function valueAs) {
        ArrayList to = new ArrayList<>();

        Map> map = new HashMap<>();

        for (Item x : xs) {
            Item e = x;
            Key val = f.apply(e);
            Key key = val;

            if (matchWith != null) {
                for (Key k : map.keySet()) {
                    if (matchWith.apply(val, k)) {
                        key = k;
                        break;
                    }
                }
            }

            if (valueAs != null) {
                e = valueAs.apply(e);
            }

            Group group;
            if (!map.containsKey(key)) {
                map.put(key, group = new Group(key));
            } else {
                group = map.get(key);
            }

            group.add(e);
        }

        return toList(map.values());
    }

    public static  ArrayList distinct(T[] xs) {
        return distinct(toList(xs));
    }

    public static  ArrayList distinct(Iterable xs) {
        HashSet to = new HashSet();
        for (T x : xs) {
            to.add(x);
        }
        return toList(to);
    }

    @SafeVarargs
    public static  ArrayList union(T[]... xss) {
        ArrayList to = new ArrayList();
        for (T[] xs : xss) {
            for (T x : xs) {
                if (!to.contains(x))
                    to.add(x);
            }
        }
        return to;
    }

    @SafeVarargs
    public static  ArrayList union(Iterable... xss) {
        ArrayList to = new ArrayList();
        for (Iterable xs : xss) {
            for (T x : xs) {
                if (!to.contains(x))
                    to.add(x);
            }
        }
        return to;
    }

    @SafeVarargs
    public static  ArrayList concat(T[]... xss) {
        ArrayList to = new ArrayList();
        for (T[] xs : xss) {
            for (T x : xs) {
                to.add(x);
            }
        }
        return to;
    }

    @SafeVarargs
    public static  ArrayList concat(Iterable... xss) {
        ArrayList to = new ArrayList();
        for (Iterable xs : xss) {
            for (T x : xs) {
                to.add(x);
            }
        }
        return to;
    }

    @SafeVarargs
    public static  ArrayList intersect(T[]... xss) {
        return intersect(expand(xss));
    }

    @SafeVarargs
    public static  ArrayList intersect(Iterable... xss) {
        ArrayList first = null;
        for (Iterable xs : xss) {
            if (first == null) {
                first = union(xs);
                continue;
            }
            retainOnly(first, toList(xs));
        }
        return first;
    }

    public static  void retainOnly(List source, List occurrances) {
        for (int i = source.size() - 1; i >= 0; i--) {
            if (!occurrances.contains(source.get(i))) {
                source.remove(i);
            }
        }
    }

    @SafeVarargs
    public static  ArrayList difference(T[] original, T[]... xss) {
        return difference(toList(original), expand(xss));
    }

    @SafeVarargs
    public static  ArrayList difference(Iterable source, Iterable... xss) {
        return difference(toList(source), expand(xss));
    }

    @SafeVarargs
    public static  ArrayList difference(List source, List... xss) {
        ArrayList to = new ArrayList();
        for (List xs : xss) {
            for (T x : source) {
                if (!xs.contains(x) && !to.contains(x)) {
                    to.add(x);
                }
            }
        }
        return to;
    }

    public static int[] range(int begin, int end) {
        if (end >= begin) {
            int[] to = new int[++end - begin];
            for (int i = 0; begin < end; ) {
                to[i++] = begin++;
            }
            return to;
        } else {
            int[] to = new int[++begin - end];
            for (int i = 0; end < begin; ) {
                to[i++] = end++;
            }
            return to;
        }
    }

    public static int[] repeat(int repeatedValue, int count) {
        int[] to = new int[count];
        for (int i = 0; i < count; i++) {
            to[i] = repeatedValue;
        }
        return to;
    }


    public static  int count(T[] xs, Predicate predicate) {
        return count(toList(xs), predicate);
    }

    public static  int count(Iterable xs, Predicate predicate) {
        if (xs == null) return 0;

        int count = 0;
        for (T x : xs) {
            if (predicate.apply(x)) {
                count++;
            }
        }
        return count;
    }

    public static int sum(int[] xs) {
        return sum(toList(xs), (Predicate) null);
    }

    public static int sum(int[] xs, Predicate predicate) {
        return sum(toList(xs), predicate);
    }

    public static Integer sum(Iterable xs) {
        return sum(xs, (Predicate) null);
    }

    public static Integer sum(Iterable xs, Predicate predicate) {
        if (xs == null) return 0;

        int sum = 0;
        for (Integer x : xs) {
            if (predicate == null || predicate.apply(x)) {
                sum += x;
            }
        }
        return sum;
    }

    public static  Integer sum(T[] xs, Function f) {
        return sum(toList(xs), f);
    }

    public static  Integer sum(Iterable xs, Function f) {
        if (xs == null) return 0;

        int sum = 0;
        for (T x : xs) {
            sum += f.apply(x);
        }
        return sum;
    }

    public static double sumDouble(double[] xs) {
        return sumDouble(toList(xs), (Predicate) null);
    }

    public static double sumDouble(double[] xs, Predicate predicate) {
        return sumDouble(toList(xs), predicate);
    }

    public static Double sumDouble(Iterable xs) {
        return sumDouble(xs, (Predicate) null);
    }

    public static Double sumDouble(Iterable xs, Predicate predicate) {
        if (xs == null) return 0d;

        double sum = 0;
        for (Double x : xs) {
            if (predicate == null || predicate.apply(x)) {
                sum += x;
            }
        }
        return sum;
    }

    public static  Double sumDouble(T[] xs, Function f) {
        return sumDouble(toList(xs), f);
    }

    public static  Double sumDouble(Iterable xs, Function f) {
        if (xs == null) return 0d;

        double sum = 0;
        for (T x : xs) {
            sum += f.apply(x);
        }
        return sum;
    }

    public static  ArrayList expand(T[]... xss) {
        ArrayList to = new ArrayList<>();
        if (xss == null) return to;

        for (T[] xs : xss) {
            for (T x : xs) {
                to.add(x);
            }
        }
        return to;
    }

    public static int min(int[] xs) {
        return min(toList(xs), (Predicate) null);
    }

    public static int min(int[] xs, Predicate predicate) {
        return min(toList(xs), predicate);
    }

    public static Integer min(Iterable xs) {
        return min(xs, (Predicate) null);
    }

    public static Integer min(Iterable xs, Predicate predicate) {
        if (xs == null) return null;

        Integer min = null;
        for (Integer x : xs) {
            if (predicate == null || predicate.apply(x)) {
                if (min == null || x < min)
                    min = x;
            }
        }
        return min;
    }

    public static  Integer min(T[] xs, Function f) {
        return min(toList(xs), f);
    }

    public static  Integer min(Iterable xs, Function f) {
        if (xs == null) return null;

        Integer min = null;
        for (T t : xs) {
            Integer x = f.apply(t);
            if (min == null || x < min)
                min = x;
        }
        return min;
    }

    public static double minDouble(double[] xs) {
        return minDouble(toList(xs), (Predicate) null);
    }

    public static double minDouble(double[] xs, Predicate predicate) {
        return minDouble(toList(xs), predicate);
    }

    public static Double minDouble(Iterable xs) {
        return minDouble(xs, (Predicate) null);
    }

    public static Double minDouble(Iterable xs, Predicate predicate) {
        if (xs == null) return null;

        Double min = null;
        for (Double x : xs) {
            if (predicate == null || predicate.apply(x)) {
                if (min == null || x < min)
                    min = x;
            }
        }
        return min;
    }

    public static  Double minDouble(T[] xs, Function f) {
        return minDouble(toList(xs), f);
    }

    public static  Double minDouble(Iterable xs, Function f) {
        if (xs == null) return null;

        Double min = null;
        for (T t : xs) {
            Double x = f.apply(t);
            if (min == null || x < min)
                min = x;
        }
        return min;
    }

    public static int max(int[] xs) {
        return max(toList(xs), (Predicate) null);
    }

    public static int max(int[] xs, Predicate predicate) {
        return max(toList(xs), predicate);
    }

    public static Integer max(Iterable xs) {
        return max(xs, (Predicate) null);
    }

    public static Integer max(Iterable xs, Predicate predicate) {
        if (xs == null) return null;

        Integer max = null;
        for (Integer x : xs) {
            if (predicate == null || predicate.apply(x)) {
                if (max == null || x > max)
                    max = x;
            }
        }
        return max;
    }

    public static  Integer max(T[] xs, Function f) {
        return max(toList(xs), f);
    }

    public static  Integer max(Iterable xs, Function f) {
        if (xs == null) return null;

        Integer max = null;
        for (T t : xs) {
            Integer x = f.apply(t);
            if (max == null || x > max)
                max = x;
        }
        return max;
    }

    public static double maxDouble(double[] xs) {
        return maxDouble(toList(xs), (Predicate) null);
    }

    public static double maxDouble(double[] xs, Predicate predicate) {
        return maxDouble(toList(xs), predicate);
    }

    public static Double maxDouble(Iterable xs) {
        return maxDouble(xs, (Predicate) null);
    }

    public static Double maxDouble(Iterable xs, Predicate predicate) {
        if (xs == null) return null;

        Double max = null;
        for (Double x : xs) {
            if (predicate == null || predicate.apply(x)) {
                if (max == null || x > max)
                    max = x;
            }
        }
        return max;
    }

    public static  Double maxDouble(T[] xs, Function f) {
        return maxDouble(toList(xs), f);
    }

    public static  Double maxDouble(Iterable xs, Function f) {
        if (xs == null) return null;

        Double max = null;
        for (T t : xs) {
            Double x = f.apply(t);
            if (max == null || x > max)
                max = x;
        }
        return max;
    }

    public static double avg(int[] xs) {
        return avg(toList(xs), (Predicate) null);
    }

    public static double avg(int[] xs, Predicate predicate) {
        return avg(toList(xs), predicate);
    }

    public static Double avg(Iterable xs) {
        return avg(xs, (Predicate) null);
    }

    public static Double avg(Iterable xs, Predicate predicate) {
        return avg(toList(xs), predicate);
    }

    public static Double avg(List xs, Predicate predicate) {
        return sum(xs, predicate) / (double) xs.size();
    }

    public static  Double avg(T[] xs, Function f) {
        return avg(toList(xs), f);
    }

    public static  Double avg(Iterable xs, Function f) {
        return avg(toList(xs), f);
    }

    public static  Double avg(List xs, Function f) {
        return sum(xs, f) / (double) xs.size();
    }

    public static double avgDouble(double[] xs) {
        return avgDouble(toList(xs), (Predicate) null);
    }

    public static double avgDouble(double[] xs, Predicate predicate) {
        return avgDouble(toList(xs), predicate);
    }

    public static Double avgDouble(Iterable xs) {
        return avgDouble(xs, (Predicate) null);
    }

    public static Double avgDouble(Iterable xs, Predicate predicate) {
        return avgDouble(toList(xs), predicate);
    }

    public static Double avgDouble(List xs, Predicate predicate) {
        return sumDouble(xs, predicate) / (double) xs.size();
    }

    public static  Double avgDouble(T[] xs, Function f) {
        return avgDouble(toList(xs), f);
    }

    public static  Double avgDouble(Iterable xs, Function f) {
        return avgDouble(toList(xs), f);
    }

    public static  Double avgDouble(List xs, Function f) {
        return sumDouble(xs, f) / (double) xs.size();
    }

    public static  List> join(Iterable xs, final Iterable with, final Predicate2 match) {
        return expand(
                map(xs, new Function>>() {
                            @Override
                            public List> apply(final T t) {
                                return map
                                        (
                                                filter(with, new Predicate() {
                                                    @Override
                                                    public boolean apply(U u) {
                                                        return match.apply(t, u);
                                                    }
                                                }),
                                                new Function>() {
                                                    @Override
                                                    public Tuple apply(U u) {
                                                        return new Tuple<>(t, u);
                                                    }
                                                }
                                        );
                            }
                        }
                )
        );
    }

    public static  List>> joinGroup(Iterable xs, final Iterable with, final Predicate2 match) {
        return groupBy(
                join(xs, with, match),
                new Function, T>() {
                    @Override
                    public T apply(Tuple x) {
                        return x.A;
                    }
                }
        );
    }
}