summaryrefslogtreecommitdiff
path: root/base/src/main/java/bjc/utils/funcutils/ListUtils.java
diff options
context:
space:
mode:
authorBenjamin J. Culkin <bjculkin@mix.wvu.edu>2017-10-08 22:39:59 -0300
committerBenjamin J. Culkin <bjculkin@mix.wvu.edu>2017-10-08 22:39:59 -0300
commitc82e3b3b2de0633317ec8fc85925e91422820597 (patch)
tree96567416ce23c5ce85601f9cedc3a94bb1c55cba /base/src/main/java/bjc/utils/funcutils/ListUtils.java
parentb3ac1c8690c3e14c879913e5dcc03a5f5e14876e (diff)
Start splitting into maven modules
Diffstat (limited to 'base/src/main/java/bjc/utils/funcutils/ListUtils.java')
-rw-r--r--base/src/main/java/bjc/utils/funcutils/ListUtils.java294
1 files changed, 294 insertions, 0 deletions
diff --git a/base/src/main/java/bjc/utils/funcutils/ListUtils.java b/base/src/main/java/bjc/utils/funcutils/ListUtils.java
new file mode 100644
index 0000000..c0daa1e
--- /dev/null
+++ b/base/src/main/java/bjc/utils/funcutils/ListUtils.java
@@ -0,0 +1,294 @@
+package bjc.utils.funcutils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.IList;
+
+/**
+ * Utilities for manipulating FunctionalLists that don't belong in the class
+ * itself
+ *
+ * @author ben
+ *
+ */
+public class ListUtils {
+ private static final int MAX_NTRIESPART = 50;
+
+ /**
+ * Collapse a string of tokens into a single string without adding any
+ * spaces
+ *
+ * @param input
+ * The list of tokens to collapse
+ * @return The collapsed string of tokens
+ */
+ public static String collapseTokens(final IList<String> input) {
+ if (input == null) throw new NullPointerException("Input must not be null");
+
+ return collapseTokens(input, "");
+ }
+
+ /**
+ * Collapse a string of tokens into a single string, adding the desired
+ * separator after each token
+ *
+ * @param input
+ * The list of tokens to collapse
+ * @param seperator
+ * The separator to use for separating tokens
+ * @return The collapsed string of tokens
+ */
+ public static String collapseTokens(final IList<String> input, final String seperator) {
+ if (input == null)
+ throw new NullPointerException("Input must not be null");
+ else if (seperator == null) throw new NullPointerException("Seperator must not be null");
+
+ if (input.getSize() < 1)
+ return "";
+ else if (input.getSize() == 1)
+ return input.first();
+ else {
+ final StringBuilder state = new StringBuilder();
+
+ int i = 1;
+ for (final String itm : input.toIterable()) {
+ state.append(itm);
+
+ if (i != input.getSize()) {
+ state.append(seperator);
+ }
+
+ i += 1;
+ }
+
+ return state.toString();
+ }
+ }
+
+ /**
+ * Select a number of random items from the list without replacement
+ *
+ * @param <E>
+ * The type of items to select
+ * @param list
+ * The list to select from
+ * @param number
+ * The number of items to selet
+ * @param rng
+ * A function that creates a random number from 0 to the
+ * desired number
+ * @return A new list containing the desired number of items randomly
+ * selected from the specified list without replacement
+ */
+
+ public static <E> IList<E> drawWithoutReplacement(final IList<E> list, final int number,
+ final Function<Integer, Integer> rng) {
+ final IList<E> selected = new FunctionalList<>(new ArrayList<>(number));
+
+ final int total = list.getSize();
+
+ final Iterator<E> itr = list.toIterable().iterator();
+ E element = null;
+
+ for (final int index = 0; itr.hasNext(); element = itr.next()) {
+ /*
+ * n - m
+ */
+ final int winningChance = number - selected.getSize();
+
+ /*
+ * N - t
+ */
+ final int totalChance = total - (index - 1);
+
+ /*
+ * Probability of selecting the t+1'th element
+ */
+ if (NumberUtils.isProbable(winningChance, totalChance, rng)) {
+ selected.add(element);
+ }
+ }
+
+ return selected;
+ }
+
+ /**
+ * Select a number of random items from the list, with replacement
+ *
+ * @param <E>
+ * The type of items to select
+ * @param list
+ * The list to select from
+ * @param number
+ * The number of items to selet
+ * @param rng
+ * A function that creates a random number from 0 to the
+ * desired number
+ * @return A new list containing the desired number of items randomly
+ * selected from the specified list
+ */
+ public static <E> IList<E> drawWithReplacement(final IList<E> list, final int number,
+ final Function<Integer, Integer> rng) {
+ final IList<E> selected = new FunctionalList<>(new ArrayList<>(number));
+
+ for (int i = 0; i < number; i++) {
+ selected.add(list.randItem(rng));
+ }
+
+ return selected;
+ }
+
+ /**
+ * Partition a list into a list of lists, where each element can count
+ * for more than one element in a partition
+ *
+ * @param <E>
+ * The type of elements in the list to partition
+ *
+ * @param input
+ * The list to partition
+ * @param counter
+ * The function to determine the count for each element
+ * for
+ * @param partitionSize
+ * The number of elements to put in each partition
+ *
+ * @return A list partitioned according to the above rules
+ */
+ public static <E> IList<IList<E>> groupPartition(final IList<E> input, final Function<E, Integer> counter,
+ final int partitionSize) {
+ if (input == null)
+ throw new NullPointerException("Input list must not be null");
+ else if (counter == null)
+ throw new NullPointerException("Counter must not be null");
+ else if (partitionSize < 1 || partitionSize > input.getSize()) {
+ final String fmt = "%d is not a valid partition size. Must be between 1 and %d";
+ final String msg = String.format(fmt, partitionSize, input.getSize());
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ /*
+ * List that holds our results
+ */
+ final IList<IList<E>> returned = new FunctionalList<>();
+
+ /*
+ * List that holds elements rejected during current pass
+ */
+ final IList<E> rejected = new FunctionalList<>();
+
+ final GroupPartIteration<E> it = new GroupPartIteration<>(returned, rejected, partitionSize, counter);
+
+ /*
+ * Run up to a certain number of passes
+ */
+ for (int numberOfIterations = 0; numberOfIterations < MAX_NTRIESPART
+ && !rejected.isEmpty(); numberOfIterations++) {
+ input.forEach(it);
+
+ if (rejected.isEmpty()) {
+ /*
+ * Nothing was rejected, so we're done
+ */
+ return returned;
+ }
+ }
+
+
+ final String fmt = "Heuristic (more than %d iterations of partitioning) detected an unpartitionable list. (%s)\nThe following elements were not partitioned: %s\nCurrent group in formation: %s\nPreviously formed groups: %s\n";
+
+ final String msg = String.format(fmt, MAX_NTRIESPART, input.toString(), rejected.toString(), it.currentPartition.toString(), returned.toString());
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Merge the contents of a bunch of lists together into a single list
+ *
+ * @param <E>
+ * The type of value in this lists
+ * @param lists
+ * The values in the lists to merge
+ * @return A list containing all the elements of the lists
+ */
+ @SafeVarargs
+ public static <E> IList<E> mergeLists(final IList<E>... lists) {
+ final IList<E> returned = new FunctionalList<>();
+
+ for (final IList<E> list : lists) {
+ for (final E itm : list.toIterable()) {
+ returned.add(itm);
+ }
+ }
+
+ return returned;
+ }
+
+ /**
+ * Pad the provided list out to the desired size
+ *
+ * @param <E>
+ * The type of elements in the list
+ * @param list
+ * The list to pad out
+ * @param counter
+ * The function to count elements with
+ * @param size
+ * The desired size of the list
+ * @param padder
+ * The function to get elements to pad with
+ * @return The list, padded to the desired size
+ * @throws IllegalArgumentException
+ * if the list couldn't be padded to the desired size
+ */
+ public static <E> IList<E> padList(final IList<E> list, final Function<E, Integer> counter, final int size,
+ final Supplier<E> padder) {
+ int count = 0;
+
+ final IList<E> returned = new FunctionalList<>();
+
+ for (final E itm : list.toIterable()) {
+ count += counter.apply(itm);
+
+ returned.add(itm);
+ }
+
+ if (count % size != 0) {
+ /*
+ * We need to pad
+ */
+ int needed = count % size;
+ int threshold = 0;
+
+ while (needed > 0 && threshold <= MAX_NTRIESPART) {
+ final E val = padder.get();
+ final int newCount = counter.apply(val);
+
+ if (newCount <= needed) {
+ returned.add(val);
+
+ threshold = 0;
+
+ needed -= newCount;
+ } else {
+ threshold += 1;
+ }
+ }
+
+ if (threshold > MAX_NTRIESPART) {
+ final String fmt = "Heuristic (more than %d iterations of attempting to pad) detected an unpaddable list. (%s)\nPartially padded list: %S";
+
+ final String msg = String.format(fmt, MAX_NTRIESPART, list.toString(), returned.toString());
+
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ return returned;
+ }
+}