package com.kickstarter.libs.utils;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import rx.functions.Func2;
public final class ListUtils {
private ListUtils() {
throw new AssertionError();
}
/**
* Returns a new list with all elements in `xs` equal to `x` replaced by `newx`.
*/
public static @NonNull <T> List<T> allReplaced(final @NonNull List<T> xs, final @NonNull T x, final @NonNull T newx) {
final List<T> ys = new ArrayList<>(xs);
for (int idx = 0; idx < xs.size(); idx++) {
if (x.equals(xs.get(idx))) {
ys.set(idx, newx);
}
}
return ys;
}
/**
* Appends `x` to the end of the list `xs`.
*/
public static @NonNull <T> List<T> append(final @NonNull List<T> xs, final @NonNull T x) {
final List<T> ys = new ArrayList<>(xs);
ys.add(x);
return ys;
}
/**
* Concats the second argument onto the end of the first without mutating
* either list.
*/
public static <T> List<T> concat(final @NonNull List<T> xs, final @NonNull List<T> ys) {
final List<T> zs = new ArrayList<>(xs);
ListUtils.mutatingConcat(zs, ys);
return zs;
}
/**
* Concats the distinct elements of `ys` onto the end of the `xs` without mutating either list.
*/
public static <T> List<T> concatDistinct(final @NonNull List<T> xs, final @NonNull List<T> ys) {
final List<T> zs = new ArrayList<>(xs);
ListUtils.mutatingConcatDistinct(zs, ys);
return zs;
}
/**
* Returns true if `y` is equal to any of the values in `xs`.
*/
public static <T> boolean contains(final @NonNull List<T> xs, final @NonNull T y) {
return ListUtils.contains(xs, y, Object::equals);
}
/**
* Returns true if `y` is equal to any of the values in `xs`, where equality is determined by a given function.
*/
public static <T> boolean contains(final @NonNull List<T> xs, final @NonNull T y, final @NonNull Func2<T, T, Boolean> equality) {
return ListUtils.indexOf(xs, y, equality) != -1;
}
/**
* Returns a list containing elements of `lhs` that do not exist in `rhs`.
*/
public static @NonNull <T> List<T> difference(final @NonNull List<T> lhs, final @NonNull List<T> rhs) {
return ListUtils.difference(lhs, rhs, Object::equals);
}
/**
* Returns a list containing elements of `lhs` that do not exist in `rhs`, where equality between elements is
* determined by a given function.
*/
public static @NonNull <T> List<T> difference(final @NonNull List<T> lhs, final @NonNull List<T> rhs,
final @NonNull Func2<T, T, Boolean> equality) {
final List<T> result = new ArrayList<>();
for (final T litem : lhs) {
if (!ListUtils.contains(rhs, litem, equality)) {
result.add(litem);
}
}
return result;
}
/**
* Returns an empty list.
*/
@NonNull public static <T> List<T> empty() {
return new ArrayList<>();
}
/**
* Returns the first element in `xs` that equals `x`, or `null` if `x` is not found in `xs`.
*/
public static @Nullable <T> T find(final @NonNull List<T> xs, final @NonNull T x) {
return ListUtils.find(xs, x, Object::equals);
}
/**
* Returns the first element in `xs` that equals `x`, or `null` if `x` is not found in `xs`. Equality is determined
* by the given function.
*/
public static @Nullable <T> T find(final @NonNull List<T> xs, final @NonNull T y, final @NonNull Func2<T, T, Boolean> equality) {
final int idx = ListUtils.indexOf(xs, y, equality);
if (idx == -1) {
return null;
}
return xs.get(idx);
}
/**
* Returns the first object or `null` if empty.
*/
public static @Nullable <T> T first(final @NonNull List<T> xs) {
return xs.size() > 0 ? xs.get(0) : null;
}
/**
* Combines a list of lists into a new single, flat list.
*/
public static @NonNull <T> List<T> flatten(final @NonNull List<List<T>> xss) {
final List<T> result = new ArrayList<>();
for (final List<T> xs : xss) {
result.addAll(xs);
}
return result;
}
/**
* Returns the index of the first element in `xs` that equals `x`, or `-1` if `x` is not found in `xs`.
*/
public static <T> int indexOf(final @NonNull List<T> xs, final @NonNull T y) {
return ListUtils.indexOf(xs, y, Object::equals);
}
/**
* Returns the index of the first element in `xs` that equals `x`, or `-1` if `x` is not found in `xs`. Equality is determined
* by the given function.
*/
public static <T> int indexOf(final @NonNull List<T> xs, final @NonNull T y, final @NonNull Func2<T, T, Boolean> equality) {
for (int idx = 0; idx < xs.size(); idx++) {
if (equality.call(xs.get(idx), y)) {
return idx;
}
}
return -1;
}
/**
* Returns a list containing the elements of `lhs` that exist in `rhs`.
*/
public static @NonNull <T> List<T> intersection(final @NonNull List<T> lhs, final @NonNull List<T> rhs) {
return ListUtils.intersection(lhs, rhs, Object::equals);
}
/**
* Returns a list containing the elements of `lhs` that exist in `rhs`, where equality is determined by the given function.
*/
public static @NonNull <T> List<T> intersection(final @NonNull List<T> lhs, final @NonNull List<T> rhs, final @NonNull Func2<T, T, Boolean> equality) {
final List<T> result = new ArrayList<>();
for (final T litem : lhs) {
if (ListUtils.contains(rhs, litem, equality)) {
result.add(litem);
}
}
return result;
}
/**
* Concats the second argument onto the end of the first, but also mutates the
* first argument.
*/
public static <T> List<T> mutatingConcat(final @NonNull List<T> xs, final @NonNull List<T> ys) {
xs.addAll(ys);
return xs;
}
/**
* Concats the distinct elements of `ys` onto the end of the `xs`, but also mutates the first.
*/
public static <T> List<T> mutatingConcatDistinct(final @NonNull List<T> xs, final @NonNull List<T> ys) {
for (final T y : ys) {
if (!xs.contains(y)) {
xs.add(y);
}
}
return xs;
}
/**
* Checks the size of a list and returns `true` if the list is non empty.
*/
public static <T> boolean nonEmpty(final @NonNull List<T> xs) {
return xs.size() > 0;
}
/**
* Prepends `x` to the beginning of the list `xs`.
*/
public static <T> List<T> prepend(final @NonNull List<T> xs, final @NonNull T x) {
final List<T> ys = new ArrayList<>(xs);
ys.add(0, x);
return ys;
}
/**
* Replaces the element at index `idx` with the element `x`. Does so by return a whole new list without
* mutating the original.
*/
public static @NonNull <T> List<T> replaced(final @NonNull List<T> xs, final int idx, final @Nullable T x) {
final List<T> ys = new ArrayList<>(xs);
ys.set(idx, x);
return ys;
}
/**
* Uses Fisher-Yates algorithm to shuffle an array without mutating input arg.
* http://www.dotnetperls.com/shuffle-java
*/
public static <T> List<T> shuffle(final @NonNull List<T> xs) {
final List<T> ys = new ArrayList<>(xs);
final int length = ys.size();
for (int i = 0; i < length; i++) {
final int j = i + (int) (Math.random() * (length - i));
final T temp = ys.get(i);
ys.set(i, ys.get(j));
ys.set(j, temp);
}
return ys;
}
}