You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2023/08/02 20:20:30 UTC
[groovy] 04/04: sort `o` and `p` methods
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 7ed4ccc3bfc23e041811d06c2f9b941b7d5095ba
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Aug 2 15:05:04 2023 -0500
sort `o` and `p` methods
---
.../groovy/runtime/DefaultGroovyMethods.java | 7904 ++++++++++----------
1 file changed, 3971 insertions(+), 3933 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 1b618f470f..d2d3dc34d7 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -220,7 +220,6 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
private static final BigInteger BI_INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE);
private static final BigInteger BI_LONG_MAX = BigInteger.valueOf( Long.MAX_VALUE);
private static final BigInteger BI_LONG_MIN = BigInteger.valueOf( Long.MIN_VALUE);
- private static final NumberAwareComparator<Comparable> COMPARABLE_NUMBER_AWARE_COMPARATOR = new NumberAwareComparator<>();
private static class NumberAwareValueComparator<K, V> implements Comparator<Map.Entry<K, V>> {
private final Comparator<V> delegate = new NumberAwareComparator<>();
@@ -231,6 +230,35 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
}
+ private static final NumberAwareComparator<Comparable> COMPARABLE_NUMBER_AWARE_COMPARATOR = new NumberAwareComparator<>();
+
+ // internal helper method
+ protected static <T> T callClosureForLine(@ClosureParams(value=FromString.class, options={"String","String,Integer"}) Closure<T> closure, String line, int counter) {
+ if (closure.getMaximumNumberOfParameters() == 2) {
+ return closure.call(line, counter);
+ }
+ return closure.call(line);
+ }
+
+ // internal helper method
+ protected static <T, K, V> T callClosureForMapEntry(@ClosureParams(value=FromString.class, options={"K,V","Map.Entry<K,V>"}) Closure<T> closure, Map.Entry<K,V> entry) {
+ if (closure.getMaximumNumberOfParameters() == 2) {
+ return closure.call(entry.getKey(), entry.getValue());
+ }
+ return closure.call(entry);
+ }
+
+ // internal helper method
+ protected static <T, K, V> T callClosureForMapEntryAndCounter(@ClosureParams(value=FromString.class, options={"K,V,Integer", "K,V","Map.Entry<K,V>"}) Closure<T> closure, Map.Entry<K,V> entry, int counter) {
+ if (closure.getMaximumNumberOfParameters() == 3) {
+ return closure.call(entry.getKey(), entry.getValue(), counter);
+ }
+ if (closure.getMaximumNumberOfParameters() == 2) {
+ return closure.call(entry, counter);
+ }
+ return closure.call(entry);
+ }
+
//--------------------------------------------------------------------------
// abs
@@ -9340,5130 +9368,5151 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
//--------------------------------------------------------------------------
+ // or
/**
- * Allows the closure to be called for the object reference self.
- * <p>
- * Any method invoked inside the closure will first be invoked on the
- * self reference. For instance, the following method calls to the append()
- * method are invoked on the StringBuilder instance:
- * <pre class="groovyTestCase">
- * def b = new StringBuilder().with {
- * append('foo')
- * append('bar')
- * return it
- * }
- * assert b.toString() == 'foobar'
- * </pre>
- * This is commonly used to simplify object creation, such as this example:
- * <pre>
- * def p = new Person().with {
- * firstName = 'John'
- * lastName = 'Doe'
- * return it
- * }
- * </pre>
- * The other typical usage, uses the self object while creating some value:
- * <pre>
- * def fullName = person.with{ "$firstName $lastName" }
- * </pre>
+ * Bitwise OR together two numbers.
*
- * @param self the object to have a closure act upon
- * @param closure the closure to call on the object
- * @return result of calling the closure
- * @see #with(Object, boolean, Closure)
- * @see #tap(Object, Closure)
- * @since 1.5.0
+ * @param left a Number
+ * @param right another Number to bitwise OR
+ * @return the bitwise OR of both Numbers
+ * @since 1.0
*/
- @SuppressWarnings("unchecked")
- public static <T,U> T with(
- @DelegatesTo.Target("self") U self,
- @DelegatesTo(value=DelegatesTo.Target.class,
- target="self",
- strategy=Closure.DELEGATE_FIRST)
- @ClosureParams(FirstParam.class)
- Closure<T> closure) {
- return (T) with(self, false, (Closure<Object>)closure);
+ public static Number or(Number left, Number right) {
+ return NumberMath.or(left, right);
}
/**
- * Allows the closure to be called for the object reference self.
- * <p/>
- * Any method invoked inside the closure will first be invoked on the
- * self reference. For example, the following method calls to the append()
- * method are invoked on the StringBuilder instance and then, because
- * 'returning' is true, the self instance is returned:
- * <pre class="groovyTestCase">
- * def b = new StringBuilder().with(true) {
- * append('foo')
- * append('bar')
- * }
- * assert b.toString() == 'foobar'
- * </pre>
- * The returning parameter is commonly set to true when using with to simplify object
- * creation, such as this example:
- * <pre>
- * def p = new Person().with(true) {
- * firstName = 'John'
- * lastName = 'Doe'
- * }
- * </pre>
- * Alternatively, 'tap' is an alias for 'with(true)', so that method can be used instead.
+ * Bitwise OR together two BitSets. Called when the '|' operator is used
+ * between two bit sets.
*
- * The other main use case for with is when returning a value calculated using self as shown here:
- * <pre>
- * def fullName = person.with(false){ "$firstName $lastName" }
- * </pre>
- * Alternatively, 'with' is an alias for 'with(false)', so the boolean parameter can be omitted instead.
+ * @param left a BitSet
+ * @param right another BitSet to bitwise OR
+ * @return the bitwise OR of both BitSets
+ * @since 1.5.0
+ */
+ public static BitSet or(BitSet left, BitSet right) {
+ BitSet result = (BitSet) left.clone();
+ result.or(right);
+ return result;
+ }
+
+ /**
+ * Logical disjunction of two boolean operators
*
- * @param self the object to have a closure act upon
- * @param returning if true, return the self object; otherwise, the result of calling the closure
- * @param closure the closure to call on the object
- * @return the self object or the result of calling the closure depending on 'returning'
- * @see #with(Object, Closure)
- * @see #tap(Object, Closure)
- * @since 2.5.0
+ * @param left left operator
+ * @param right right operator
+ * @return result of logical disjunction
+ * @since 1.0
*/
- public static <T,U extends T, V extends T> T with(
- @DelegatesTo.Target("self") U self,
- boolean returning,
- @DelegatesTo(value=DelegatesTo.Target.class,
- target="self",
- strategy=Closure.DELEGATE_FIRST)
- @ClosureParams(FirstParam.class)
- Closure<T> closure) {
- @SuppressWarnings("unchecked")
- final Closure<V> clonedClosure = (Closure<V>) closure.clone();
- clonedClosure.setResolveStrategy(Closure.DELEGATE_FIRST);
- clonedClosure.setDelegate(self);
- V result = clonedClosure.call(self);
- return returning ? self : result;
+ public static Boolean or(Boolean left, Boolean right) {
+ return left || Boolean.TRUE.equals(right);
}
/**
- * Allows the closure to be called for the object reference self (similar
- * to <code>with</code>) and always returns self.
+ * Create a Set as a union of a Set and an Iterable. Any
+ * elements that exist in either are added to the resultant Set.
* <p>
- * Any method invoked inside the closure will first be invoked on the
- * self reference. For instance, the following method calls to the append()
- * method are invoked on the StringBuilder instance:
- * <pre>
- * def b = new StringBuilder().tap {
- * append('foo')
- * append('bar')
- * }
- * assert b.toString() == 'foobar'
- * </pre>
- * This is commonly used to simplify object creation, such as this example:
- * <pre>
- * def p = new Person().tap {
- * firstName = 'John'
- * lastName = 'Doe'
- * }
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ *
+ * <pre class="groovyTestCase">
+ * def a = [1,2,3,4] as Set
+ * def b = [3,4,5,6] as Set
+ * assert (a | b) == [1,2,3,4,5,6] as Set
* </pre>
*
- * @param self the object to have a closure act upon
- * @param closure the closure to call on the object
- * @return self
- * @see #with(Object, boolean, Closure)
- * @see #with(Object, Closure)
- * @since 2.5.0
+ * @param left the left Set
+ * @param right the right Iterable
+ * @return the merged Set
+ * @since 5.0.0
+ * @see #plus(Set, Iterable)
*/
- @SuppressWarnings("unchecked")
- public static <T,U> U tap(
- @DelegatesTo.Target("self") U self,
- @DelegatesTo(value=DelegatesTo.Target.class,
- target="self",
- strategy=Closure.DELEGATE_FIRST)
- @ClosureParams(FirstParam.class)
- Closure<T> closure) {
- return (U) with(self, true, (Closure<Object>)closure);
+ public static <T> Set<T> or(Set<T> left, Iterable<T> right) {
+ return plus(left, right);
}
/**
- * Allows the subscript operator to be used to set dynamically named property values.
- * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
- * of groovy is neater and more concise but only works with property names which
- * are known at compile time.
+ * Create a SortedSet as a union of a SortedSet and an Iterable. Any
+ * elements that exist in either are added to the resultant SortedSet.
+ * <p>
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self the object to act upon
- * @param property the name of the property to set
- * @param newValue the value to set
- * @since 1.0
+ * <pre class="groovyTestCase">
+ * def a = [1,2,3,4] as SortedSet
+ * def b = [3,4,5,6] as Set
+ * assert (a | b) == [1,2,3,4,5,6] as SortedSet
+ * </pre>
+ *
+ * @param left the left SortedSet
+ * @param right the right Iterable
+ * @return the merged SortedSet
+ * @since 5.0.0
+ * @see #plus(SortedSet, Iterable)
*/
- public static void putAt(Object self, String property, Object newValue) {
- InvokerHelper.setProperty(self, property, newValue);
+ public static <T> SortedSet<T> or(SortedSet<T> left, Iterable<T> right) {
+ return plus(left, right);
}
+ //--------------------------------------------------------------------------
+ // permutations
+
/**
- * Scoped use method
+ * Finds all permutations of an iterable.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">def result = [1, 2, 3].permutations()
+ * assert result == [[3, 2, 1], [3, 1, 2], [1, 3, 2], [2, 3, 1], [2, 1, 3], [1, 2, 3]] as Set</pre>
*
- * @param self any Object
- * @param categoryClass a category class to use
- * @param closure the closure to invoke with the category in place
- * @return the value returned from the closure
- * @since 1.0
+ * @param self the Iterable of items
+ * @return the permutations from the list
+ * @since 1.7.0
*/
- public static <T> T use(Object self, Class categoryClass, Closure<T> closure) {
- return GroovyCategorySupport.use(categoryClass, closure);
+ public static <T> Set<List<T>> permutations(Iterable<T> self) {
+ Set<List<T>> ans = new HashSet<>();
+ PermutationGenerator<T> generator = new PermutationGenerator<>(self);
+ while (generator.hasNext()) {
+ ans.add(generator.next());
+ }
+ return ans;
}
/**
- * Scoped use method with list of categories.
+ * Finds all permutations of an iterable, applies a function to each permutation and collects the result
+ * into a list.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">Set result = [1, 2, 3].permutations { it.collect { v {@code ->} 2*v }}
+ * assert result == [[6, 4, 2], [6, 2, 4], [2, 6, 4], [4, 6, 2], [4, 2, 6], [2, 4, 6]] as Set</pre>
*
- * @param self any Object
- * @param categoryClassList a list of category classes
- * @param closure the closure to invoke with the categories in place
- * @return the value returned from the closure
- * @since 1.0
+ * @param self the Iterable of items
+ * @param function the function to apply on each permutation
+ * @return the list of results of the application of the function on each permutation
+ * @since 2.2.0
*/
- public static <T> T use(Object self, List<Class> categoryClassList, Closure<T> closure) {
- return GroovyCategorySupport.use(categoryClassList, closure);
+ public static <T,V> List<V> permutations(Iterable<T> self, Closure<V> function) {
+ return collect(permutations(self), function);
}
+ //--------------------------------------------------------------------------
+ // plus
+
/**
- * Allows you to use a list of categories, specifying the list as varargs.
- * <code>use(CategoryClass1, CategoryClass2) { ... }</code>
- * This method saves having to wrap the category
- * classes in a list.
+ * Create a Collection as a union of two collections. If the left collection
+ * is a Set, then the returned collection will be a Set otherwise a List.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3,4] == [1,2] + [3,4]</pre>
*
- * @param self any Object
- * @param array a list of category classes and a Closure
- * @return the value returned from the closure
- * @since 1.0
+ * @param left the left Collection
+ * @param right the right Collection
+ * @return the merged Collection
+ * @since 1.5.0
*/
- @SuppressWarnings("unchecked")
- public static Object use(Object self, Object[] array) {
- if (array.length < 2)
- throw new IllegalArgumentException(
- "Expecting at least 2 arguments, a category class and a Closure");
- Closure closure;
- try {
- closure = (Closure) array[array.length - 1];
- } catch (ClassCastException e) {
- throw new IllegalArgumentException("Expecting a Closure to be the last argument");
- }
- List<Class> list = new ArrayList<>(array.length - 1);
- for (int i = 0; i < array.length - 1; ++i) {
- Class categoryClass;
- try {
- categoryClass = (Class) array[i];
- } catch (ClassCastException e) {
- throw new IllegalArgumentException("Expecting a Category Class for argument " + i);
- }
- list.add(categoryClass);
- }
- return GroovyCategorySupport.use(list, closure);
+ public static <T> Collection<T> plus(Collection<T> left, Collection<T> right) {
+ final Collection<T> answer = cloneSimilarCollection(left, left.size() + right.size());
+ answer.addAll(right);
+ return answer;
}
- //--------------------------------------------------------------------------
- // print/println/printf/sprintf
-
/**
- * Print a value formatted Groovy style to self if it
- * is a Writer, otherwise to the standard output stream.
+ * Create a Collection as a union of two iterables. If the left iterable
+ * is a Set, then the returned collection will be a Set otherwise a List.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3,4] == [1,2] + [3,4]</pre>
*
- * @param self any Object
- * @param value the value to print
- * @since 1.0
+ * @param left the left Iterable
+ * @param right the right Iterable
+ * @return the merged Collection
+ * @since 2.4.0
*/
- public static void print(Object self, Object value) {
- // we won't get here if we are a PrintWriter
- if (self instanceof Writer) {
- try {
- ((Writer) self).write(FormatHelper.toString(value));
- } catch (IOException e) {
- // TODO: Should we have some unified function like PrintWriter.checkError()?
- }
- } else {
- System.out.print(FormatHelper.toString(value));
- }
+ public static <T> Collection<T> plus(Iterable<T> left, Iterable<T> right) {
+ return plus(asCollection(left), asCollection(right));
}
/**
- * Print a value formatted Groovy style to the print writer.
+ * Create a Collection as a union of a Collection and an Iterable. If the left collection
+ * is a Set, then the returned collection will be a Set otherwise a List.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self a PrintWriter
- * @param value the value to print
- * @since 1.0
+ * @param left the left Collection
+ * @param right the right Iterable
+ * @return the merged Collection
+ * @since 1.8.7
+ * @see #plus(Collection, Collection)
*/
- public static void print(PrintWriter self, Object value) {
- self.print(FormatHelper.toString(value));
+ public static <T> Collection<T> plus(Collection<T> left, Iterable<T> right) {
+ return plus(left, asCollection(right));
}
/**
- * Print a value formatted Groovy style to the print stream.
+ * Create a List as a union of a List and an Iterable.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self a PrintStream
- * @param value the value to print
- * @since 1.6.0
+ * @param left the left List
+ * @param right the right Iterable
+ * @return the merged List
+ * @since 2.4.0
+ * @see #plus(Collection, Collection)
*/
- public static void print(PrintStream self, Object value) {
- self.print(FormatHelper.toString(value));
+ public static <T> List<T> plus(List<T> left, Iterable<T> right) {
+ return (List<T>) plus((Collection<T>) left, asCollection(right));
}
/**
- * Print a value to the standard output stream.
- * This method delegates to the owner to execute the method.
+ * Create a List as a union of a List and a Collection.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self a generated closure
- * @param value the value to print
- * @since 1.0
+ * @param left the left List
+ * @param right the right Collection
+ * @return the merged List
+ * @since 2.4.0
+ * @see #plus(Collection, Collection)
*/
- public static void print(Closure self, Object value) {
- Object owner = getClosureOwner(self);
- InvokerHelper.invokeMethod(owner, "print", new Object[]{value});
+ public static <T> List<T> plus(List<T> left, Collection<T> right) {
+ return (List<T>) plus((Collection<T>) left, right);
}
/**
- * Print to a console in interactive format.
+ * Create a Set as a union of a Set and an Iterable.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self any Object
- * @param out the PrintWriter used for printing
- * @since 1.0
+ * @param left the left Set
+ * @param right the right Iterable
+ * @return the merged Set
+ * @since 2.4.0
+ * @see #plus(Collection, Collection)
*/
- public static void print(Object self, PrintWriter out) {
- if (out == null) {
- out = new PrintWriter(System.out);
- }
- out.print(FormatHelper.toString(self));
+ public static <T> Set<T> plus(Set<T> left, Iterable<T> right) {
+ return (Set<T>) plus((Collection<T>) left, asCollection(right));
}
/**
- * Print a linebreak to the standard output stream.
+ * Create a Set as a union of a Set and a Collection.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self any Object
- * @since 1.0
+ * @param left the left Set
+ * @param right the right Collection
+ * @return the merged Set
+ * @since 2.4.0
+ * @see #plus(Collection, Collection)
*/
- public static void println(Object self) {
- // we won't get here if we are a PrintWriter
- if (self instanceof Writer) {
- PrintWriter pw = new GroovyPrintWriter((Writer) self);
- pw.println();
- } else {
- System.out.println();
- }
+ public static <T> Set<T> plus(Set<T> left, Collection<T> right) {
+ return (Set<T>) plus((Collection<T>) left, right);
}
/**
- * Print a linebreak to the standard output stream.
- * This method delegates to the owner to execute the method.
+ * Create a SortedSet as a union of a SortedSet and an Iterable.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self a closure
- * @since 1.0
+ * @param left the left SortedSet
+ * @param right the right Iterable
+ * @return the merged SortedSet
+ * @since 2.4.0
+ * @see #plus(Collection, Collection)
*/
- public static void println(Closure self) {
- Object owner = getClosureOwner(self);
- InvokerHelper.invokeMethod(owner, "println", InvokerHelper.EMPTY_ARGS);
+ public static <T> SortedSet<T> plus(SortedSet<T> left, Iterable<T> right) {
+ return (SortedSet<T>) plus((Collection<T>) left, asCollection(right));
}
/**
- * Print a value formatted Groovy style (followed by a newline) to self
- * if it is a Writer, otherwise to the standard output stream.
+ * Create a SortedSet as a union of a SortedSet and a Collection.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self any Object
- * @param value the value to print
- * @since 1.0
+ * @param left the left SortedSet
+ * @param right the right Collection
+ * @return the merged SortedSet
+ * @since 2.4.0
+ * @see #plus(Collection, Collection)
*/
- public static void println(Object self, Object value) {
- // we won't get here if we are a PrintWriter
- if (self instanceof Writer) {
- final PrintWriter pw = new GroovyPrintWriter((Writer) self);
- pw.println(value);
- } else {
- System.out.println(FormatHelper.toString(value));
- }
+ public static <T> SortedSet<T> plus(SortedSet<T> left, Collection<T> right) {
+ return (SortedSet<T>) plus((Collection<T>) left, right);
}
/**
- * Print a value formatted Groovy style (followed by a newline) to the print writer.
+ * Creates a new List by inserting all the elements in the specified array
+ * to the elements from the original List at the specified index.
+ * Shifts the element currently at that index (if any) and any subsequent
+ * elements to the right (increasing their indices).
+ * The new elements will appear in the resulting List in the order that
+ * they occur in the original array.
+ * The behavior of this operation is undefined if the list or
+ * array operands are modified while the operation is in progress.
+ * The original list and array operands remain unchanged.
*
- * @param self a PrintWriter
- * @param value the value to print
- * @since 1.0
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 3]
+ * def newItems = items.plus(2, 'a'..'c' as String[])
+ * assert newItems == [1, 2, 'a', 'b', 'c', 3]
+ * assert items == [1, 2, 3]
+ * </pre>
+ *
+ * See also <code>addAll</code> for similar functionality with modify semantics, i.e. which performs
+ * the changes on the original list itself.
+ *
+ * @param self an original list
+ * @param items array containing elements to be merged with elements from the original list
+ * @param index index at which to insert the first element from the specified array
+ * @return the new list
+ * @see #plus(List, int, List)
+ * @since 1.8.1
*/
- public static void println(PrintWriter self, Object value) {
- self.println(FormatHelper.toString(value));
+ public static <T> List<T> plus(List<T> self, int index, T[] items) {
+ return plus(self, index, Arrays.asList(items));
}
/**
- * Print a value formatted Groovy style (followed by a newline) to the print stream.
- *
- * @param self any Object
- * @param value the value to print
- * @since 1.6.0
- */
- public static void println(PrintStream self, Object value) {
- self.println(FormatHelper.toString(value));
+ * Creates a new List by inserting all the elements in the given additions List
+ * to the elements from the original List at the specified index.
+ * Shifts the element currently at that index (if any) and any subsequent
+ * elements to the right (increasing their indices). The new elements
+ * will appear in the resulting List in the order that they occur in the original lists.
+ * The behavior of this operation is undefined if the original lists
+ * are modified while the operation is in progress. The original lists remain unchanged.
+ *
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 3]
+ * def newItems = items.plus(2, 'a'..'c')
+ * assert newItems == [1, 2, 'a', 'b', 'c', 3]
+ * assert items == [1, 2, 3]
+ * </pre>
+ *
+ * See also <code>addAll</code> for similar functionality with modify semantics, i.e. which performs
+ * the changes on the original list itself.
+ *
+ * @param self an original List
+ * @param additions a List containing elements to be merged with elements from the original List
+ * @param index index at which to insert the first element from the given additions List
+ * @return the new list
+ * @since 1.8.1
+ */
+ public static <T> List<T> plus(List<T> self, int index, List<T> additions) {
+ final List<T> answer = new ArrayList<>(self);
+ answer.addAll(index, additions);
+ return answer;
}
/**
- * Print a value (followed by a newline) to the standard output stream.
- * This method delegates to the owner to execute the method.
+ * Creates a new List by inserting all the elements in the given Iterable
+ * to the elements from this List at the specified index.
*
- * @param self a closure
- * @param value the value to print
- * @since 1.0
+ * @param self an original list
+ * @param additions an Iterable containing elements to be merged with the elements from the original List
+ * @param index index at which to insert the first element from the given additions Iterable
+ * @return the new list
+ * @since 1.8.7
+ * @see #plus(List, int, List)
*/
- public static void println(Closure self, Object value) {
- Object owner = getClosureOwner(self);
- InvokerHelper.invokeMethod(owner, "println", new Object[]{value});
+ public static <T> List<T> plus(List<T> self, int index, Iterable<T> additions) {
+ return plus(self, index, toList(additions));
}
/**
- * Print to a console in interactive format.
+ * Create a collection as a union of a Collection and an Object. If the collection
+ * is a Set, then the returned collection will be a Set otherwise a List.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
*
- * @param self any Object
- * @param out the PrintWriter used for printing
- * @since 1.0
+ * @param left a Collection
+ * @param right an object to add/append
+ * @return the resulting Collection
+ * @since 1.5.0
*/
- public static void println(Object self, PrintWriter out) {
- if (out == null) {
- out = new PrintWriter(System.out);
- }
- out.println(FormatHelper.toString(self));
+ public static <T> Collection<T> plus(Collection<T> left, T right) {
+ final Collection<T> answer = cloneSimilarCollection(left, left.size() + 1);
+ answer.add(right);
+ return answer;
}
/**
- * Printf to the standard output stream.
+ * Create a collection as a union of an Iterable and an Object. If the iterable
+ * is a Set, then the returned collection will be a Set otherwise a List.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
*
- * @param self any Object
- * @param format a format string
- * @param values values referenced by the format specifiers in the format string
- * @since 1.0
+ * @param left an Iterable
+ * @param right an object to add/append
+ * @return the resulting Collection
+ * @since 2.4.0
*/
- public static void printf(Object self, String format, Object[] values) {
- if (self instanceof PrintStream)
- ((PrintStream)self).printf(format, values);
- else
- System.out.printf(format, values);
+ public static <T> Collection<T> plus(Iterable<T> left, T right) {
+ return plus(asCollection(left), right);
}
/**
- * Printf 0 or more values to the standard output stream using a format string.
- * This method delegates to the owner to execute the method.
+ * Create a List as a union of a List and an Object.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
*
- * @param self a generated closure
- * @param format a format string
- * @param values values referenced by the format specifiers in the format string
- * @since 3.0.0
+ * @param left a List
+ * @param right an object to add/append
+ * @return the resulting List
+ * @since 2.4.0
*/
- public static void printf(Closure self, String format, Object[] values) {
- Object owner = getClosureOwner(self);
- Object[] newValues = new Object[values.length + 1];
- newValues[0] = format;
- System.arraycopy(values, 0, newValues, 1, values.length);
- InvokerHelper.invokeMethod(owner, "printf", newValues);
+ public static <T> List<T> plus(List<T> left, T right) {
+ return (List<T>) plus((Collection<T>) left, right);
}
/**
- * Printf a value to the standard output stream using a format string.
- * This method delegates to the owner to execute the method.
+ * Create a Set as a union of a Set and an Object.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
*
- * @param self a generated closure
- * @param format a format string
- * @param value value referenced by the format specifier in the format string
- * @since 3.0.0
+ * @param left a Set
+ * @param right an object to add/append
+ * @return the resulting Set
+ * @since 2.4.0
*/
- public static void printf(Closure self, String format, Object value) {
- Object owner = getClosureOwner(self);
- Object[] newValues = new Object[2];
- newValues[0] = format;
- newValues[1] = value;
- InvokerHelper.invokeMethod(owner, "printf", newValues);
+ public static <T> Set<T> plus(Set<T> left, T right) {
+ return (Set<T>) plus((Collection<T>) left, right);
}
/**
- * Prints a formatted string using the specified format string and arguments.
- * <p>
- * Examples:
- * <pre>
- * printf ( "Hello, %s!\n" , [ "world" ] as String[] )
- * printf ( "Hello, %s!\n" , [ "Groovy" ])
- * printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
- * printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
- *
- * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
- * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
- * ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
- * ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
- * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
- * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
- * ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
- * ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
- * </pre>
+ * Create a SortedSet as a union of a SortedSet and an Object.
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
*
- * @param self any Object
- * @param format A format string
- * @param arg Argument which is referenced by the format specifiers in the format
- * string. The type of <code>arg</code> should be one of Object[], List,
- * int[], short[], byte[], char[], boolean[], long[], float[], or double[].
- * @since 1.0
+ * @param left a SortedSet
+ * @param right an object to add/append
+ * @return the resulting SortedSet
+ * @since 2.4.0
*/
- public static void printf(Object self, String format, Object arg) {
- if (self instanceof PrintStream)
- printf((PrintStream) self, format, arg);
- else if (self instanceof Writer)
- printf((Writer) self, format, arg);
- else
- printf(System.out, format, arg);
+ public static <T> SortedSet<T> plus(SortedSet<T> left, T right) {
+ return (SortedSet<T>) plus((Collection<T>) left, right);
}
- private static void printf(PrintStream self, String format, Object arg) {
- self.print(sprintf(self, format, arg));
+ /**
+ * Returns a new <code>Map</code> containing all entries from <code>left</code> and <code>right</code>,
+ * giving precedence to <code>right</code>. Any keys appearing in both Maps
+ * will appear in the resultant map with values from the <code>right</code>
+ * operand. If the <code>left</code> map is one of TreeMap, LinkedHashMap, Hashtable
+ * or Properties, the returned Map will preserve that type, otherwise a HashMap will
+ * be returned.
+ * <p>
+ * Roughly equivalent to <code>Map m = new HashMap(); m.putAll(left); m.putAll(right); return m;</code>
+ * but with some additional logic to preserve the <code>left</code> Map type for common cases as
+ * described above.
+ * <pre class="groovyTestCase">
+ * assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]
+ * </pre>
+ *
+ * @param left a Map
+ * @param right a Map
+ * @return a new Map containing all entries from left and right
+ * @since 1.5.0
+ */
+ public static <K, V> Map<K, V> plus(Map<K, V> left, Map<K, V> right) {
+ Map<K, V> map = cloneSimilarMap(left);
+ map.putAll(right);
+ return map;
}
- private static void printf(Writer self, String format, Object arg) {
- try {
- self.write(sprintf(self, format, arg));
- } catch (IOException e) {
- printf(System.out, format, arg);
- }
+ /**
+ * Returns a new <code>Map</code> containing all entries from <code>self</code> and <code>entries</code>,
+ * giving precedence to <code>entries</code>. Any keys appearing in both Maps
+ * will appear in the resultant map with values from the <code>entries</code>
+ * operand. If <code>self</code> map is one of TreeMap, LinkedHashMap, Hashtable
+ * or Properties, the returned Map will preserve that type, otherwise a HashMap will
+ * be returned.
+ *
+ * @param self a Map
+ * @param entries a Collection of Map.Entry items to be added to the Map.
+ * @return a new Map containing all key, value pairs from self and entries
+ * @since 1.6.1
+ */
+ public static <K, V> Map<K, V> plus(Map<K, V> self, Collection<? extends Map.Entry<? extends K, ? extends V>> entries) {
+ Map<K, V> map = cloneSimilarMap(self);
+ putAll(map, entries);
+ return map;
}
/**
- * Sprintf to a string.
+ * Add a Character and a Number. The ordinal value of the Character
+ * is used in the addition (the ordinal value is the unicode
+ * value which for simple character sets is the ASCII value).
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self any Object
- * @param format a format string
- * @param values values referenced by the format specifiers in the format string
- * @return the resulting formatted string
- * @since 1.5.0
+ * @see java.lang.Integer#valueOf(int)
+ * @param left a Character
+ * @param right a Number
+ * @return the Number corresponding to the addition of left and right
+ * @since 1.0
*/
- public static String sprintf(Object self, String format, Object[] values) {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- PrintStream out = new PrintStream(outputStream);
- out.printf(format, values);
- return outputStream.toString();
+ public static Number plus(Character left, Number right) {
+ return NumberNumberPlus.plus(Integer.valueOf(left), right);
}
/**
- * Returns a formatted string using the specified format string and
- * arguments.
+ * Add a Number and a Character. The ordinal value of the Character
+ * is used in the addition (the ordinal value is the unicode
+ * value which for simple character sets is the ASCII value).
*
- * @param self any Object
- * @param format A format string
- * @param arg Argument which is referenced by the format specifiers in the format
- * string. The type of <code>arg</code> should be one of Object[], List,
- * int[], short[], byte[], char[], boolean[], long[], float[], or double[].
- * @return the resulting printf'd string
- * @since 1.5.0
- */
- public static String sprintf(Object self, String format, Object arg) {
- if (arg instanceof Object[]) {
- return sprintf(self, format, (Object[]) arg);
- }
- if (arg instanceof List) {
- return sprintf(self, format, ((List) arg).toArray());
- }
- if (!arg.getClass().isArray()) {
- Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
- o[0] = arg;
- return sprintf(self, format, o);
- }
-
- Object[] ans;
- String elemType = arg.getClass().getName();
- switch (elemType) {
- case "[I":
- int[] ia = (int[]) arg;
- ans = new Integer[ia.length];
- for (int i = 0; i < ia.length; i++) {
- ans[i] = ia[i];
- }
- break;
- case "[C":
- char[] ca = (char[]) arg;
- ans = new Character[ca.length];
- for (int i = 0; i < ca.length; i++) {
- ans[i] = ca[i];
- }
- break;
- case "[Z": {
- boolean[] ba = (boolean[]) arg;
- ans = new Boolean[ba.length];
- for (int i = 0; i < ba.length; i++) {
- ans[i] = ba[i];
- }
- break;
- }
- case "[B": {
- byte[] ba = (byte[]) arg;
- ans = new Byte[ba.length];
- for (int i = 0; i < ba.length; i++) {
- ans[i] = ba[i];
- }
- break;
- }
- case "[S":
- short[] sa = (short[]) arg;
- ans = new Short[sa.length];
- for (int i = 0; i < sa.length; i++) {
- ans[i] = sa[i];
- }
- break;
- case "[F":
- float[] fa = (float[]) arg;
- ans = new Float[fa.length];
- for (int i = 0; i < fa.length; i++) {
- ans[i] = fa[i];
- }
- break;
- case "[J":
- long[] la = (long[]) arg;
- ans = new Long[la.length];
- for (int i = 0; i < la.length; i++) {
- ans[i] = la[i];
- }
- break;
- case "[D":
- double[] da = (double[]) arg;
- ans = new Double[da.length];
- for (int i = 0; i < da.length; i++) {
- ans[i] = da[i];
- }
- break;
- default:
- throw new RuntimeException("sprintf(String," + arg + ")");
- }
- return sprintf(self, format, ans);
- }
-
- private static Object getClosureOwner(Closure<?> cls) {
- Object owner = cls.getOwner();
- while (owner instanceof GeneratedClosure) {
- owner = ((Closure<?>) owner).getOwner();
- }
- return owner;
- }
-
- //--------------------------------------------------------------------------
-
- /**
- * Returns an iterator equivalent to this iterator with all duplicated items removed
- * by using Groovy's default number-aware comparator. The original iterator will become
- * exhausted of elements after determining the unique values. A new iterator
- * for the unique values will be returned.
- *
- * @param self an Iterator
- * @return a new Iterator of the unique items from the original iterator
- * @since 1.5.5
+ * @see java.lang.Integer#valueOf(int)
+ * @param left a Number
+ * @param right a Character
+ * @return The Number corresponding to the addition of left and right
+ * @since 1.0
*/
- public static <T> Iterator<T> unique(Iterator<T> self) {
- return uniqueItems(new IteratorIterableAdapter<>(self)).listIterator();
+ public static Number plus(Number left, Character right) {
+ return NumberNumberPlus.plus(left, Integer.valueOf(right));
}
/**
- * Modifies this collection to remove all duplicated items, using Groovy's
- * default number-aware comparator.
- * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre>
+ * Add one Character to another. The ordinal values of the Characters
+ * are used in the addition (the ordinal value is the unicode
+ * value which for simple character sets is the ASCII value).
+ * This operation will always create a new object for the result,
+ * while the operands remain unchanged.
*
- * @param self a collection
- * @return the now modified collection
- * @see #unique(Collection, boolean)
+ * @see #plus(java.lang.Number, java.lang.Character)
+ * @param left a Character
+ * @param right a Character
+ * @return the Number corresponding to the addition of left and right
* @since 1.0
*/
- public static <T> Collection<T> unique(Collection<T> self) {
- return unique(self, true);
+ public static Number plus(Character left, Character right) {
+ return plus(Integer.valueOf(left), right);
}
/**
- * Modifies this List to remove all duplicated items, using Groovy's
- * default number-aware comparator.
- * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre>
+ * Appends a String to the literal of the Map instance.
*
- * @param self a List
- * @return the now modified List
- * @see #unique(Collection, boolean)
- * @since 2.4.0
+ * <pre class="groovyTestCase">
+ * assert '[a:1] is a map' == [a:1] + ' is a map'
+ * </pre>
+ *
+ * @param left a Map
+ * @param right a String
+ * @return the concatenated string
+ * @since 4.0.3
*/
- public static <T> List<T> unique(List<T> self) {
- return (List<T>) unique((Collection<T>) self, true);
+ public static String plus(Map left, String right) {
+ return DefaultGroovyMethods.toString(left) + right;
}
/**
- * Remove all duplicates from a given Collection using Groovy's default number-aware comparator.
- * If mutate is true, it works by modifying the original object (and also returning it).
- * If mutate is false, a new collection is returned leaving the original unchanged.
- * <pre class="groovyTestCase">
- * assert [1,3] == [1,3,3].unique()
- * </pre>
+ * Appends a GString to the literal of the Map instance.
+ *
* <pre class="groovyTestCase">
- * def orig = [1, 3, 2, 3]
- * def uniq = orig.unique(false)
- * assert orig == [1, 3, 2, 3]
- * assert uniq == [1, 3, 2]
+ * assert '[a:1] is a map' == [a:1] + " is ${'a'} map"
* </pre>
*
- * @param self a collection
- * @param mutate false will cause a new list containing unique items from the collection to be created, true will mutate collections in place
- * @return the now modified collection
- * @since 1.8.1
+ * @param left a Map
+ * @param right a GString
+ * @return the concatenated string
+ * @since 4.0.3
*/
- public static <T> Collection<T> unique(Collection<T> self, boolean mutate) {
- List<T> answer = uniqueItems(self);
- if (mutate) {
- self.clear();
- self.addAll(answer);
- }
- return mutate ? self : answer ;
+ public static String plus(Map left, GString right) {
+ return DefaultGroovyMethods.toString(left) + right;
}
- private static <T> List<T> uniqueItems(Iterable<T> self) {
- List<T> answer = new ArrayList<>();
- for (T t : self) {
- boolean duplicated = false;
- for (T t2 : answer) {
- if (coercedEquals(t, t2)) {
- duplicated = true;
- break;
- }
- }
- if (!duplicated)
- answer.add(t);
- }
- return answer;
- }
+ //--------------------------------------------------------------------------
+ // pop
/**
- * Remove all duplicates from a given List using Groovy's default number-aware comparator.
- * If mutate is true, it works by modifying the original object (and also returning it).
- * If mutate is false, a new collection is returned leaving the original unchanged.
- * <pre class="groovyTestCase">
- * assert [1,3] == [1,3,3].unique()
- * </pre>
+ * Removes the initial item from the List.
+ *
* <pre class="groovyTestCase">
- * def orig = [1, 3, 2, 3]
- * def uniq = orig.unique(false)
- * assert orig == [1, 3, 2, 3]
- * assert uniq == [1, 3, 2]
+ * def list = ["a", false, 2]
+ * assert list.pop() == 'a'
+ * assert list == [false, 2]
* </pre>
*
+ * This is similar to pop on a Stack where the first item in the list
+ * represents the top of the stack.
+ *
+ * Note: The behavior of this method changed in Groovy 2.5 to align with Java.
+ * If you need the old behavior use 'removeLast'.
+ *
* @param self a List
- * @param mutate false will cause a new List containing unique items from the List to be created, true will mutate List in place
- * @return the now modified List
- * @since 2.4.0
+ * @return the item removed from the List
+ * @throws NoSuchElementException if the list is empty
+ * @since 1.0
*/
- public static <T> List<T> unique(List<T> self, boolean mutate) {
- return (List<T>) unique((Collection<T>) self, mutate);
+ public static <T> T pop(List<T> self) {
+ if (self.isEmpty()) {
+ throw new NoSuchElementException("Cannot pop() an empty List");
+ }
+ return self.remove(0);
}
+ //--------------------------------------------------------------------------
+ // power
+
/**
- * Returns an iterator equivalent to this iterator but with all duplicated items
- * removed by using a Closure to determine duplicate (equal) items.
- * The original iterator will be fully processed after the call.
- * <p>
- * If the closure takes a single parameter, the argument passed will be each element,
- * and the closure should return a value used for comparison (either using
- * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
- * If the closure takes two parameters, two items from the Iterator
- * will be passed as arguments, and the closure should return an
- * int value (with 0 indicating the items are not unique).
+ * Power of a Number to a certain exponent. Called by the '**' operator.
*
- * @param self an Iterator
- * @param condition a Closure used to determine unique items
- * @return the modified Iterator
- * @since 1.5.5
+ * @param self a Number
+ * @param exponent a Number exponent
+ * @return a Number to the power of a certain exponent
+ * @since 1.0
*/
- public static <T> Iterator<T> unique(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
- Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
- ? new OrderBy<>(condition, true)
- : new ClosureComparator<>(condition);
- return unique(self, comparator);
- }
+ public static Number power(Number self, Number exponent) {
+ double base, exp, answer;
+ base = self.doubleValue();
+ exp = exponent.doubleValue();
+
+ answer = Math.pow(base, exp);
+ if ((double) ((int) answer) == answer) {
+ return (int) answer;
+ } else if ((double) ((long) answer) == answer) {
+ return (long) answer;
+ } else {
+ return answer;
+ }
+ }
/**
- * A convenience method for making a collection unique using a Closure
- * to determine duplicate (equal) items.
- * <p>
- * If the closure takes a single parameter, the
- * argument passed will be each element, and the closure
- * should return a value used for comparison (either using
- * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
- * If the closure takes two parameters, two items from the collection
- * will be passed as arguments, and the closure should return an
- * int value (with 0 indicating the items are not unique).
- * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre>
- * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b {@code ->} a {@code <=>} b }</pre>
+ * Power of a BigDecimal to an integer certain exponent. If the
+ * exponent is positive, call the BigDecimal.pow(int) method to
+ * maintain precision. Called by the '**' operator.
*
- * @param self a Collection
- * @param closure a 1 or 2 arg Closure used to determine unique items
- * @return self without any duplicates
- * @see #unique(Collection, boolean, Closure)
- * @since 1.0
+ * @param self a BigDecimal
+ * @param exponent an Integer exponent
+ * @return a Number to the power of the exponent
*/
- public static <T> Collection<T> unique(Collection<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return unique(self, true, closure);
+ public static Number power(BigDecimal self, Integer exponent) {
+ if (exponent >= 0) {
+ return self.pow(exponent);
+ } else {
+ return power(self, (double) exponent);
+ }
}
/**
- * A convenience method for making a List unique using a Closure
- * to determine duplicate (equal) items.
- * <p>
- * If the closure takes a single parameter, the
- * argument passed will be each element, and the closure
- * should return a value used for comparison (either using
- * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
- * If the closure takes two parameters, two items from the List
- * will be passed as arguments, and the closure should return an
- * int value (with 0 indicating the items are not unique).
- * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre>
- * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b {@code ->} a {@code <=>} b }</pre>
+ * Power of a BigInteger to an integer certain exponent. If the
+ * exponent is positive, call the BigInteger.pow(int) method to
+ * maintain precision. Called by the '**' operator.
*
- * @param self a List
- * @param closure a 1 or 2 arg Closure used to determine unique items
- * @return self without any duplicates
- * @see #unique(Collection, boolean, Closure)
- * @since 2.4.0
+ * @param self a BigInteger
+ * @param exponent an Integer exponent
+ * @return a Number to the power of the exponent
*/
- public static <T> List<T> unique(List<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return (List<T>) unique((Collection<T>) self, true, closure);
+ public static Number power(BigInteger self, Integer exponent) {
+ if (exponent >= 0) {
+ return self.pow(exponent);
+ } else {
+ return power(self, (double) exponent);
+ }
}
/**
- * A convenience method for making a collection unique using a Closure to determine duplicate (equal) items.
- * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned.
- * <p>
- * If the closure takes a single parameter, each element from the Collection will be passed to the closure. The closure
- * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
- * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the collection
- * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
- * <pre class="groovyTestCase">
- * def orig = [1, 3, 4, 5]
- * def uniq = orig.unique(false) { it % 2 }
- * assert orig == [1, 3, 4, 5]
- * assert uniq == [1, 4]
- * </pre>
- * <pre class="groovyTestCase">
- * def orig = [2, 3, 3, 4]
- * def uniq = orig.unique(false) { a, b {@code ->} a {@code <=>} b }
- * assert orig == [2, 3, 3, 4]
- * assert uniq == [2, 3, 4]
- * </pre>
+ * Power of an integer to an integer certain exponent. If the
+ * exponent is positive, convert to a BigInteger and call
+ * BigInteger.pow(int) method to maintain precision. Called by the
+ * '**' operator.
*
- * @param self a Collection
- * @param mutate false will always cause a new list to be created, true will mutate lists in place
- * @param closure a 1 or 2 arg Closure used to determine unique items
- * @return self without any duplicates
- * @since 1.8.1
+ * @param self an Integer
+ * @param exponent an Integer exponent
+ * @return a Number to the power of the exponent
*/
- public static <T> Collection<T> unique(Collection<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- // use a comparator of one item or two
- int params = closure.getMaximumNumberOfParameters();
- if (params == 1) {
- self = unique(self, mutate, new OrderBy<>(closure, true));
+ public static Number power(Integer self, Integer exponent) {
+ if (exponent >= 0) {
+ BigInteger answer = BigInteger.valueOf(self).pow(exponent);
+ if (answer.compareTo(BI_INT_MIN) >= 0 && answer.compareTo(BI_INT_MAX) <= 0) {
+ return answer.intValue();
+ } else {
+ return answer;
+ }
} else {
- self = unique(self, mutate, new ClosureComparator<>(closure));
+ return power(self, (double) exponent);
}
- return self;
}
/**
- * A convenience method for making a List unique using a Closure to determine duplicate (equal) items.
- * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned.
- * <p>
- * If the closure takes a single parameter, each element from the List will be passed to the closure. The closure
- * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
- * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the collection
- * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
- * <pre class="groovyTestCase">
- * def orig = [1, 3, 4, 5]
- * def uniq = orig.unique(false) { it % 2 }
- * assert orig == [1, 3, 4, 5]
- * assert uniq == [1, 4]
- * </pre>
- * <pre class="groovyTestCase">
- * def orig = [2, 3, 3, 4]
- * def uniq = orig.unique(false) { a, b {@code ->} a {@code <=>} b }
- * assert orig == [2, 3, 3, 4]
- * assert uniq == [2, 3, 4]
- * </pre>
+ * Power of a long to an integer certain exponent. If the
+ * exponent is positive, convert to a BigInteger and call
+ * BigInteger.pow(int) method to maintain precision. Called by the
+ * '**' operator.
*
- * @param self a List
- * @param mutate false will always cause a new list to be created, true will mutate lists in place
- * @param closure a 1 or 2 arg Closure used to determine unique items
- * @return self without any duplicates
- * @since 2.4.0
+ * @param self a Long
+ * @param exponent an Integer exponent
+ * @return a Number to the power of the exponent
*/
- public static <T> List<T> unique(List<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return (List<T>) unique((Collection<T>) self, mutate, closure);
+ public static Number power(Long self, Integer exponent) {
+ if (exponent >= 0) {
+ BigInteger answer = BigInteger.valueOf(self).pow(exponent);
+ if (answer.compareTo(BI_LONG_MIN) >= 0 && answer.compareTo(BI_LONG_MAX) <= 0) {
+ return answer.longValue();
+ } else {
+ return answer;
+ }
+ } else {
+ return power(self, (double) exponent);
+ }
}
/**
- * Returns an iterator equivalent to this iterator with all duplicated
- * items removed by using the supplied comparator. The original iterator
- * will be exhausted upon returning.
+ * Power of a BigInteger to a BigInteger certain exponent. Called by the '**' operator.
*
- * @param self an Iterator
- * @param comparator a Comparator
- * @return the modified Iterator
- * @since 1.5.5
+ * @param self a BigInteger
+ * @param exponent a BigInteger exponent
+ * @return a BigInteger to the power of the exponent
+ * @since 2.3.8
*/
- public static <T> Iterator<T> unique(Iterator<T> self, Comparator<? super T> comparator) {
- return uniqueItems(new IteratorIterableAdapter<>(self), comparator).listIterator();
- }
-
- private static final class IteratorIterableAdapter<T> implements Iterable<T> {
- private final Iterator<T> self;
-
- private IteratorIterableAdapter(Iterator<T> self) {
- this.self = self;
- }
-
- @Override
- public Iterator<T> iterator() {
- return self;
+ public static BigInteger power(BigInteger self, BigInteger exponent) {
+ if ((exponent.signum() >= 0) && (exponent.compareTo(BI_INT_MAX) <= 0)) {
+ return self.pow(exponent.intValue());
+ } else {
+ return BigDecimal.valueOf(Math.pow(self.doubleValue(), exponent.doubleValue())).toBigInteger();
}
}
+ //--------------------------------------------------------------------------
+ // previous
+
/**
- * Remove all duplicates from a given Collection.
- * Works on the original object (and also returns it).
- * The order of members in the Collection are compared by the given Comparator.
- * For each duplicate, the first member which is returned
- * by the given Collection's iterator is retained, but all other ones are removed.
- * The given Collection's original order is preserved.
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * class PersonComparator implements Comparator {
- * int compare(Object o1, Object o2) {
- * Person p1 = (Person) o1
- * Person p2 = (Person) o2
- * if (p1.lname != p2.lname)
- * return p1.lname.compareTo(p2.lname)
- * else
- * return p1.fname.compareTo(p2.fname)
- * }
- *
- * boolean equals(Object obj) {
- * return this.equals(obj)
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
- *
- * def list = [a, b, c, d]
- * List list2 = list.unique(new PersonComparator())
- * assert( list2 == list {@code &&} list == [a, b, c] )
- * </pre>
+ * Decrement a Character by one.
*
- * @param self a Collection
- * @param comparator a Comparator
- * @return self the now modified collection without duplicates
- * @see #unique(java.util.Collection, boolean, java.util.Comparator)
- * @since 1.0
+ * @param self a Character
+ * @return a decremented Character
+ * @since 1.5.7
*/
- public static <T> Collection<T> unique(Collection<T> self, Comparator<? super T> comparator) {
- return unique(self, true, comparator) ;
+ public static Character previous(Character self) {
+ return (char) (self - 1);
}
/**
- * Remove all duplicates from a given List.
- * Works on the original object (and also returns it).
- * The order of members in the List are compared by the given Comparator.
- * For each duplicate, the first member which is returned
- * by the given List's iterator is retained, but all other ones are removed.
- * The given List's original order is preserved.
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * class PersonComparator implements Comparator {
- * int compare(Object o1, Object o2) {
- * Person p1 = (Person) o1
- * Person p2 = (Person) o2
- * if (p1.lname != p2.lname)
- * return p1.lname.compareTo(p2.lname)
- * else
- * return p1.fname.compareTo(p2.fname)
- * }
- *
- * boolean equals(Object obj) {
- * return this.equals(obj)
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
- *
- * def list = [a, b, c, d]
- * List list2 = list.unique(new PersonComparator())
- * assert( list2 == list {@code &&} list == [a, b, c] )
- * </pre>
+ * Decrement a Number by one.
*
- * @param self a List
- * @param comparator a Comparator
- * @return self the now modified List without duplicates
- * @see #unique(java.util.Collection, boolean, java.util.Comparator)
- * @since 2.4.0
+ * @param self a Number
+ * @return a decremented Number
+ * @since 1.0
*/
- public static <T> List<T> unique(List<T> self, Comparator<? super T> comparator) {
- return (List<T>) unique((Collection<T>) self, true, comparator);
+ public static Number previous(Number self) {
+ return NumberNumberMinus.minus(self, Integer.valueOf(1));
}
+ //--------------------------------------------------------------------------
+ // print
+
/**
- * Remove all duplicates from a given Collection.
- * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new collection is returned.
- * The order of members in the Collection are compared by the given Comparator.
- * For each duplicate, the first member which is returned
- * by the given Collection's iterator is retained, but all other ones are removed.
- * The given Collection's original order is preserved.
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * class PersonComparator implements Comparator {
- * int compare(Object o1, Object o2) {
- * Person p1 = (Person) o1
- * Person p2 = (Person) o2
- * if (p1.lname != p2.lname)
- * return p1.lname.compareTo(p2.lname)
- * else
- * return p1.fname.compareTo(p2.fname)
- * }
- *
- * boolean equals(Object obj) {
- * return this.equals(obj)
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
- *
- * def list = [a, b, c, d]
- * List list2 = list.unique(false, new PersonComparator())
- * assert( list2 != list {@code &&} list2 == [a, b, c] )
- * </pre>
+ * Print a value formatted Groovy style to self if it
+ * is a Writer, otherwise to the standard output stream.
*
- * @param self a Collection
- * @param mutate false will always cause a new collection to be created, true will mutate collections in place
- * @param comparator a Comparator
- * @return self the collection without duplicates
- * @since 1.8.1
+ * @param self any Object
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T> Collection<T> unique(Collection<T> self, boolean mutate, Comparator<? super T> comparator) {
- List<T> answer = uniqueItems(self, comparator);
- if (mutate) {
- self.clear();
- self.addAll(answer);
- }
- return mutate ? self : answer;
- }
-
- private static <T> List<T> uniqueItems(Iterable<T> self, Comparator<? super T> comparator) {
- List<T> answer = new ArrayList<>();
- for (T t : self) {
- boolean duplicated = false;
- for (T t2 : answer) {
- if (comparator.compare(t, t2) == 0) {
- duplicated = true;
- break;
- }
+ public static void print(Object self, Object value) {
+ // we won't get here if we are a PrintWriter
+ if (self instanceof Writer) {
+ try {
+ ((Writer) self).write(FormatHelper.toString(value));
+ } catch (IOException e) {
+ // TODO: Should we have some unified function like PrintWriter.checkError()?
}
- if (!duplicated)
- answer.add(t);
+ } else {
+ System.out.print(FormatHelper.toString(value));
}
- return answer;
}
/**
- * Remove all duplicates from a given List.
- * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new List is returned.
- * The order of members in the List are compared by the given Comparator.
- * For each duplicate, the first member which is returned
- * by the given List's iterator is retained, but all other ones are removed.
- * The given List's original order is preserved.
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * class PersonComparator implements Comparator {
- * int compare(Object o1, Object o2) {
- * Person p1 = (Person) o1
- * Person p2 = (Person) o2
- * if (p1.lname != p2.lname)
- * return p1.lname.compareTo(p2.lname)
- * else
- * return p1.fname.compareTo(p2.fname)
- * }
- *
- * boolean equals(Object obj) {
- * return this.equals(obj)
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
+ * Print a value formatted Groovy style to the print writer.
*
- * def list = [a, b, c, d]
- * List list2 = list.unique(false, new PersonComparator())
- * assert( list2 != list {@code &&} list2 == [a, b, c] )
- * </pre>
+ * @param self a PrintWriter
+ * @param value the value to print
+ * @since 1.0
+ */
+ public static void print(PrintWriter self, Object value) {
+ self.print(FormatHelper.toString(value));
+ }
+
+ /**
+ * Print a value formatted Groovy style to the print stream.
*
- * @param self a List
- * @param mutate false will always cause a new List to be created, true will mutate List in place
- * @param comparator a Comparator
- * @return self the List without duplicates
- * @since 2.4.0
+ * @param self a PrintStream
+ * @param value the value to print
+ * @since 1.6.0
*/
- public static <T> List<T> unique(List<T> self, boolean mutate, Comparator<? super T> comparator) {
- return (List<T>) unique((Collection<T>) self, mutate, comparator);
+ public static void print(PrintStream self, Object value) {
+ self.print(FormatHelper.toString(value));
}
/**
- * Returns an iterator equivalent to this iterator but with all duplicated items
- * removed where duplicate (equal) items are deduced by calling the supplied Closure condition.
- * <p>
- * If the supplied Closure takes a single parameter, the argument passed will be each element,
- * and the closure should return a value used for comparison (either using
- * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
- * If the closure takes two parameters, two items from the Iterator
- * will be passed as arguments, and the closure should return an
- * int value (with 0 indicating the items are not unique).
- * <pre class="groovyTestCase">
- * def items = "Hello".toList() + [null, null] + "there".toList()
- * def toLower = { it == null ? null : it.toLowerCase() }
- * def noDups = items.iterator().toUnique(toLower).toList()
- * assert noDups == ['H', 'e', 'l', 'o', null, 't', 'r']
- * </pre>
- * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].toUnique { it % 2 }</pre>
- * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].toUnique { a, b {@code ->} a {@code <=>} b }</pre>
+ * Print to a console in interactive format.
*
- * @param self an Iterator
- * @param condition a Closure used to determine unique items
- * @return an Iterator with no duplicate items
- * @since 2.4.0
+ * @param self any Object
+ * @param out the PrintWriter used for printing
+ * @since 1.0
*/
- public static <T> Iterator<T> toUnique(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
- return toUnique(self, condition.getMaximumNumberOfParameters() == 1
- ? new OrderBy<>(condition, true)
- : new ClosureComparator<>(condition));
+ public static void print(Object self, PrintWriter out) {
+ if (out == null) {
+ out = new PrintWriter(System.out);
+ }
+ out.print(FormatHelper.toString(self));
}
- private static final class ToUniqueIterator<E> implements Iterator<E> {
- private final Iterator<E> delegate;
- private final Set<E> seen;
- private boolean exhausted;
- private E next;
+ /**
+ * Print a value to the standard output stream.
+ * This method delegates to the owner to execute the method.
+ *
+ * @param self a generated closure
+ * @param value the value to print
+ * @since 1.0
+ */
+ public static void print(Closure self, Object value) {
+ Object owner = getClosureOwner(self);
+ InvokerHelper.invokeMethod(owner, "print", new Object[]{value});
+ }
- private ToUniqueIterator(Iterator<E> delegate, Comparator<? super E> comparator) {
- this.seen = new TreeSet<>(comparator);
- this.delegate = delegate;
- advance();
+ private static Object getClosureOwner(Closure<?> cl) {
+ Object owner = cl.getOwner();
+ while (owner instanceof GeneratedClosure) {
+ owner = ((Closure<?>)owner).getOwner();
}
+ return owner;
+ }
- @Override
- public boolean hasNext() {
- return !exhausted;
- }
-
- @Override
- public E next() {
- if (exhausted) throw new NoSuchElementException();
- E result = next;
- advance();
- return result;
- }
-
- @Override
- public void remove() {
- if (exhausted) throw new NoSuchElementException();
- delegate.remove();
- }
-
- private void advance() {
- boolean foundNext = false;
- while (!foundNext && !exhausted) {
- exhausted = !delegate.hasNext();
- if (!exhausted) {
- next = delegate.next();
- foundNext = seen.add(next);
- }
- }
- }
- }
+ //--------------------------------------------------------------------------
+ // printf
/**
- * Returns an iterator equivalent to this iterator with all duplicated
- * items removed by using the supplied comparator.
+ * Printf to the standard output stream.
*
- * @param self an Iterator
- * @param comparator a Comparator used to determine unique (equal) items
- * If {@code null}, the Comparable natural ordering of the elements will be used.
- * @return an Iterator with no duplicate items
- * @since 2.4.0
+ * @param self any Object
+ * @param format a format string
+ * @param values values referenced by the format specifiers in the format string
+ * @since 1.0
*/
- public static <T> Iterator<T> toUnique(Iterator<T> self, Comparator<? super T> comparator) {
- return new ToUniqueIterator<>(self, comparator);
+ public static void printf(Object self, String format, Object[] values) {
+ if (self instanceof PrintStream)
+ ((PrintStream)self).printf(format, values);
+ else
+ System.out.printf(format, values);
}
/**
- * Returns an iterator equivalent to this iterator with all duplicated
- * items removed by using the natural ordering of the items.
+ * Printf 0 or more values to the standard output stream using a format string.
+ * This method delegates to the owner to execute the method.
*
- * @param self an Iterator
- * @return an Iterator with no duplicate items
- * @since 2.4.0
+ * @param self a generated closure
+ * @param format a format string
+ * @param values values referenced by the format specifiers in the format string
+ * @since 3.0.0
*/
- public static <T> Iterator<T> toUnique(Iterator<T> self) {
- return toUnique(self, (Comparator<T>) null);
+ public static void printf(Closure self, String format, Object[] values) {
+ Object owner = getClosureOwner(self);
+ Object[] newValues = new Object[values.length + 1];
+ newValues[0] = format;
+ System.arraycopy(values, 0, newValues, 1, values.length);
+ InvokerHelper.invokeMethod(owner, "printf", newValues);
}
/**
- * Returns a Collection containing the items from the Iterable but with duplicates removed.
- * The items in the Iterable are compared by the given Comparator.
- * For each duplicate, the first member which is returned from the
- * Iterable is retained, but all other ones are removed.
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * class PersonComparator implements Comparator {
- * int compare(Object o1, Object o2) {
- * Person p1 = (Person) o1
- * Person p2 = (Person) o2
- * if (p1.lname != p2.lname)
- * return p1.lname.compareTo(p2.lname)
- * else
- * return p1.fname.compareTo(p2.fname)
- * }
- *
- * boolean equals(Object obj) {
- * return this.equals(obj)
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
- *
- * def list = [a, b, c, d]
- * List list2 = list.toUnique(new PersonComparator())
- * assert list2 == [a, b, c] {@code &&} list == [a, b, c, d]
- * </pre>
+ * Printf a value to the standard output stream using a format string.
+ * This method delegates to the owner to execute the method.
*
- * @param self an Iterable
- * @param comparator a Comparator used to determine unique (equal) items
- * If {@code null}, the Comparable natural ordering of the elements will be used.
- * @return the Collection of non-duplicate items
- * @since 2.4.0
+ * @param self a generated closure
+ * @param format a format string
+ * @param value value referenced by the format specifier in the format string
+ * @since 3.0.0
*/
- public static <T> Collection<T> toUnique(Iterable<T> self, Comparator<? super T> comparator) {
- Collection<T> result = createSimilarCollection(self);
- addAll(result, toUnique(self.iterator(), comparator));
- return result;
+ public static void printf(Closure self, String format, Object value) {
+ Object owner = getClosureOwner(self);
+ Object[] newValues = new Object[2];
+ newValues[0] = format;
+ newValues[1] = value;
+ InvokerHelper.invokeMethod(owner, "printf", newValues);
}
/**
- * Returns a List containing the items from the List but with duplicates removed.
- * The items in the List are compared by the given Comparator.
- * For each duplicate, the first member which is returned from the
- * List is retained, but all other ones are removed.
+ * Prints a formatted string using the specified format string and arguments.
* <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * class PersonComparator implements Comparator {
- * int compare(Object o1, Object o2) {
- * Person p1 = (Person) o1
- * Person p2 = (Person) o2
- * if (p1.lname != p2.lname)
- * return p1.lname.compareTo(p2.lname)
- * else
- * return p1.fname.compareTo(p2.fname)
- * }
- *
- * boolean equals(Object obj) {
- * return this.equals(obj)
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
+ * Examples:
+ * <pre>
+ * printf ( "Hello, %s!\n" , [ "world" ] as String[] )
+ * printf ( "Hello, %s!\n" , [ "Groovy" ])
+ * printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
+ * printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
*
- * def list = [a, b, c, d]
- * List list2 = list.toUnique(new PersonComparator())
- * assert list2 == [a, b, c] {@code &&} list == [a, b, c, d]
+ * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
+ * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
+ * ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
+ * ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
+ * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
+ * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
+ * ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
+ * ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
* </pre>
*
- * @param self a List
- * @param comparator a Comparator used to determine unique (equal) items
- * If {@code null}, the Comparable natural ordering of the elements will be used.
- * @return the List of non-duplicate items
- * @since 2.4.0
+ * @param self any Object
+ * @param format A format string
+ * @param arg Argument which is referenced by the format specifiers in the format
+ * string. The type of <code>arg</code> should be one of Object[], List,
+ * int[], short[], byte[], char[], boolean[], long[], float[], or double[].
+ * @since 1.0
*/
- public static <T> List<T> toUnique(List<T> self, Comparator<? super T> comparator) {
- return (List<T>) toUnique((Iterable<T>) self, comparator);
+ public static void printf(Object self, String format, Object arg) {
+ if (self instanceof PrintStream)
+ printf((PrintStream) self, format, arg);
+ else if (self instanceof Writer)
+ printf((Writer) self, format, arg);
+ else
+ printf(System.out, format, arg);
+ }
+
+ private static void printf(PrintStream self, String format, Object arg) {
+ self.print(sprintf(self, format, arg));
}
+ private static void printf(Writer self, String format, Object arg) {
+ try {
+ self.write(sprintf(self, format, arg));
+ } catch (IOException e) {
+ printf(System.out, format, arg);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // println
+
/**
- * Returns a Collection containing the items from the Iterable but with duplicates removed
- * using the natural ordering of the items to determine uniqueness.
- * <p>
- * <pre class="groovyTestCase">
- * String[] letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
- * String[] expected = ['c', 'a', 't', 's', 'h']
- * assert letters.toUnique() == expected
- * </pre>
+ * Print a linebreak to the standard output stream.
*
- * @param self an Iterable
- * @return the Collection of non-duplicate items
- * @since 2.4.0
+ * @param self any Object
+ * @since 1.0
*/
- public static <T> Collection<T> toUnique(Iterable<T> self) {
- return toUnique(self, (Comparator<T>) null);
+ public static void println(Object self) {
+ // we won't get here if we are a PrintWriter
+ if (self instanceof Writer) {
+ PrintWriter pw = new GroovyPrintWriter((Writer) self);
+ pw.println();
+ } else {
+ System.out.println();
+ }
}
/**
- * Returns a List containing the items from the List but with duplicates removed
- * using the natural ordering of the items to determine uniqueness.
- * <p>
- * <pre class="groovyTestCase">
- * def letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
- * def expected = ['c', 'a', 't', 's', 'h']
- * assert letters.toUnique() == expected
- * </pre>
+ * Print a linebreak to the standard output stream.
+ * This method delegates to the owner to execute the method.
*
- * @param self a List
- * @return the List of non-duplicate items
- * @since 2.4.0
+ * @param self a closure
+ * @since 1.0
*/
- public static <T> List<T> toUnique(List<T> self) {
- return toUnique(self, (Comparator<T>) null);
+ public static void println(Closure self) {
+ Object owner = getClosureOwner(self);
+ InvokerHelper.invokeMethod(owner, "println", InvokerHelper.EMPTY_ARGS);
}
/**
- * Returns a Collection containing the items from the Iterable but with duplicates removed.
- * The items in the Iterable are compared by the given Closure condition.
- * For each duplicate, the first member which is returned from the
- * Iterable is retained, but all other ones are removed.
- * <p>
- * If the closure takes a single parameter, each element from the Iterable will be passed to the closure. The closure
- * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
- * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the Iterable
- * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
- *
- * def list = [a, b, c, d]
- * def list2 = list.toUnique{ p1, p2 {@code ->} p1.lname != p2.lname ? p1.lname <=> p2.lname : p1.fname <=> p2.fname }
- * assert( list2 == [a, b, c] {@code &&} list == [a, b, c, d] )
- * def list3 = list.toUnique{ it.toString() }
- * assert( list3 == [a, b, c] {@code &&} list == [a, b, c, d] )
- * </pre>
+ * Print a value formatted Groovy style (followed by a newline) to self
+ * if it is a Writer, otherwise to the standard output stream.
*
- * @param self an Iterable
- * @param condition a Closure used to determine unique items
- * @return a new Collection
- * @see #toUnique(Iterable, Comparator)
- * @since 2.4.0
+ * @param self any Object
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T> Collection<T> toUnique(Iterable<T> self, @ClosureParams(value = FromString.class, options = {"T", "T,T"}) Closure condition) {
- Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
- ? new OrderBy<>(condition, true)
- : new ClosureComparator<>(condition);
- return toUnique(self, comparator);
+ public static void println(Object self, Object value) {
+ // we won't get here if we are a PrintWriter
+ if (self instanceof Writer) {
+ final PrintWriter pw = new GroovyPrintWriter((Writer) self);
+ pw.println(value);
+ } else {
+ System.out.println(FormatHelper.toString(value));
+ }
}
/**
- * Returns a List containing the items from the List but with duplicates removed.
- * The items in the List are compared by the given Closure condition.
- * For each duplicate, the first member which is returned from the
- * Iterable is retained, but all other ones are removed.
- * <p>
- * If the closure takes a single parameter, each element from the Iterable will be passed to the closure. The closure
- * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
- * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the Iterable
- * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
- * <p>
- * <pre class="groovyTestCase">
- * class Person {
- * def fname, lname
- * String toString() {
- * return fname + " " + lname
- * }
- * }
- *
- * Person a = new Person(fname:"John", lname:"Taylor")
- * Person b = new Person(fname:"Clark", lname:"Taylor")
- * Person c = new Person(fname:"Tom", lname:"Cruz")
- * Person d = new Person(fname:"Clark", lname:"Taylor")
- *
- * def list = [a, b, c, d]
- * def list2 = list.toUnique{ p1, p2 {@code ->} p1.lname != p2.lname ? p1.lname <=> p2.lname : p1.fname <=> p2.fname }
- * assert( list2 == [a, b, c] {@code &&} list == [a, b, c, d] )
- * def list3 = list.toUnique{ it.toString() }
- * assert( list3 == [a, b, c] {@code &&} list == [a, b, c, d] )
- * </pre>
+ * Print a value formatted Groovy style (followed by a newline) to the print writer.
*
- * @param self a List
- * @param condition a Closure used to determine unique items
- * @return a new List
- * @see #toUnique(Iterable, Comparator)
- * @since 2.4.0
+ * @param self a PrintWriter
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T> List<T> toUnique(List<T> self, @ClosureParams(value = FromString.class, options = {"T", "T,T"}) Closure condition) {
- return (List<T>) toUnique((Iterable<T>) self, condition);
+ public static void println(PrintWriter self, Object value) {
+ self.println(FormatHelper.toString(value));
}
/**
- * Allows a Map to be iterated through in reverse order using a closure.
+ * Print a value formatted Groovy style (followed by a newline) to the print stream.
*
- * In general, the order in which the map contents are processed
- * cannot be guaranteed. In practise, specialized forms of Map,
- * e.g. a TreeMap will have its contents processed according to the
- * reverse of the natural ordering of the map.
+ * @param self any Object
+ * @param value the value to print
+ * @since 1.6.0
+ */
+ public static void println(PrintStream self, Object value) {
+ self.println(FormatHelper.toString(value));
+ }
+
+ /**
+ * Print a value (followed by a newline) to the standard output stream.
+ * This method delegates to the owner to execute the method.
*
- * @param self the map over which we iterate
- * @param closure the 1 or 2 arg closure applied on each entry of the map
- * @return returns the self parameter
- * @see #each(Map, Closure)
- * @since 1.7.2
+ * @param self a closure
+ * @param value the value to print
+ * @since 1.0
*/
- @SuppressWarnings("unchecked")
- public static <K, V> Map<K, V> reverseEach(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<?> closure) {
- final Iterator<Map.Entry<K, V>> entries = reverse(self.entrySet().iterator());
- while (entries.hasNext()) {
- callClosureForMapEntry(closure, entries.next());
- }
- return self;
+ public static void println(Closure self, Object value) {
+ Object owner = getClosureOwner(self);
+ InvokerHelper.invokeMethod(owner, "println", new Object[]{value});
}
/**
- * Iterate over each element of the list in the reverse order.
- * <pre class="groovyTestCase">def result = []
- * [1,2,3].reverseEach { result << it }
- * assert result == [3,2,1]</pre>
+ * Print to a console in interactive format.
*
- * @param self a List
- * @param closure a closure to which each item is passed.
- * @return the original list
- * @since 1.5.0
+ * @param self any Object
+ * @param out the PrintWriter used for printing
+ * @since 1.0
*/
- public static <T> List<T> reverseEach(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- each(new ReverseListIterator<>(self), closure);
- return self;
+ public static void println(Object self, PrintWriter out) {
+ if (out == null) {
+ out = new PrintWriter(System.out);
+ }
+ out.println(FormatHelper.toString(self));
}
+ //--------------------------------------------------------------------------
+ // push
+
/**
- * Iterate over each element of the set in reverse order.
+ * Prepends an item to the start of the List.
+ *
* <pre class="groovyTestCase">
- * TreeSet navSet = [2, 4, 1, 3] // natural order is sorted
- * List result = []
- * navSet.reverseEach { result << it }
- * assert result == [4, 3, 2, 1]
+ * def list = [3, 4, 2]
+ * list.push("x")
+ * assert list == ['x', 3, 4, 2]
* </pre>
*
- * @param self a NavigableSet
- * @param closure a closure to which each item is passed.
- * @return the original NavigableSet
- * @since 4.0.11
+ * This is similar to push on a Stack where the first item in the list
+ * represents the top of the stack.
+ *
+ * Note: The behavior of this method changed in Groovy 2.5 to align with Java.
+ * If you need the old behavior use 'add'.
+ *
+ * @param self a List
+ * @param value element to be prepended to this list.
+ * @return <tt>true</tt> (for legacy compatibility reasons).
+ * @since 1.5.5
*/
- public static <T> NavigableSet<T> reverseEach(NavigableSet<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- each(self.descendingIterator(), closure);
- return self;
+ public static <T> boolean push(List<T> self, T value) {
+ self.add(0, value);
+ return true;
}
+ //--------------------------------------------------------------------------
+ // putAll
+
/**
- * Convert an iterator to a List. The iterator will become
- * exhausted of elements after making this conversion.
+ * Provides an easy way to append multiple Map.Entry values to a Map.
*
- * @param self an iterator
- * @return a List
- * @since 1.5.0
+ * @param self a Map
+ * @param entries a Collection of Map.Entry items to be added to the Map.
+ * @return the same map, after the items have been added to it.
+ * @since 1.6.1
*/
- public static <T> List<T> toList(Iterator<T> self) {
-
- List<T> answer = new ArrayList<>();
- while (self.hasNext()) {
- answer.add(self.next());
+ public static <K, V> Map<K, V> putAll(Map<K, V> self, Collection<? extends Map.Entry<? extends K, ? extends V>> entries) {
+ for (Map.Entry<? extends K, ? extends V> entry : entries) {
+ self.put(entry.getKey(), entry.getValue());
}
- return answer;
+ return self;
}
+ //--------------------------------------------------------------------------
+ // putAt
+
/**
- * Convert an Iterable to a List. The Iterable's iterator will
- * become exhausted of elements after making this conversion.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">def x = [1,2,3] as HashSet
- * assert x.class == HashSet
- * assert x.toList() instanceof List</pre>
+ * Allows the subscript operator to be used to set dynamically named property values.
+ * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
+ * of groovy is neater and more concise but only works with property names which
+ * are known at compile time.
*
- * @param self an Iterable
- * @return a List
- * @since 1.8.7
+ * @param self the object to act upon
+ * @param property the name of the property to set
+ * @param newValue the value to set
+ * @since 1.0
*/
- public static <T> List<T> toList(Iterable<T> self) {
- return toList(self.iterator());
+ public static void putAt(Object self, String property, Object newValue) {
+ InvokerHelper.setProperty(self, property, newValue);
}
/**
- * Convert an enumeration to a List.
+ * A helper method to allow lists to work with subscript operators.
+ * <pre class="groovyTestCase">def list = [2, 3]
+ * list[0] = 1
+ * assert list == [1, 3]</pre>
*
- * @param self an enumeration
- * @return a List
- * @since 1.5.0
+ * @param self a List
+ * @param idx an index
+ * @param value the value to put at the given index
+ * @since 1.0
*/
- public static <T> List<T> toList(Enumeration<T> self) {
- List<T> answer = new ArrayList<>();
- while (self.hasMoreElements()) {
- answer.add(self.nextElement());
+ public static <T> void putAt(List<T> self, int idx, T value) {
+ int size = self.size();
+ idx = normaliseIndex(idx, size);
+ if (idx < size) {
+ self.set(idx, value);
+ } else {
+ while (size < idx) {
+ self.add(size++, null);
+ }
+ self.add(idx, value);
}
- return answer;
}
/**
- * A variant of withCollectedValues for Iterators.
- *
- * @param keys an Iterator
- * @param collector the Map into which the transformed entries are put
- * @param valueTransform a function for transforming Iterator elements into values
- * @return the collector with all transformed values added to it
- * @see #withCollectedValues(Iterable, Map, Function)
- * @since 5.0.0
+ * Support subscript operator for list modification.
*/
- public static <K, V> Map<K, V> withCollectedValues(Iterator<K> keys, Map<K, V> collector, Function<? super K, V> valueTransform) {
- return collectEntries(keys, collector, Function.identity(), valueTransform);
- }
-
- /**
- * A variant of withCollectedValues for Iterators.
- *
- * @param keys an Iterator
- * @param valueTransform a function for transforming Iterator elements into values
- * @return a Map of the transformed entries
- * @see #withCollectedValues(Iterable, Function)
- * @since 5.0.0
- */
- public static <K, V> Map<K, V> withCollectedValues(Iterator<K> keys, Function<? super K, V> valueTransform) {
- return withCollectedValues(keys, new LinkedHashMap<>(), valueTransform);
+ public static <T> void putAt(List<T> self, Number idx, T value) {
+ putAt(self, idx.intValue(), value);
}
/**
- * Transform Iterable elements into Map entries with keys unchanged
- * and values transformed using the supplied function.
- * The supplied collector map is used as the destination for transformed entries.
+ * A helper method to allow lists to work with subscript operators.
* <pre class="groovyTestCase">
- * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
- * assert languages.withCollectedValues([Clojure:7], String::size) ==
- * [Clojure:7, Groovy:6, Java:4, Kotlin:6, Scala:5]
+ * def list = ["a", true]
+ * {@code list[1..<1] = 5}
+ * assert list == ["a", 5, true]
* </pre>
- * This method is a convenience method for {@link #collectEntries(Iterable, Map, Function, Function)}
- * with the {@code keyTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
*
- * @param keys an Iterable
- * @param collector the Map into which the transformed entries are put
- * @param valueTransform a function for transforming Iterable elements into values
- * @return the collector with all transformed values added to it
- * @since 5.0.0
+ * @param self a List
+ * @param range the (in this case empty) subset of the list to set
+ * @param value the values to put at the given sublist or a Collection of values
+ * @since 1.0
*/
- public static <K, V> Map<K, V> withCollectedValues(Iterable<K> keys, Map<K, V> collector, Function<? super K, V> valueTransform) {
- return collectEntries(keys.iterator(), collector, Function.identity(), valueTransform);
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, EmptyRange range, Object value) {
+ RangeInfo info = subListBorders(self.size(), range);
+ List sublist = self.subList(info.from, info.to);
+ sublist.clear();
+ if (value instanceof Collection) {
+ Collection col = (Collection) value;
+ if (col.isEmpty()) return;
+ sublist.addAll(col);
+ } else {
+ sublist.add(value);
+ }
}
/**
- * Transform Iterable elements into Map entries with keys unchanged
- * and values transformed using the supplied function.
+ * A helper method to allow lists to work with subscript operators.
* <pre class="groovyTestCase">
- * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
- * assert languages.withCollectedValues(String::size) ==
- * [Groovy:6, Java:4, Kotlin:6, Scala:5]
+ * def list = ["a", true]
+ * {@code list[1..<1] = [4, 3, 2]}
+ * assert list == ["a", 4, 3, 2, true]
* </pre>
- * This method is a convenience method for {@link #collectEntries(Iterable, Function, Function)}
- * with the {@code keyTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
*
- * @param keys an Iterable
- * @param valueTransform a function for transforming Iterable elements into values
- * @return a Map of the transformed entries
- * @since 5.0.0
+ * @param self a List
+ * @param range the (in this case empty) subset of the list to set
+ * @param value the Collection of values
+ * @since 1.0
+ * @see #putAt(java.util.List, groovy.lang.EmptyRange, java.lang.Object)
*/
- public static <K, V> Map<K, V> withCollectedValues(Iterable<K> keys, Function<? super K, V> valueTransform) {
- return withCollectedValues(keys.iterator(), new LinkedHashMap<>(), valueTransform);
+ public static void putAt(List self, EmptyRange range, Collection value) {
+ putAt(self, range, (Object)value);
}
- /**
- * A variant of withCollectedKeys for Iterators.
- *
- * @param values an Iterator
- * @param collector the Map into which the transformed entries are put
- * @param keyTransform a function for transforming Iterator elements into values
- * @return the collector with all transformed values added to it
- * @see #withCollectedKeys(Iterable, Map, Function)
- * @since 5.0.0
- */
- public static <K, V> Map<K, V> withCollectedKeys(Iterator<V> values, Map<K, V> collector, Function<? super V, K> keyTransform) {
- return collectEntries(values, collector, keyTransform, Function.identity());
+ private static <T> List<T> resizeListWithRangeAndGetSublist(List<T> self, IntRange range) {
+ RangeInfo info = subListBorders(self.size(), range);
+ int size = self.size();
+ if (info.to >= size) {
+ while (size < info.to) {
+ self.add(size++, null);
+ }
+ }
+ List<T> sublist = self.subList(info.from, info.to);
+ sublist.clear();
+ return sublist;
}
/**
- * A variant of withCollectedKeys for Iterators.
+ * List subscript assignment operator when given a range as the index and
+ * the assignment operand is a collection.
+ * Example: <pre class="groovyTestCase">def myList = [4, 3, 5, 1, 2, 8, 10]
+ * myList[3..5] = ["a", true]
+ * assert myList == [4, 3, 5, "a", true, 10]</pre>
*
- * @param values an Iterator
- * @param keyTransform a function for transforming Iterator elements into values
- * @return a Map of the transformed entries
- * @see #withCollectedKeys(Iterable, Function)
- * @since 5.0.0
+ * Items in the given
+ * range are replaced with items from the collection.
+ *
+ * @param self a List
+ * @param range the subset of the list to set
+ * @param col the collection of values to put at the given sublist
+ * @since 1.5.0
*/
- public static <K, V> Map<K, V> withCollectedKeys(Iterator<V> values, Function<? super V, K> keyTransform) {
- return withCollectedKeys(values, new LinkedHashMap<>(), keyTransform);
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, IntRange range, Collection col) {
+ List sublist = resizeListWithRangeAndGetSublist(self, range);
+ if (col.isEmpty()) return;
+ sublist.addAll(col);
}
/**
- * Transform Iterable elements into Map entries with values unchanged
- * and keys transformed using the supplied function.
- * The supplied map is used as the destination for transformed entries.
- * <pre class="groovyTestCase">
- * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
- * def firstLetter = s {@code ->} s[0]
- * assert languages.withCollectedKeys([C:'Clojure'], firstLetter) ==
- * [C:'Clojure', G:'Groovy', J:'Java', K:'Kotlin', S:'Scala']
- * </pre>
- * This method is a convenience method for {@link #collectEntries(Iterable, Map, Function, Function)}
- * with the {@code valueTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
+ * List subscript assignment operator when given a range as the index.
+ * Example: <pre class="groovyTestCase">def myList = [4, 3, 5, 1, 2, 8, 10]
+ * myList[3..5] = "b"
+ * assert myList == [4, 3, 5, "b", 10]</pre>
*
- * @param values an Iterable that will become the added entry values
- * @param collector the Map into which the transformed entries are put
- * @param keyTransform a function for transforming Iterator elements into keys
- * @return the collector with all transformed values added to it
- * @since 5.0.0
+ * Items in the given
+ * range are replaced with the operand. The <code>value</code> operand is
+ * always treated as a single value.
+ *
+ * @param self a List
+ * @param range the subset of the list to set
+ * @param value the value to put at the given sublist
+ * @since 1.0
*/
- public static <K, V> Map<K, V> withCollectedKeys(Iterable<V> values, Map<K, V> collector, Function<? super V, K> keyTransform) {
- return collectEntries(values.iterator(), collector, keyTransform, Function.identity());
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, IntRange range, Object value) {
+ List sublist = resizeListWithRangeAndGetSublist(self, range);
+ sublist.add(value);
}
/**
- * Transform Iterable elements into Map entries with values unchanged
- * and keys transformed using the supplied function.
- * <pre class="groovyTestCase">
- * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
- * def firstLetter = s {@code ->} s[0]
- * assert languages.withCollectedKeys(firstLetter) ==
- * [G:'Groovy', J:'Java', K:'Kotlin', S:'Scala']
- * </pre>
- * This method is a convenience method for {@link #collectEntries(Iterable, Function, Function)}
- * with the {@code valueTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
+ * A helper method to allow lists to work with subscript operators.
+ * <pre class="groovyTestCase">def list = ["a", true, 42, 9.4]
+ * list[1, 4] = ["x", false]
+ * assert list == ["a", "x", 42, 9.4, false]</pre>
*
- * @param values an Iterable that will become the added entry values
- * @param keyTransform a function for transforming Iterator elements into keys
- * @return the collector with all transformed values added to it
- * @since 5.0.0
+ * @param self a List
+ * @param splice the subset of the list to set
+ * @param values the value to put at the given sublist
+ * @since 1.0
*/
- public static <K, V> Map<K, V> withCollectedKeys(Iterable<V> values, Function<? super V, K> keyTransform) {
- return withCollectedKeys(values.iterator(), new LinkedHashMap<>(), keyTransform);
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, List splice, Collection values) {
+ if (splice.isEmpty()) {
+ if (!values.isEmpty())
+ throw new IllegalArgumentException("Trying to replace 0 elements with " + values.size() + " elements");
+ return;
+ }
+ Object first = splice.iterator().next();
+ if (first instanceof Integer) {
+ if (values.size() != splice.size())
+ throw new IllegalArgumentException("Trying to replace " + splice.size() + " elements with " + values.size() + " elements");
+ Iterator<?> valuesIter = values.iterator();
+ for (Object index : splice) {
+ putAt(self, (Integer) index, valuesIter.next());
+ }
+ } else {
+ throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of " + first.getClass().getName());
+ }
}
/**
- * Modifies this collection by removing its elements that are contained
- * within the specified object array.
- *
- * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list
- * containing items which don't match some criteria while leaving the original collection unchanged.
+ * A helper method to allow lists to work with subscript operators.
+ * <pre class="groovyTestCase">def list = ["a", true, 42, 9.4]
+ * list[1, 3] = 5
+ * assert list == ["a", 5, 42, 5]</pre>
*
- * @param self a Collection to be modified
- * @param items array containing elements to be removed from this collection
- * @return <tt>true</tt> if this collection changed as a result of the call
- * @see Collection#removeAll(Collection)
- * @since 1.7.2
+ * @param self a List
+ * @param splice the subset of the list to set
+ * @param value the value to put at the given sublist
+ * @since 1.0
*/
@SuppressWarnings("unchecked")
- public static boolean removeAll(Collection self, Object[] items) {
- Collection pickFrom = new TreeSet(new NumberAwareComparator());
- pickFrom.addAll(Arrays.asList(items));
- return self.removeAll(pickFrom);
+ public static void putAt(List self, List splice, Object value) {
+ if (splice.isEmpty()) {
+ return;
+ }
+ Object first = splice.get(0);
+ if (first instanceof Integer) {
+ for (Object index : splice) {
+ self.set((Integer) index, value);
+ }
+ } else {
+ throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName());
+ }
}
/**
- * Modifies this collection so that it retains only its elements that are contained
- * in the specified array. In other words, removes from this collection all of
- * its elements that are not contained in the specified array.
- *
- * See also <code>grep</code> and <code>findAll</code> when wanting to produce a new list
- * containing items which match some specified items but leaving the original collection unchanged.
+ * A helper method to allow maps to work with subscript operators
*
- * @param self a Collection to be modified
- * @param items array containing elements to be retained from this collection
- * @return <tt>true</tt> if this collection changed as a result of the call
- * @see Collection#retainAll(Collection)
- * @since 1.7.2
+ * @param self a Map
+ * @param key an Object as a key for the map
+ * @param value the value to put into the map
+ * @return the value corresponding to the given key
+ * @since 1.0
*/
- @SuppressWarnings("unchecked")
- public static boolean retainAll(Collection self, Object[] items) {
- Collection pickFrom = new TreeSet(new NumberAwareComparator());
- pickFrom.addAll(Arrays.asList(items));
- return self.retainAll(pickFrom);
+ public static <K,V> V putAt(Map<K,V> self, K key, V value) {
+ self.put(key, value);
+ return value;
}
/**
- * Modifies this collection so that it retains only its elements
- * that are matched according to the specified closure condition. In other words,
- * removes from this collection all of its elements that don't match.
- *
- * <pre class="groovyTestCase">def list = ['a', 'b']
- * list.retainAll { it == 'b' }
- * assert list == ['b']</pre>
- *
- * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list
- * containing items which match some criteria but leaving the original collection unchanged.
+ * Support assigning a range of values with a single assignment statement.
*
- * @param self a Collection to be modified
- * @param condition a closure condition
- * @return <tt>true</tt> if this collection changed as a result of the call
- * @see Iterator#remove()
- * @since 1.7.2
+ * @param self a BitSet
+ * @param range the range of values to set
+ * @param value value
+ * @see java.util.BitSet
+ * @see groovy.lang.Range
+ * @since 1.5.0
*/
- public static <T> boolean retainAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- Iterator iter = InvokerHelper.asIterator(self);
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
- boolean result = false;
- while (iter.hasNext()) {
- Object value = iter.next();
- if (!bcw.call(value)) {
- iter.remove();
- result = true;
- }
- }
- return result;
+ public static void putAt(BitSet self, IntRange range, boolean value) {
+ RangeInfo info = subListBorders(self.length(), range);
+ self.set(info.from, info.to, value);
}
/**
- * Modifies this map so that it retains only its elements that are matched
- * according to the specified closure condition. In other words, removes from
- * this map all of its elements that don't match. If the closure takes one
- * parameter then it will be passed the <code>Map.Entry</code>. Otherwise the closure should
- * take two parameters, which will be the key and the value.
- *
- * <pre class="groovyTestCase">def map = [a:1, b:2]
- * map.retainAll { k,v {@code ->} k == 'b' }
- * assert map == [b:2]</pre>
- *
- * See also <code>findAll</code> when wanting to produce a new map containing items
- * which match some criteria but leaving the original map unchanged.
+ * Support subscript-style assignment for a BitSet.
*
- * @param self a Map to be modified
- * @param condition a 1 or 2 arg Closure condition applying on the entries
- * @return <tt>true</tt> if this map changed as a result of the call
- * @since 2.5.0
+ * @param self a BitSet
+ * @param index index of the entry to set
+ * @param value value
+ * @see java.util.BitSet
+ * @since 1.5.0
*/
- public static <K, V> boolean retainAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
- Iterator<Map.Entry<K, V>> iter = self.entrySet().iterator();
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
- boolean result = false;
- while (iter.hasNext()) {
- Map.Entry<K, V> entry = iter.next();
- if (!bcw.callForMap(entry)) {
- iter.remove();
- result = true;
- }
- }
- return result;
+ public static void putAt(BitSet self, int index, boolean value) {
+ self.set(index, value);
}
+ //--------------------------------------------------------------------------
+
/**
- * Modifies this collection by removing the elements that are matched according
- * to the specified closure condition.
- *
- * <pre class="groovyTestCase">def list = ['a', 'b']
- * list.removeAll { it == 'b' }
- * assert list == ['a']</pre>
- *
- * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list
- * containing items which match some criteria but leaving the original collection unchanged.
+ * Allows the closure to be called for the object reference self.
+ * <p>
+ * Any method invoked inside the closure will first be invoked on the
+ * self reference. For instance, the following method calls to the append()
+ * method are invoked on the StringBuilder instance:
+ * <pre class="groovyTestCase">
+ * def b = new StringBuilder().with {
+ * append('foo')
+ * append('bar')
+ * return it
+ * }
+ * assert b.toString() == 'foobar'
+ * </pre>
+ * This is commonly used to simplify object creation, such as this example:
+ * <pre>
+ * def p = new Person().with {
+ * firstName = 'John'
+ * lastName = 'Doe'
+ * return it
+ * }
+ * </pre>
+ * The other typical usage, uses the self object while creating some value:
+ * <pre>
+ * def fullName = person.with{ "$firstName $lastName" }
+ * </pre>
*
- * @param self a Collection to be modified
- * @param condition a closure condition
- * @return <tt>true</tt> if this collection changed as a result of the call
- * @see Iterator#remove()
- * @since 1.7.2
+ * @param self the object to have a closure act upon
+ * @param closure the closure to call on the object
+ * @return result of calling the closure
+ * @see #with(Object, boolean, Closure)
+ * @see #tap(Object, Closure)
+ * @since 1.5.0
*/
- public static <T> boolean removeAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- return self.removeIf(new BooleanClosurePredicate<>(condition));
+ @SuppressWarnings("unchecked")
+ public static <T,U> T with(
+ @DelegatesTo.Target("self") U self,
+ @DelegatesTo(value=DelegatesTo.Target.class,
+ target="self",
+ strategy=Closure.DELEGATE_FIRST)
+ @ClosureParams(FirstParam.class)
+ Closure<T> closure) {
+ return (T) with(self, false, (Closure<Object>)closure);
}
/**
- * Modifies this map by removing the elements that are matched according to the
- * specified closure condition. If the closure takes one parameter then it will be
- * passed the <code>Map.Entry</code>. Otherwise the closure should take two parameters, which
- * will be the key and the value.
- *
- * <pre class="groovyTestCase">def map = [a:1, b:2]
- * map.removeAll { k,v {@code ->} k == 'b' }
- * assert map == [a:1]</pre>
+ * Allows the closure to be called for the object reference self.
+ * <p/>
+ * Any method invoked inside the closure will first be invoked on the
+ * self reference. For example, the following method calls to the append()
+ * method are invoked on the StringBuilder instance and then, because
+ * 'returning' is true, the self instance is returned:
+ * <pre class="groovyTestCase">
+ * def b = new StringBuilder().with(true) {
+ * append('foo')
+ * append('bar')
+ * }
+ * assert b.toString() == 'foobar'
+ * </pre>
+ * The returning parameter is commonly set to true when using with to simplify object
+ * creation, such as this example:
+ * <pre>
+ * def p = new Person().with(true) {
+ * firstName = 'John'
+ * lastName = 'Doe'
+ * }
+ * </pre>
+ * Alternatively, 'tap' is an alias for 'with(true)', so that method can be used instead.
*
- * See also <code>findAll</code> when wanting to produce a new map containing items
- * which match some criteria but leaving the original map unchanged.
+ * The other main use case for with is when returning a value calculated using self as shown here:
+ * <pre>
+ * def fullName = person.with(false){ "$firstName $lastName" }
+ * </pre>
+ * Alternatively, 'with' is an alias for 'with(false)', so the boolean parameter can be omitted instead.
*
- * @param self a Map to be modified
- * @param condition a 1 or 2 arg Closure condition applying on the entries
- * @return <tt>true</tt> if this map changed as a result of the call
+ * @param self the object to have a closure act upon
+ * @param returning if true, return the self object; otherwise, the result of calling the closure
+ * @param closure the closure to call on the object
+ * @return the self object or the result of calling the closure depending on 'returning'
+ * @see #with(Object, Closure)
+ * @see #tap(Object, Closure)
* @since 2.5.0
*/
- public static <K, V> boolean removeAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
- return self.entrySet().removeIf(new BooleanClosureForMapPredicate<>(condition));
+ public static <T,U extends T, V extends T> T with(
+ @DelegatesTo.Target("self") U self,
+ boolean returning,
+ @DelegatesTo(value=DelegatesTo.Target.class,
+ target="self",
+ strategy=Closure.DELEGATE_FIRST)
+ @ClosureParams(FirstParam.class)
+ Closure<T> closure) {
+ @SuppressWarnings("unchecked")
+ final Closure<V> clonedClosure = (Closure<V>) closure.clone();
+ clonedClosure.setResolveStrategy(Closure.DELEGATE_FIRST);
+ clonedClosure.setDelegate(self);
+ V result = clonedClosure.call(self);
+ return returning ? self : result;
}
/**
- * Splits all items into two lists based on the closure condition.
- * The first list contains all items matching the closure expression.
- * The second list all those that don't.
+ * Scoped use method
*
- * @param self an Object with an Iterator returning its values
- * @param closure a closure condition
- * @return a List whose first item is the accepted values and whose second item is the rejected values
- * @since 1.6.0
+ * @param self any Object
+ * @param categoryClass a category class to use
+ * @param closure the closure to invoke with the category in place
+ * @return the value returned from the closure
+ * @since 1.0
*/
- @SuppressWarnings("unchecked")
- public static Collection split(Object self, Closure closure) {
- List accept = new ArrayList();
- List reject = new ArrayList();
- return split(closure, accept, reject, InvokerHelper.asIterator(self));
+ public static <T> T use(Object self, Class categoryClass, Closure<T> closure) {
+ return GroovyCategorySupport.use(categoryClass, closure);
}
/**
- * Splits all items into two collections based on the closure condition.
- * The first list contains all items which match the closure expression.
- * The second list all those that don't.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">assert [[2,4],[1,3]] == [1,2,3,4].split { it % 2 == 0 }</pre>
+ * Scoped use method with list of categories.
*
- * @param self a Collection of values
- * @param closure a closure condition
- * @return a List whose first item is the accepted values and whose second item is the rejected values
- * @since 1.6.0
+ * @param self any Object
+ * @param categoryClassList a list of category classes
+ * @param closure the closure to invoke with the categories in place
+ * @return the value returned from the closure
+ * @since 1.0
*/
- public static <T> Collection<Collection<T>> split(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- Collection<T> accept = createSimilarCollection(self);
- Collection<T> reject = createSimilarCollection(self);
- Iterator<T> iter = self.iterator();
- return split(closure, accept, reject, iter);
- }
-
- static <T> Collection<Collection<T>> split(Closure closure, Collection<T> accept, Collection<T> reject, Iterator<T> iter) {
- List<Collection<T>> answer = new ArrayList<>();
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- while (iter.hasNext()) {
- T value = iter.next();
- if (bcw.call(value)) {
- accept.add(value);
- } else {
- reject.add(value);
- }
- }
- answer.add(accept);
- answer.add(reject);
- return answer;
+ public static <T> T use(Object self, List<Class> categoryClassList, Closure<T> closure) {
+ return GroovyCategorySupport.use(categoryClassList, closure);
}
/**
- * Splits all items into two collections based on the closure condition.
- * The first list contains all items which match the closure expression.
- * The second list all those that don't.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">assert [[2,4],[1,3]] == [1,2,3,4].split { it % 2 == 0 }</pre>
+ * Allows you to use a list of categories, specifying the list as varargs.
+ * <code>use(CategoryClass1, CategoryClass2) { ... }</code>
+ * This method saves having to wrap the category
+ * classes in a list.
*
- * @param self a List of values
- * @param closure a closure condition
- * @return a List whose first item is the accepted values and whose second item is the rejected values
- * @since 2.4.0
+ * @param self any Object
+ * @param array a list of category classes and a Closure
+ * @return the value returned from the closure
+ * @since 1.0
*/
@SuppressWarnings("unchecked")
- public static <T> List<List<T>> split(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return (List<List<T>>) (List<?>) split((Collection<T>) self, closure);
+ public static Object use(Object self, Object[] array) {
+ if (array.length < 2)
+ throw new IllegalArgumentException(
+ "Expecting at least 2 arguments, a category class and a Closure");
+ Closure closure;
+ try {
+ closure = (Closure) array[array.length - 1];
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Expecting a Closure to be the last argument");
+ }
+ List<Class> list = new ArrayList<>(array.length - 1);
+ for (int i = 0; i < array.length - 1; ++i) {
+ Class categoryClass;
+ try {
+ categoryClass = (Class) array[i];
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Expecting a Category Class for argument " + i);
+ }
+ list.add(categoryClass);
+ }
+ return GroovyCategorySupport.use(list, closure);
}
- /**
- * Splits all items into two collections based on the closure condition.
- * The first list contains all items which match the closure expression.
- * The second list all those that don't.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">assert [[2,4] as Set, [1,3] as Set] == ([1,2,3,4] as Set).split { it % 2 == 0 }</pre>
- *
- * @param self a Set of values
- * @param closure a closure condition
- * @return a List whose first item is the accepted values and whose second item is the rejected values
- * @since 2.4.0
- */
- @SuppressWarnings("unchecked")
- public static <T> List<Set<T>> split(Set<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return (List<Set<T>>) (List<?>) split((Collection<T>) self, closure);
- }
+ //--------------------------------------------------------------------------
+ // sprintf
/**
- * Finds all non-null subsequences of a list.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">def result = [1, 2, 3].subsequences()
- * assert result == [[1, 2, 3], [1, 3], [2, 3], [1, 2], [1], [2], [3]] as Set</pre>
+ * Sprintf to a string.
*
- * @param self the List of items
- * @return the subsequences from the list
- * @since 1.7.0
+ * @param self any Object
+ * @param format a format string
+ * @param values values referenced by the format specifiers in the format string
+ * @return the resulting formatted string
+ * @since 1.5.0
*/
- public static <T> Set<List<T>> subsequences(List<T> self) {
- return GroovyCollections.subsequences(self);
+ public static String sprintf(Object self, String format, Object[] values) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(outputStream);
+ out.printf(format, values);
+ return outputStream.toString();
}
/**
- * Finds all permutations of an iterable.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">def result = [1, 2, 3].permutations()
- * assert result == [[3, 2, 1], [3, 1, 2], [1, 3, 2], [2, 3, 1], [2, 1, 3], [1, 2, 3]] as Set</pre>
+ * Returns a formatted string using the specified format string and
+ * arguments.
*
- * @param self the Iterable of items
- * @return the permutations from the list
- * @since 1.7.0
+ * @param self any Object
+ * @param format A format string
+ * @param arg Argument which is referenced by the format specifiers in the format
+ * string. The type of <code>arg</code> should be one of Object[], List,
+ * int[], short[], byte[], char[], boolean[], long[], float[], or double[].
+ * @return the resulting printf'd string
+ * @since 1.5.0
*/
- public static <T> Set<List<T>> permutations(Iterable<T> self) {
- Set<List<T>> ans = new HashSet<>();
- PermutationGenerator<T> generator = new PermutationGenerator<>(self);
- while (generator.hasNext()) {
- ans.add(generator.next());
+ public static String sprintf(Object self, String format, Object arg) {
+ if (arg instanceof Object[]) {
+ return sprintf(self, format, (Object[]) arg);
}
- return ans;
+ if (arg instanceof List) {
+ return sprintf(self, format, ((List) arg).toArray());
+ }
+ if (!arg.getClass().isArray()) {
+ Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
+ o[0] = arg;
+ return sprintf(self, format, o);
+ }
+
+ Object[] ans;
+ String elemType = arg.getClass().getName();
+ switch (elemType) {
+ case "[I":
+ int[] ia = (int[]) arg;
+ ans = new Integer[ia.length];
+ for (int i = 0; i < ia.length; i++) {
+ ans[i] = ia[i];
+ }
+ break;
+ case "[C":
+ char[] ca = (char[]) arg;
+ ans = new Character[ca.length];
+ for (int i = 0; i < ca.length; i++) {
+ ans[i] = ca[i];
+ }
+ break;
+ case "[Z": {
+ boolean[] ba = (boolean[]) arg;
+ ans = new Boolean[ba.length];
+ for (int i = 0; i < ba.length; i++) {
+ ans[i] = ba[i];
+ }
+ break;
+ }
+ case "[B": {
+ byte[] ba = (byte[]) arg;
+ ans = new Byte[ba.length];
+ for (int i = 0; i < ba.length; i++) {
+ ans[i] = ba[i];
+ }
+ break;
+ }
+ case "[S":
+ short[] sa = (short[]) arg;
+ ans = new Short[sa.length];
+ for (int i = 0; i < sa.length; i++) {
+ ans[i] = sa[i];
+ }
+ break;
+ case "[F":
+ float[] fa = (float[]) arg;
+ ans = new Float[fa.length];
+ for (int i = 0; i < fa.length; i++) {
+ ans[i] = fa[i];
+ }
+ break;
+ case "[J":
+ long[] la = (long[]) arg;
+ ans = new Long[la.length];
+ for (int i = 0; i < la.length; i++) {
+ ans[i] = la[i];
+ }
+ break;
+ case "[D":
+ double[] da = (double[]) arg;
+ ans = new Double[da.length];
+ for (int i = 0; i < da.length; i++) {
+ ans[i] = da[i];
+ }
+ break;
+ default:
+ throw new RuntimeException("sprintf(String," + arg + ")");
+ }
+ return sprintf(self, format, ans);
}
+ //--------------------------------------------------------------------------
+ // tap
+
/**
- * Finds all permutations of an iterable, applies a function to each permutation and collects the result
- * into a list.
+ * Allows the closure to be called for the object reference self (similar
+ * to <code>with</code>) and always returns self.
* <p>
- * Example usage:
- * <pre class="groovyTestCase">Set result = [1, 2, 3].permutations { it.collect { v {@code ->} 2*v }}
- * assert result == [[6, 4, 2], [6, 2, 4], [2, 6, 4], [4, 6, 2], [4, 2, 6], [2, 4, 6]] as Set</pre>
+ * Any method invoked inside the closure will first be invoked on the
+ * self reference. For instance, the following method calls to the append()
+ * method are invoked on the StringBuilder instance:
+ * <pre>
+ * def b = new StringBuilder().tap {
+ * append('foo')
+ * append('bar')
+ * }
+ * assert b.toString() == 'foobar'
+ * </pre>
+ * This is commonly used to simplify object creation, such as this example:
+ * <pre>
+ * def p = new Person().tap {
+ * firstName = 'John'
+ * lastName = 'Doe'
+ * }
+ * </pre>
*
- * @param self the Iterable of items
- * @param function the function to apply on each permutation
- * @return the list of results of the application of the function on each permutation
- * @since 2.2.0
+ * @param self the object to have a closure act upon
+ * @param closure the closure to call on the object
+ * @return self
+ * @see #with(Object, boolean, Closure)
+ * @see #with(Object, Closure)
+ * @since 2.5.0
*/
- public static <T,V> List<V> permutations(Iterable<T> self, Closure<V> function) {
- return collect(permutations(self), function);
+ @SuppressWarnings("unchecked")
+ public static <T,U> U tap(
+ @DelegatesTo.Target("self") U self,
+ @DelegatesTo(value=DelegatesTo.Target.class,
+ target="self",
+ strategy=Closure.DELEGATE_FIRST)
+ @ClosureParams(FirstParam.class)
+ Closure<T> closure) {
+ return (U) with(self, true, (Closure<Object>)closure);
}
+ //--------------------------------------------------------------------------
+ // unique
+
/**
- * Adds GroovyCollections#transpose(List) as a method on lists.
- * A Transpose Function takes a collection of columns and returns a collection of
- * rows. The first row consists of the first element from each column. Successive
- * rows are constructed similarly.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">
- * def result = [['a', 'b'], [1, 2]].transpose()
- * assert result == [['a', 1], ['b', 2]]
- * </pre>
- * <pre class="groovyTestCase">
- * def result = [['a', 'b'], [1, 2], [3, 4]].transpose()
- * assert result == [['a', 1, 3], ['b', 2, 4]]
- * </pre>
+ * Returns an iterator equivalent to this iterator with all duplicated items removed
+ * by using Groovy's default number-aware comparator. The original iterator will become
+ * exhausted of elements after determining the unique values. A new iterator
+ * for the unique values will be returned.
*
- * @param self a List of lists
- * @return a List of the transposed lists
- * @see groovy.util.GroovyCollections#transpose(java.util.List)
- * @since 1.5.0
+ * @param self an Iterator
+ * @return a new Iterator of the unique items from the original iterator
+ * @since 1.5.5
*/
- public static List transpose(List self) {
- return GroovyCollections.transpose(self);
- }
-
- // internal helper method
- protected static <T, K, V> T callClosureForMapEntry(@ClosureParams(value=FromString.class, options={"K,V","Map.Entry<K,V>"}) Closure<T> closure, Map.Entry<K,V> entry) {
- if (closure.getMaximumNumberOfParameters() == 2) {
- return closure.call(entry.getKey(), entry.getValue());
- }
- return closure.call(entry);
- }
-
- // internal helper method
- protected static <T> T callClosureForLine(@ClosureParams(value=FromString.class, options={"String","String,Integer"}) Closure<T> closure, String line, int counter) {
- if (closure.getMaximumNumberOfParameters() == 2) {
- return closure.call(line, counter);
- }
- return closure.call(line);
- }
-
- // internal helper method
- protected static <T, K, V> T callClosureForMapEntryAndCounter(@ClosureParams(value=FromString.class, options={"K,V,Integer", "K,V","Map.Entry<K,V>"}) Closure<T> closure, Map.Entry<K,V> entry, int counter) {
- if (closure.getMaximumNumberOfParameters() == 3) {
- return closure.call(entry.getKey(), entry.getValue(), counter);
- }
- if (closure.getMaximumNumberOfParameters() == 2) {
- return closure.call(entry, counter);
- }
- return closure.call(entry);
+ public static <T> Iterator<T> unique(Iterator<T> self) {
+ return uniqueItems(new IteratorIterableAdapter<>(self)).listIterator();
}
/**
- * Sums the items in an Iterable. This is equivalent to invoking the
- * "plus" method on all items in the Iterable.
- * <pre class="groovyTestCase">assert 1+2+3+4 == [1,2,3,4].sum()</pre>
+ * Modifies this collection to remove all duplicated items, using Groovy's
+ * default number-aware comparator.
+ * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre>
*
- * @param self Iterable of values to add together
- * @return The sum of all the items
- * @see #sum(Iterator)
- * @since 2.2.0
+ * @param self a collection
+ * @return the now modified collection
+ * @see #unique(Collection, boolean)
+ * @since 1.0
*/
- public static Object sum(Iterable<?> self) {
- return sum(self.iterator(), null, true);
+ public static <T> Collection<T> unique(Collection<T> self) {
+ return unique(self, true);
}
/**
- * Sums the items from an Iterator. This is equivalent to invoking the
- * "plus" method on all items from the Iterator. The iterator will become
- * exhausted of elements after determining the sum value.
+ * Modifies this List to remove all duplicated items, using Groovy's
+ * default number-aware comparator.
+ * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre>
*
- * @param self an Iterator for the values to add together
- * @return The sum of all the items
- * @since 1.5.5
+ * @param self a List
+ * @return the now modified List
+ * @see #unique(Collection, boolean)
+ * @since 2.4.0
*/
- public static Object sum(Iterator<Object> self) {
- return sum(self, null, true);
+ public static <T> List<T> unique(List<T> self) {
+ return (List<T>) unique((Collection<T>) self, true);
}
/**
- * Sums the items in an Iterable, adding the result to some initial value.
+ * Remove all duplicates from a given Collection using Groovy's default number-aware comparator.
+ * If mutate is true, it works by modifying the original object (and also returning it).
+ * If mutate is false, a new collection is returned leaving the original unchanged.
* <pre class="groovyTestCase">
- * assert 5+1+2+3+4 == [1,2,3,4].sum(5)
+ * assert [1,3] == [1,3,3].unique()
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def orig = [1, 3, 2, 3]
+ * def uniq = orig.unique(false)
+ * assert orig == [1, 3, 2, 3]
+ * assert uniq == [1, 3, 2]
* </pre>
*
- * @param self an Iterable of values to sum
- * @param initialValue the items in the collection will be summed to this initial value
- * @return The sum of all the items.
- * @see #sum(Iterator, Object)
- * @since 2.2.0
- */
- public static Object sum(Iterable<?> self, Object initialValue) {
- return sum(self.iterator(), initialValue, false);
- }
-
- /**
- * Sums the items from an Iterator, adding the result to some initial value. This is
- * equivalent to invoking the "plus" method on all items from the Iterator. The iterator
- * will become exhausted of elements after determining the sum value.
- *
- * @param self an Iterator for the values to add together
- * @param initialValue the items in the collection will be summed to this initial value
- * @return The sum of all the items
- * @since 1.5.5
+ * @param self a collection
+ * @param mutate false will cause a new list containing unique items from the collection to be created, true will mutate collections in place
+ * @return the now modified collection
+ * @since 1.8.1
*/
- public static Object sum(Iterator<?> self, Object initialValue) {
- return sum(self, initialValue, false);
+ public static <T> Collection<T> unique(Collection<T> self, boolean mutate) {
+ List<T> answer = uniqueItems(self);
+ if (mutate) {
+ self.clear();
+ self.addAll(answer);
+ }
+ return mutate ? self : answer ;
}
- static Object sum(Iterator<?> self, Object initialValue, boolean first) {
- Object result = initialValue;
- Object[] param = new Object[1];
- while (self.hasNext()) {
- Object next = self.next();
- param[0] = next;
- if (first) {
- result = param[0];
- first = false;
- continue;
+ private static <T> List<T> uniqueItems(Iterable<T> self) {
+ List<T> answer = new ArrayList<>();
+ for (T t : self) {
+ boolean duplicated = false;
+ for (T t2 : answer) {
+ if (coercedEquals(t, t2)) {
+ duplicated = true;
+ break;
+ }
}
- MetaClass metaClass = InvokerHelper.getMetaClass(result);
- result = metaClass.invokeMethod(result, "plus", param);
+ if (!duplicated)
+ answer.add(t);
}
- return result;
+ return answer;
}
/**
- * Sums the result of applying a closure to each item of an Iterable.
- * <code>coll.sum(closure)</code> is equivalent to:
- * <code>coll.collect(closure).sum()</code>.
- * <pre class="groovyTestCase">assert 4+6+10+12 == [2,3,5,6].sum { it * 2 }</pre>
+ * Remove all duplicates from a given List using Groovy's default number-aware comparator.
+ * If mutate is true, it works by modifying the original object (and also returning it).
+ * If mutate is false, a new collection is returned leaving the original unchanged.
+ * <pre class="groovyTestCase">
+ * assert [1,3] == [1,3,3].unique()
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def orig = [1, 3, 2, 3]
+ * def uniq = orig.unique(false)
+ * assert orig == [1, 3, 2, 3]
+ * assert uniq == [1, 3, 2]
+ * </pre>
*
- * @param self an Iterable
- * @param closure a single parameter closure that returns a (typically) numeric value.
- * @return The sum of the values returned by applying the closure to each
- * item of the Iterable.
- * @since 2.2.0
+ * @param self a List
+ * @param mutate false will cause a new List containing unique items from the List to be created, true will mutate List in place
+ * @return the now modified List
+ * @since 2.4.0
*/
- public static <T> Object sum(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return sum(self.iterator(), null, closure, true);
+ public static <T> List<T> unique(List<T> self, boolean mutate) {
+ return (List<T>) unique((Collection<T>) self, mutate);
}
/**
- * Sums the result of applying a closure to each item returned from an iterator.
- * <code>iter.sum(closure)</code> is equivalent to:
- * <code>iter.collect(closure).sum()</code>. The iterator will become
- * exhausted of elements after determining the sum value.
+ * Returns an iterator equivalent to this iterator but with all duplicated items
+ * removed by using a Closure to determine duplicate (equal) items.
+ * The original iterator will be fully processed after the call.
+ * <p>
+ * If the closure takes a single parameter, the argument passed will be each element,
+ * and the closure should return a value used for comparison (either using
+ * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+ * If the closure takes two parameters, two items from the Iterator
+ * will be passed as arguments, and the closure should return an
+ * int value (with 0 indicating the items are not unique).
*
- * @param self An Iterator
- * @param closure a single parameter closure that returns a (typically) numeric value.
- * @return The sum of the values returned by applying the closure to each
- * item from the Iterator.
- * @since 1.7.1
+ * @param self an Iterator
+ * @param condition a Closure used to determine unique items
+ * @return the modified Iterator
+ * @since 1.5.5
*/
- public static <T> Object sum(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return sum(self, null, closure, true);
+ public static <T> Iterator<T> unique(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+ Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+ ? new OrderBy<>(condition, true)
+ : new ClosureComparator<>(condition);
+ return unique(self, comparator);
}
/**
- * Sums the result of applying a closure to each item of an Iterable to some initial value.
- * <code>iter.sum(initVal, closure)</code> is equivalent to:
- * <code>iter.collect(closure).sum(initVal)</code>.
- * <pre class="groovyTestCase">assert 50+4+6+10+12 == [2,3,5,6].sum(50) { it * 2 }</pre>
+ * A convenience method for making a collection unique using a Closure
+ * to determine duplicate (equal) items.
+ * <p>
+ * If the closure takes a single parameter, the
+ * argument passed will be each element, and the closure
+ * should return a value used for comparison (either using
+ * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+ * If the closure takes two parameters, two items from the collection
+ * will be passed as arguments, and the closure should return an
+ * int value (with 0 indicating the items are not unique).
+ * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre>
+ * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b {@code ->} a {@code <=>} b }</pre>
*
- * @param self an Iterable
- * @param closure a single parameter closure that returns a (typically) numeric value.
- * @param initialValue the closure results will be summed to this initial value
- * @return The sum of the values returned by applying the closure to each
- * item of the collection.
- * @since 1.5.0
+ * @param self a Collection
+ * @param closure a 1 or 2 arg Closure used to determine unique items
+ * @return self without any duplicates
+ * @see #unique(Collection, boolean, Closure)
+ * @since 1.0
*/
- public static <T> Object sum(Iterable<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return sum(self.iterator(), initialValue, closure, false);
+ public static <T> Collection<T> unique(Collection<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return unique(self, true, closure);
}
/**
- * Sums the result of applying a closure to each item of an Iterator to some initial value.
- * <code>iter.sum(initVal, closure)</code> is equivalent to:
- * <code>iter.collect(closure).sum(initVal)</code>. The iterator will become
- * exhausted of elements after determining the sum value.
+ * A convenience method for making a List unique using a Closure
+ * to determine duplicate (equal) items.
+ * <p>
+ * If the closure takes a single parameter, the
+ * argument passed will be each element, and the closure
+ * should return a value used for comparison (either using
+ * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+ * If the closure takes two parameters, two items from the List
+ * will be passed as arguments, and the closure should return an
+ * int value (with 0 indicating the items are not unique).
+ * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre>
+ * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b {@code ->} a {@code <=>} b }</pre>
*
- * @param self an Iterator
- * @param closure a single parameter closure that returns a (typically) numeric value.
- * @param initialValue the closure results will be summed to this initial value
- * @return The sum of the values returned by applying the closure to each
- * item from the Iterator.
- * @since 1.7.1
+ * @param self a List
+ * @param closure a 1 or 2 arg Closure used to determine unique items
+ * @return self without any duplicates
+ * @see #unique(Collection, boolean, Closure)
+ * @since 2.4.0
*/
- public static <T> Object sum(Iterator<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return sum(self, initialValue, closure, false);
- }
-
- static Object sum(Iterator<?> self, Object initialValue, Closure closure, boolean first) {
- Object result = initialValue;
- Object[] closureParam = new Object[1];
- Object[] plusParam = new Object[1];
- while (self.hasNext()) {
- closureParam[0] = self.next();
- plusParam[0] = closure.call(closureParam);
- if (first) {
- result = plusParam[0];
- first = false;
- continue;
- }
- MetaClass metaClass = InvokerHelper.getMetaClass(result);
- result = metaClass.invokeMethod(result, "plus", plusParam);
- }
- return result;
+ public static <T> List<T> unique(List<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return (List<T>) unique((Collection<T>) self, true, closure);
}
/**
- * Provide the standard Groovy <code>size()</code> method for <code>Iterator</code>.
- * The iterator will become exhausted of elements after determining the size value.
+ * A convenience method for making a collection unique using a Closure to determine duplicate (equal) items.
+ * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned.
+ * <p>
+ * If the closure takes a single parameter, each element from the Collection will be passed to the closure. The closure
+ * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+ * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the collection
+ * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+ * <pre class="groovyTestCase">
+ * def orig = [1, 3, 4, 5]
+ * def uniq = orig.unique(false) { it % 2 }
+ * assert orig == [1, 3, 4, 5]
+ * assert uniq == [1, 4]
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def orig = [2, 3, 3, 4]
+ * def uniq = orig.unique(false) { a, b {@code ->} a {@code <=>} b }
+ * assert orig == [2, 3, 3, 4]
+ * assert uniq == [2, 3, 4]
+ * </pre>
*
- * @param self an Iterator
- * @return the length of the Iterator
- * @since 1.5.5
+ * @param self a Collection
+ * @param mutate false will always cause a new list to be created, true will mutate lists in place
+ * @param closure a 1 or 2 arg Closure used to determine unique items
+ * @return self without any duplicates
+ * @since 1.8.1
*/
- public static int size(Iterator self) {
- int count = 0;
- while (self.hasNext()) {
- self.next();
- count++;
+ public static <T> Collection<T> unique(Collection<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ // use a comparator of one item or two
+ int params = closure.getMaximumNumberOfParameters();
+ if (params == 1) {
+ self = unique(self, mutate, new OrderBy<>(closure, true));
+ } else {
+ self = unique(self, mutate, new ClosureComparator<>(closure));
}
- return count;
+ return self;
}
/**
- * Provide the standard Groovy <code>size()</code> method for <code>Iterable</code>.
+ * A convenience method for making a List unique using a Closure to determine duplicate (equal) items.
+ * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned.
+ * <p>
+ * If the closure takes a single parameter, each element from the List will be passed to the closure. The closure
+ * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+ * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the collection
+ * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
* <pre class="groovyTestCase">
- * def items = [1, 2, 3]
- * def iterable = { [ hasNext:{ !items.isEmpty() }, next:{ items.pop() } ] as Iterator } as Iterable
- * assert iterable.size() == 3
+ * def orig = [1, 3, 4, 5]
+ * def uniq = orig.unique(false) { it % 2 }
+ * assert orig == [1, 3, 4, 5]
+ * assert uniq == [1, 4]
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def orig = [2, 3, 3, 4]
+ * def uniq = orig.unique(false) { a, b {@code ->} a {@code <=>} b }
+ * assert orig == [2, 3, 3, 4]
+ * assert uniq == [2, 3, 4]
* </pre>
*
- * @param self an Iterable
- * @return the length of the Iterable
- * @since 2.3.8
+ * @param self a List
+ * @param mutate false will always cause a new list to be created, true will mutate lists in place
+ * @param closure a 1 or 2 arg Closure used to determine unique items
+ * @return self without any duplicates
+ * @since 2.4.0
*/
- public static int size(Iterable self) {
- return size(self.iterator());
+ public static <T> List<T> unique(List<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return (List<T>) unique((Collection<T>) self, mutate, closure);
}
/**
- * Creates a sub-Map containing the given keys. This method is similar to
- * List.subList() but uses keys rather than index ranges.
- * <pre class="groovyTestCase">assert [1:10, 2:20, 4:40].subMap( [2, 4] ) == [2:20, 4:40]</pre>
+ * Returns an iterator equivalent to this iterator with all duplicated
+ * items removed by using the supplied comparator. The original iterator
+ * will be exhausted upon returning.
*
- * @param map a Map
- * @param keys a Collection of keys
- * @return a new Map containing the given keys
- * @since 1.0
+ * @param self an Iterator
+ * @param comparator a Comparator
+ * @return the modified Iterator
+ * @since 1.5.5
*/
- public static <K, V> Map<K, V> subMap(Map<K, V> map, Collection<K> keys) {
- Map<K, V> answer = new LinkedHashMap<>(keys.size());
- for (K key : keys) {
- if (map.containsKey(key)) {
- answer.put(key, map.get(key));
- }
- }
- return answer;
+ public static <T> Iterator<T> unique(Iterator<T> self, Comparator<? super T> comparator) {
+ return uniqueItems(new IteratorIterableAdapter<>(self), comparator).listIterator();
}
- /**
- * Creates a sub-Map containing the given keys. This method is similar to
- * List.subList() but uses keys rather than index ranges. The original
- * map is unaltered.
- * <pre class="groovyTestCase">
- * def orig = [1:10, 2:20, 3:30, 4:40]
- * assert orig.subMap([1, 3] as int[]) == [1:10, 3:30]
- * assert orig.subMap([2, 4] as Integer[]) == [2:20, 4:40]
- * assert orig.size() == 4
- * </pre>
- *
- * @param map a Map
- * @param keys an array of keys
- * @return a new Map containing the given keys
- * @since 2.1.0
- */
- public static <K, V> Map<K, V> subMap(Map<K, V> map, K[] keys) {
- Map<K, V> answer = new LinkedHashMap<>(keys.length);
- for (K key : keys) {
- if (map.containsKey(key)) {
- answer.put(key, map.get(key));
- }
- }
- return answer;
- }
+ private static final class IteratorIterableAdapter<T> implements Iterable<T> {
+ private final Iterator<T> self;
- /**
- * A helper method to allow lists to work with subscript operators.
- * <pre class="groovyTestCase">def list = [2, 3]
- * list[0] = 1
- * assert list == [1, 3]</pre>
- *
- * @param self a List
- * @param idx an index
- * @param value the value to put at the given index
- * @since 1.0
- */
- public static <T> void putAt(List<T> self, int idx, T value) {
- int size = self.size();
- idx = normaliseIndex(idx, size);
- if (idx < size) {
- self.set(idx, value);
- } else {
- while (size < idx) {
- self.add(size++, null);
- }
- self.add(idx, value);
+ private IteratorIterableAdapter(Iterator<T> self) {
+ this.self = self;
}
- }
- /**
- * Support subscript operator for list modification.
- */
- public static <T> void putAt(List<T> self, Number idx, T value) {
- putAt(self, idx.intValue(), value);
+ @Override
+ public Iterator<T> iterator() {
+ return self;
+ }
}
/**
- * A helper method to allow lists to work with subscript operators.
+ * Remove all duplicates from a given Collection.
+ * Works on the original object (and also returns it).
+ * The order of members in the Collection are compared by the given Comparator.
+ * For each duplicate, the first member which is returned
+ * by the given Collection's iterator is retained, but all other ones are removed.
+ * The given Collection's original order is preserved.
+ * <p>
* <pre class="groovyTestCase">
- * def list = ["a", true]
- * {@code list[1..<1] = 5}
- * assert list == ["a", 5, true]
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
+ *
+ * class PersonComparator implements Comparator {
+ * int compare(Object o1, Object o2) {
+ * Person p1 = (Person) o1
+ * Person p2 = (Person) o2
+ * if (p1.lname != p2.lname)
+ * return p1.lname.compareTo(p2.lname)
+ * else
+ * return p1.fname.compareTo(p2.fname)
+ * }
+ *
+ * boolean equals(Object obj) {
+ * return this.equals(obj)
+ * }
+ * }
+ *
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
+ *
+ * def list = [a, b, c, d]
+ * List list2 = list.unique(new PersonComparator())
+ * assert( list2 == list {@code &&} list == [a, b, c] )
* </pre>
*
- * @param self a List
- * @param range the (in this case empty) subset of the list to set
- * @param value the values to put at the given sublist or a Collection of values
+ * @param self a Collection
+ * @param comparator a Comparator
+ * @return self the now modified collection without duplicates
+ * @see #unique(java.util.Collection, boolean, java.util.Comparator)
* @since 1.0
*/
- @SuppressWarnings("unchecked")
- public static void putAt(List self, EmptyRange range, Object value) {
- RangeInfo info = subListBorders(self.size(), range);
- List sublist = self.subList(info.from, info.to);
- sublist.clear();
- if (value instanceof Collection) {
- Collection col = (Collection) value;
- if (col.isEmpty()) return;
- sublist.addAll(col);
- } else {
- sublist.add(value);
- }
+ public static <T> Collection<T> unique(Collection<T> self, Comparator<? super T> comparator) {
+ return unique(self, true, comparator) ;
}
/**
- * A helper method to allow lists to work with subscript operators.
+ * Remove all duplicates from a given List.
+ * Works on the original object (and also returns it).
+ * The order of members in the List are compared by the given Comparator.
+ * For each duplicate, the first member which is returned
+ * by the given List's iterator is retained, but all other ones are removed.
+ * The given List's original order is preserved.
+ * <p>
* <pre class="groovyTestCase">
- * def list = ["a", true]
- * {@code list[1..<1] = [4, 3, 2]}
- * assert list == ["a", 4, 3, 2, true]
- * </pre>
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
*
- * @param self a List
- * @param range the (in this case empty) subset of the list to set
- * @param value the Collection of values
- * @since 1.0
- * @see #putAt(java.util.List, groovy.lang.EmptyRange, java.lang.Object)
- */
- public static void putAt(List self, EmptyRange range, Collection value) {
- putAt(self, range, (Object)value);
- }
-
- private static <T> List<T> resizeListWithRangeAndGetSublist(List<T> self, IntRange range) {
- RangeInfo info = subListBorders(self.size(), range);
- int size = self.size();
- if (info.to >= size) {
- while (size < info.to) {
- self.add(size++, null);
- }
- }
- List<T> sublist = self.subList(info.from, info.to);
- sublist.clear();
- return sublist;
- }
-
- /**
- * List subscript assignment operator when given a range as the index and
- * the assignment operand is a collection.
- * Example: <pre class="groovyTestCase">def myList = [4, 3, 5, 1, 2, 8, 10]
- * myList[3..5] = ["a", true]
- * assert myList == [4, 3, 5, "a", true, 10]</pre>
+ * class PersonComparator implements Comparator {
+ * int compare(Object o1, Object o2) {
+ * Person p1 = (Person) o1
+ * Person p2 = (Person) o2
+ * if (p1.lname != p2.lname)
+ * return p1.lname.compareTo(p2.lname)
+ * else
+ * return p1.fname.compareTo(p2.fname)
+ * }
*
- * Items in the given
- * range are replaced with items from the collection.
+ * boolean equals(Object obj) {
+ * return this.equals(obj)
+ * }
+ * }
*
- * @param self a List
- * @param range the subset of the list to set
- * @param col the collection of values to put at the given sublist
- * @since 1.5.0
- */
- @SuppressWarnings("unchecked")
- public static void putAt(List self, IntRange range, Collection col) {
- List sublist = resizeListWithRangeAndGetSublist(self, range);
- if (col.isEmpty()) return;
- sublist.addAll(col);
- }
-
- /**
- * List subscript assignment operator when given a range as the index.
- * Example: <pre class="groovyTestCase">def myList = [4, 3, 5, 1, 2, 8, 10]
- * myList[3..5] = "b"
- * assert myList == [4, 3, 5, "b", 10]</pre>
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
*
- * Items in the given
- * range are replaced with the operand. The <code>value</code> operand is
- * always treated as a single value.
+ * def list = [a, b, c, d]
+ * List list2 = list.unique(new PersonComparator())
+ * assert( list2 == list {@code &&} list == [a, b, c] )
+ * </pre>
*
- * @param self a List
- * @param range the subset of the list to set
- * @param value the value to put at the given sublist
- * @since 1.0
+ * @param self a List
+ * @param comparator a Comparator
+ * @return self the now modified List without duplicates
+ * @see #unique(java.util.Collection, boolean, java.util.Comparator)
+ * @since 2.4.0
*/
- @SuppressWarnings("unchecked")
- public static void putAt(List self, IntRange range, Object value) {
- List sublist = resizeListWithRangeAndGetSublist(self, range);
- sublist.add(value);
+ public static <T> List<T> unique(List<T> self, Comparator<? super T> comparator) {
+ return (List<T>) unique((Collection<T>) self, true, comparator);
}
/**
- * A helper method to allow lists to work with subscript operators.
- * <pre class="groovyTestCase">def list = ["a", true, 42, 9.4]
- * list[1, 4] = ["x", false]
- * assert list == ["a", "x", 42, 9.4, false]</pre>
+ * Remove all duplicates from a given Collection.
+ * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new collection is returned.
+ * The order of members in the Collection are compared by the given Comparator.
+ * For each duplicate, the first member which is returned
+ * by the given Collection's iterator is retained, but all other ones are removed.
+ * The given Collection's original order is preserved.
+ * <p>
+ * <pre class="groovyTestCase">
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
*
- * @param self a List
- * @param splice the subset of the list to set
- * @param values the value to put at the given sublist
- * @since 1.0
+ * class PersonComparator implements Comparator {
+ * int compare(Object o1, Object o2) {
+ * Person p1 = (Person) o1
+ * Person p2 = (Person) o2
+ * if (p1.lname != p2.lname)
+ * return p1.lname.compareTo(p2.lname)
+ * else
+ * return p1.fname.compareTo(p2.fname)
+ * }
+ *
+ * boolean equals(Object obj) {
+ * return this.equals(obj)
+ * }
+ * }
+ *
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
+ *
+ * def list = [a, b, c, d]
+ * List list2 = list.unique(false, new PersonComparator())
+ * assert( list2 != list {@code &&} list2 == [a, b, c] )
+ * </pre>
+ *
+ * @param self a Collection
+ * @param mutate false will always cause a new collection to be created, true will mutate collections in place
+ * @param comparator a Comparator
+ * @return self the collection without duplicates
+ * @since 1.8.1
*/
- @SuppressWarnings("unchecked")
- public static void putAt(List self, List splice, Collection values) {
- if (splice.isEmpty()) {
- if (!values.isEmpty())
- throw new IllegalArgumentException("Trying to replace 0 elements with " + values.size() + " elements");
- return;
- }
- Object first = splice.iterator().next();
- if (first instanceof Integer) {
- if (values.size() != splice.size())
- throw new IllegalArgumentException("Trying to replace " + splice.size() + " elements with " + values.size() + " elements");
- Iterator<?> valuesIter = values.iterator();
- for (Object index : splice) {
- putAt(self, (Integer) index, valuesIter.next());
- }
- } else {
- throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of " + first.getClass().getName());
+ public static <T> Collection<T> unique(Collection<T> self, boolean mutate, Comparator<? super T> comparator) {
+ List<T> answer = uniqueItems(self, comparator);
+ if (mutate) {
+ self.clear();
+ self.addAll(answer);
}
+ return mutate ? self : answer;
}
- /**
- * @deprecated use #putAt(List, List, Collection), retained for binary compatibility
- */
- @Deprecated
- @SuppressWarnings("unchecked")
- public static void putAt(List self, List splice, List values) {
- putAt(self, splice, (Collection) values);
- }
-
- /**
- * A helper method to allow lists to work with subscript operators.
- * <pre class="groovyTestCase">def list = ["a", true, 42, 9.4]
- * list[1, 3] = 5
- * assert list == ["a", 5, 42, 5]</pre>
- *
- * @param self a List
- * @param splice the subset of the list to set
- * @param value the value to put at the given sublist
- * @since 1.0
- */
- @SuppressWarnings("unchecked")
- public static void putAt(List self, List splice, Object value) {
- if (splice.isEmpty()) {
- return;
- }
- Object first = splice.get(0);
- if (first instanceof Integer) {
- for (Object index : splice) {
- self.set((Integer) index, value);
+ private static <T> List<T> uniqueItems(Iterable<T> self, Comparator<? super T> comparator) {
+ List<T> answer = new ArrayList<>();
+ for (T t : self) {
+ boolean duplicated = false;
+ for (T t2 : answer) {
+ if (comparator.compare(t, t2) == 0) {
+ duplicated = true;
+ break;
+ }
}
- } else {
- throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName());
+ if (!duplicated)
+ answer.add(t);
}
+ return answer;
}
/**
- * Returns a new <code>Map</code> containing all entries from <code>left</code> and <code>right</code>,
- * giving precedence to <code>right</code>. Any keys appearing in both Maps
- * will appear in the resultant map with values from the <code>right</code>
- * operand. If the <code>left</code> map is one of TreeMap, LinkedHashMap, Hashtable
- * or Properties, the returned Map will preserve that type, otherwise a HashMap will
- * be returned.
+ * Remove all duplicates from a given List.
+ * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new List is returned.
+ * The order of members in the List are compared by the given Comparator.
+ * For each duplicate, the first member which is returned
+ * by the given List's iterator is retained, but all other ones are removed.
+ * The given List's original order is preserved.
* <p>
- * Roughly equivalent to <code>Map m = new HashMap(); m.putAll(left); m.putAll(right); return m;</code>
- * but with some additional logic to preserve the <code>left</code> Map type for common cases as
- * described above.
* <pre class="groovyTestCase">
- * assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]
- * </pre>
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
*
- * @param left a Map
- * @param right a Map
- * @return a new Map containing all entries from left and right
- * @since 1.5.0
- */
- public static <K, V> Map<K, V> plus(Map<K, V> left, Map<K, V> right) {
- Map<K, V> map = cloneSimilarMap(left);
- map.putAll(right);
- return map;
- }
-
- /**
- * A helper method to allow maps to work with subscript operators
+ * class PersonComparator implements Comparator {
+ * int compare(Object o1, Object o2) {
+ * Person p1 = (Person) o1
+ * Person p2 = (Person) o2
+ * if (p1.lname != p2.lname)
+ * return p1.lname.compareTo(p2.lname)
+ * else
+ * return p1.fname.compareTo(p2.fname)
+ * }
*
- * @param self a Map
- * @param key an Object as a key for the map
- * @param value the value to put into the map
- * @return the value corresponding to the given key
- * @since 1.0
- */
- public static <K,V> V putAt(Map<K,V> self, K key, V value) {
- self.put(key, value);
- return value;
- }
-
- /**
- * Synonym for {@link #toSpreadMap(java.util.Map)}.
+ * boolean equals(Object obj) {
+ * return this.equals(obj)
+ * }
+ * }
*
- * @param self a map
- * @return a newly created SpreadMap
- * @since 1.0
- */
- public static SpreadMap spread(Map self) {
- return toSpreadMap(self);
- }
-
- /**
- * Returns a new <code>SpreadMap</code> from this map.
- * <p>
- * The example below shows the various possible use cases:
- * <pre class="groovyTestCase">
- * def fn(Map m) { return m.a + m.b + m.c + m.d }
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
*
- * assert fn(a:1, b:2, c:3, d:4) == 10
- * assert fn(a:1, *:[b:2, c:3], d:4) == 10
- * assert fn([a:1, b:2, c:3, d:4].toSpreadMap()) == 10
- * assert fn((['a', 1, 'b', 2, 'c', 3, 'd', 4] as Object[]).toSpreadMap()) == 10
- * assert fn(['a', 1, 'b', 2, 'c', 3, 'd', 4].toSpreadMap()) == 10
- * assert fn(['abcd'.toList(), 1..4].transpose().flatten().toSpreadMap()) == 10
+ * def list = [a, b, c, d]
+ * List list2 = list.unique(false, new PersonComparator())
+ * assert( list2 != list {@code &&} list2 == [a, b, c] )
* </pre>
- * Note that toSpreadMap() is not normally used explicitly but under the covers by Groovy.
*
- * @param self a map to be converted into a SpreadMap
- * @return a newly created SpreadMap if this map is not null and its size is positive.
- * @see groovy.lang.SpreadMap#SpreadMap(java.util.Map)
- * @since 1.0
- */
- public static SpreadMap toSpreadMap(Map self) {
- if (self == null)
- throw new GroovyRuntimeException("Fail to convert Map to SpreadMap, because it is null.");
- else
- return new SpreadMap(self);
- }
-
- /**
- * Creates a spreadable map from this list.
- * <p>
- * @param self a list
- * @return a newly created SpreadMap
- * @see groovy.lang.SpreadMap#SpreadMap(java.util.List)
- * @see #toSpreadMap(java.util.Map)
- * @since 1.8.0
- */
- public static SpreadMap toSpreadMap(List self) {
- if (self == null)
- throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because it is null.");
- else if (self.size() % 2 != 0)
- throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because its size is not even.");
- else
- return new SpreadMap(self);
- }
-
- /**
- * Creates a spreadable map from this iterable.
- * <p>
- * @param self an iterable
- * @return a newly created SpreadMap
- * @see groovy.lang.SpreadMap#SpreadMap(java.util.List)
- * @see #toSpreadMap(java.util.Map)
+ * @param self a List
+ * @param mutate false will always cause a new List to be created, true will mutate List in place
+ * @param comparator a Comparator
+ * @return self the List without duplicates
* @since 2.4.0
*/
- @SuppressWarnings("unchecked")
- public static SpreadMap toSpreadMap(Iterable self) {
- if (self == null)
- throw new GroovyRuntimeException("Fail to convert Iterable to SpreadMap, because it is null.");
- else
- return toSpreadMap(asList(self));
+ public static <T> List<T> unique(List<T> self, boolean mutate, Comparator<? super T> comparator) {
+ return (List<T>) unique((Collection<T>) self, mutate, comparator);
}
/**
- * Wraps a map using the decorator pattern with a wrapper that intercepts all calls
- * to <code>get(key)</code>. If an unknown key is found, a default value will be
- * stored into the Map before being returned. The default value stored will be the
- * result of calling the supplied Closure with the key as the parameter to the Closure.
- *
- * Example usage:
+ * Returns an iterator equivalent to this iterator but with all duplicated items
+ * removed where duplicate (equal) items are deduced by calling the supplied Closure condition.
+ * <p>
+ * If the supplied Closure takes a single parameter, the argument passed will be each element,
+ * and the closure should return a value used for comparison (either using
+ * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+ * If the closure takes two parameters, two items from the Iterator
+ * will be passed as arguments, and the closure should return an
+ * int value (with 0 indicating the items are not unique).
* <pre class="groovyTestCase">
- * def map = [a:1, b:2].withDefault{ k {@code ->} k.toCharacter().isLowerCase() ? 10 : -10 }
- * def expected = [a:1, b:2, c:10, D:-10]
- * assert expected.every{ e {@code ->} e.value == map[e.key] }
- *
- * def constMap = [:].withDefault{ 42 }
- * assert constMap.foo == 42
- * assert constMap.size() == 1
+ * def items = "Hello".toList() + [null, null] + "there".toList()
+ * def toLower = { it == null ? null : it.toLowerCase() }
+ * def noDups = items.iterator().toUnique(toLower).toList()
+ * assert noDups == ['H', 'e', 'l', 'o', null, 't', 'r']
* </pre>
+ * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].toUnique { it % 2 }</pre>
+ * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].toUnique { a, b {@code ->} a {@code <=>} b }</pre>
*
- * @param self a Map
- * @param init a Closure which is passed the unknown key
- * @return the wrapped Map
- * @since 1.7.1
- * @see #withDefault(Map, boolean, boolean, Closure)
+ * @param self an Iterator
+ * @param condition a Closure used to determine unique items
+ * @return an Iterator with no duplicate items
+ * @since 2.4.0
*/
- public static <K, V> Map<K, V> withDefault(Map<K, V> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> init) {
- return MapWithDefault.newInstance(self, true, false, init);
+ public static <T> Iterator<T> toUnique(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+ return toUnique(self, condition.getMaximumNumberOfParameters() == 1
+ ? new OrderBy<>(condition, true)
+ : new ClosureComparator<>(condition));
}
- /**
- * Wraps a map using the decorator pattern with a wrapper that intercepts all calls
- * to <code>get(key)</code> and <code>put(key, value)</code>.
- * If an unknown key is found for <code>get</code>, a default value will be returned.
- * The default value will be the result of calling the supplied Closure with the key
- * as the parameter to the Closure.
- * If <code>autoGrow</code> is set, the value will be stored into the map.
- *
- * If <code>autoShrink</code> is set, then an attempt to <code>put</code> the default value
- * into the map is ignored and indeed any existing value would be removed.
- *
- * If you wish the backing map to be as small as possible, consider setting <code>autoGrow</code>
- * to <code>false</code> and <code>autoShrink</code> to <code>true</code>.
- * This keeps the backing map as small as possible, i.e. sparse, but also means that
- * <code>containsKey</code>, <code>keySet</code>, <code>size</code>, and other methods
- * will only reflect the sparse values.
- *
- * If you are wrapping an immutable map, you should set <code>autoGrow</code>
- * and <code>autoShrink</code> to <code>false</code>.
- * In this scenario, the <code>get</code> method is essentially a shorthand
- * for calling <code>getOrDefault</code> with the default value supplied once as a Closure.
- *
- * Example usage:
- * <pre class="groovyTestCase">
- * // sparse map example
- * def answers = [life: 100].withDefault(false, true){ 42 }
- * assert answers.size() == 1
- * assert answers.foo == 42
- * assert answers.size() == 1
- * answers.life = 42
- * answers.putAll(universe: 42, everything: 42)
- * assert answers.size() == 0
- *
- * // immutable map example
- * def certainties = [death: true, taxes: true].asImmutable().withDefault(false, false){ false }
- * assert certainties.size() == 2
- * assert certainties.wealth == false
- * assert certainties.size() == 2
- * </pre>
+ private static final class ToUniqueIterator<E> implements Iterator<E> {
+ private final Iterator<E> delegate;
+ private final Set<E> seen;
+ private boolean exhausted;
+ private E next;
+
+ private ToUniqueIterator(Iterator<E> delegate, Comparator<? super E> comparator) {
+ this.seen = new TreeSet<>(comparator);
+ this.delegate = delegate;
+ advance();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !exhausted;
+ }
+
+ @Override
+ public E next() {
+ if (exhausted) throw new NoSuchElementException();
+ E result = next;
+ advance();
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ if (exhausted) throw new NoSuchElementException();
+ delegate.remove();
+ }
+
+ private void advance() {
+ boolean foundNext = false;
+ while (!foundNext && !exhausted) {
+ exhausted = !delegate.hasNext();
+ if (!exhausted) {
+ next = delegate.next();
+ foundNext = seen.add(next);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an iterator equivalent to this iterator with all duplicated
+ * items removed by using the supplied comparator.
*
- * @param self a Map
- * @param autoGrow whether calling get could potentially grow the map if the key isn't found
- * @param autoShrink whether calling put with the default value could potentially shrink the map
- * @param init a Closure which is passed the unknown key
- * @return the wrapped Map
- * @since 4.0.1
+ * @param self an Iterator
+ * @param comparator a Comparator used to determine unique (equal) items
+ * If {@code null}, the Comparable natural ordering of the elements will be used.
+ * @return an Iterator with no duplicate items
+ * @since 2.4.0
*/
- public static <K, V> Map<K, V> withDefault(Map<K, V> self, boolean autoGrow, boolean autoShrink, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> init) {
- return MapWithDefault.newInstance(self, autoGrow, autoShrink, init);
+ public static <T> Iterator<T> toUnique(Iterator<T> self, Comparator<? super T> comparator) {
+ return new ToUniqueIterator<>(self, comparator);
}
/**
- * An alias for <code>withLazyDefault</code> which decorates a list allowing
- * it to grow when called with index values outside the normal list bounds.
+ * Returns an iterator equivalent to this iterator with all duplicated
+ * items removed by using the natural ordering of the items.
*
- * @param self a List
- * @param init a Closure with the target index as parameter which generates the default value
- * @return the decorated List
- * @see #withLazyDefault(java.util.List, groovy.lang.Closure)
- * @see #withEagerDefault(java.util.List, groovy.lang.Closure)
- * @since 1.8.7
+ * @param self an Iterator
+ * @return an Iterator with no duplicate items
+ * @since 2.4.0
*/
- public static <T> ListWithDefault<T> withDefault(List<T> self, @ClosureParams(value=SimpleType.class, options = "int") Closure<T> init) {
- return withLazyDefault(self, init);
+ public static <T> Iterator<T> toUnique(Iterator<T> self) {
+ return toUnique(self, (Comparator<T>) null);
}
/**
- * Decorates a list allowing it to grow when called with a non-existent index value.
- * When called with such values, the list is grown in size and a default value
- * is placed in the list by calling a supplied <code>init</code> Closure. Subsequent
- * retrieval operations if finding a null value in the list assume it was set
- * as null from an earlier growing operation and again call the <code>init</code> Closure
- * to populate the retrieved value; consequently the list can't be used to store null values.
- * <p>
- * How it works: The decorated list intercepts all calls
- * to <code>getAt(index)</code> and <code>get(index)</code>. If an index greater than
- * or equal to the current <code>size()</code> is used, the list will grow automatically
- * up to the specified index. Gaps will be filled by {@code null}. If a default value
- * should also be used to fill gaps instead of {@code null}, use <code>withEagerDefault</code>.
- * If <code>getAt(index)</code> or <code>get(index)</code> are called and a null value
- * is found, it is assumed that the null value was a consequence of an earlier grow list
- * operation and the <code>init</code> Closure is called to populate the value.
+ * Returns a Collection containing the items from the Iterable but with duplicates removed.
+ * The items in the Iterable are compared by the given Comparator.
+ * For each duplicate, the first member which is returned from the
+ * Iterable is retained, but all other ones are removed.
* <p>
- * Example usage:
* <pre class="groovyTestCase">
- * def list = [0, 1].withLazyDefault{ 42 }
- * assert list[0] == 0
- * assert list[1] == 1
- * assert list[3] == 42 // default value
- * assert list == [0, 1, null, 42] // gap filled with null
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
*
- * // illustrate using the index when generating default values
- * def list2 = [5].withLazyDefault{ index {@code ->} index * index }
- * assert list2[3] == 9
- * assert list2 == [5, null, null, 9]
- * assert list2[2] == 4
- * assert list2 == [5, null, 4, 9]
+ * class PersonComparator implements Comparator {
+ * int compare(Object o1, Object o2) {
+ * Person p1 = (Person) o1
+ * Person p2 = (Person) o2
+ * if (p1.lname != p2.lname)
+ * return p1.lname.compareTo(p2.lname)
+ * else
+ * return p1.fname.compareTo(p2.fname)
+ * }
*
- * // illustrate what happens with null values
- * list2[2] = null
- * assert list2[2] == 4
+ * boolean equals(Object obj) {
+ * return this.equals(obj)
+ * }
+ * }
+ *
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
+ *
+ * def list = [a, b, c, d]
+ * List list2 = list.toUnique(new PersonComparator())
+ * assert list2 == [a, b, c] {@code &&} list == [a, b, c, d]
* </pre>
*
- * @param self a List
- * @param init a Closure with the target index as parameter which generates the default value
- * @return the decorated List
- * @since 1.8.7
+ * @param self an Iterable
+ * @param comparator a Comparator used to determine unique (equal) items
+ * If {@code null}, the Comparable natural ordering of the elements will be used.
+ * @return the Collection of non-duplicate items
+ * @since 2.4.0
*/
- public static <T> ListWithDefault<T> withLazyDefault(List<T> self, @ClosureParams(value=SimpleType.class, options="int") Closure<T> init) {
- return ListWithDefault.newInstance(self, true, init);
+ public static <T> Collection<T> toUnique(Iterable<T> self, Comparator<? super T> comparator) {
+ Collection<T> result = createSimilarCollection(self);
+ addAll(result, toUnique(self.iterator(), comparator));
+ return result;
}
/**
- * Decorates a list allowing it to grow when called with a non-existent index value.
- * When called with such values, the list is grown in size and a default value
- * is placed in the list by calling a supplied <code>init</code> Closure. Null values
- * can be stored in the list.
- * <p>
- * How it works: The decorated list intercepts all calls
- * to <code>getAt(index)</code> and <code>get(index)</code>. If an index greater than
- * or equal to the current <code>size()</code> is used, the list will grow automatically
- * up to the specified index. Gaps will be filled by calling the <code>init</code> Closure.
- * If generating a default value is a costly operation consider using <code>withLazyDefault</code>.
+ * Returns a List containing the items from the List but with duplicates removed.
+ * The items in the List are compared by the given Comparator.
+ * For each duplicate, the first member which is returned from the
+ * List is retained, but all other ones are removed.
* <p>
- * Example usage:
* <pre class="groovyTestCase">
- * def list = [0, 1].withEagerDefault{ 42 }
- * assert list[0] == 0
- * assert list[1] == 1
- * assert list[3] == 42 // default value
- * assert list == [0, 1, 42, 42] // gap filled with default value
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
*
- * // illustrate using the index when generating default values
- * def list2 = [5].withEagerDefault{ index {@code ->} index * index }
- * assert list2[3] == 9
- * assert list2 == [5, 1, 4, 9]
+ * class PersonComparator implements Comparator {
+ * int compare(Object o1, Object o2) {
+ * Person p1 = (Person) o1
+ * Person p2 = (Person) o2
+ * if (p1.lname != p2.lname)
+ * return p1.lname.compareTo(p2.lname)
+ * else
+ * return p1.fname.compareTo(p2.fname)
+ * }
*
- * // illustrate what happens with null values
- * list2[2] = null
- * assert list2[2] == null
- * assert list2 == [5, 1, null, 9]
+ * boolean equals(Object obj) {
+ * return this.equals(obj)
+ * }
+ * }
+ *
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
+ *
+ * def list = [a, b, c, d]
+ * List list2 = list.toUnique(new PersonComparator())
+ * assert list2 == [a, b, c] {@code &&} list == [a, b, c, d]
* </pre>
*
- * @param self a List
- * @param init a Closure with the target index as parameter which generates the default value
- * @return the wrapped List
- * @since 1.8.7
+ * @param self a List
+ * @param comparator a Comparator used to determine unique (equal) items
+ * If {@code null}, the Comparable natural ordering of the elements will be used.
+ * @return the List of non-duplicate items
+ * @since 2.4.0
*/
- public static <T> ListWithDefault<T> withEagerDefault(List<T> self, @ClosureParams(value=SimpleType.class, options="int") Closure<T> init) {
- return ListWithDefault.newInstance(self, false, init);
+ public static <T> List<T> toUnique(List<T> self, Comparator<? super T> comparator) {
+ return (List<T>) toUnique((Iterable<T>) self, comparator);
}
/**
- * Zips an Iterable with indices in (value, index) order.
- * <p/>
- * Example usage:
+ * Returns a Collection containing the items from the Iterable but with duplicates removed
+ * using the natural ordering of the items to determine uniqueness.
+ * <p>
* <pre class="groovyTestCase">
- * assert [["a", 0], ["b", 1]] == ["a", "b"].withIndex()
- * assert ["0: a", "1: b"] == ["a", "b"].withIndex().collect { str, idx {@code ->} "$idx: $str" }
+ * String[] letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
+ * String[] expected = ['c', 'a', 't', 's', 'h']
+ * assert letters.toUnique() == expected
* </pre>
*
- * @param self an Iterable
- * @return a zipped list with indices
- * @see #indexed(Iterable)
+ * @param self an Iterable
+ * @return the Collection of non-duplicate items
* @since 2.4.0
*/
- public static <E> List<Tuple2<E, Integer>> withIndex(Iterable<E> self) {
- return withIndex(self, 0);
+ public static <T> Collection<T> toUnique(Iterable<T> self) {
+ return toUnique(self, (Comparator<T>) null);
}
/**
- * Zips an Iterable with indices in (value, index) order.
- * <p/>
- * Example usage:
+ * Returns a List containing the items from the List but with duplicates removed
+ * using the natural ordering of the items to determine uniqueness.
+ * <p>
* <pre class="groovyTestCase">
- * assert [["a", 5], ["b", 6]] == ["a", "b"].withIndex(5)
- * assert ["1: a", "2: b"] == ["a", "b"].withIndex(1).collect { str, idx {@code ->} "$idx: $str" }
+ * def letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
+ * def expected = ['c', 'a', 't', 's', 'h']
+ * assert letters.toUnique() == expected
+ * </pre>
+ *
+ * @param self a List
+ * @return the List of non-duplicate items
+ * @since 2.4.0
+ */
+ public static <T> List<T> toUnique(List<T> self) {
+ return toUnique(self, (Comparator<T>) null);
+ }
+
+ /**
+ * Returns a Collection containing the items from the Iterable but with duplicates removed.
+ * The items in the Iterable are compared by the given Closure condition.
+ * For each duplicate, the first member which is returned from the
+ * Iterable is retained, but all other ones are removed.
+ * <p>
+ * If the closure takes a single parameter, each element from the Iterable will be passed to the closure. The closure
+ * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+ * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the Iterable
+ * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+ * <p>
+ * <pre class="groovyTestCase">
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
+ *
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
+ *
+ * def list = [a, b, c, d]
+ * def list2 = list.toUnique{ p1, p2 {@code ->} p1.lname != p2.lname ? p1.lname <=> p2.lname : p1.fname <=> p2.fname }
+ * assert( list2 == [a, b, c] {@code &&} list == [a, b, c, d] )
+ * def list3 = list.toUnique{ it.toString() }
+ * assert( list3 == [a, b, c] {@code &&} list == [a, b, c, d] )
* </pre>
*
- * @param self an Iterable
- * @param offset an index to start from
- * @return a zipped list with indices
- * @see #indexed(Iterable, int)
+ * @param self an Iterable
+ * @param condition a Closure used to determine unique items
+ * @return a new Collection
+ * @see #toUnique(Iterable, Comparator)
* @since 2.4.0
*/
- public static <E> List<Tuple2<E, Integer>> withIndex(Iterable<E> self, int offset) {
- return toList(withIndex(self.iterator(), offset));
+ public static <T> Collection<T> toUnique(Iterable<T> self, @ClosureParams(value = FromString.class, options = {"T", "T,T"}) Closure condition) {
+ Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+ ? new OrderBy<>(condition, true)
+ : new ClosureComparator<>(condition);
+ return toUnique(self, comparator);
}
/**
- * Zips an iterator with indices in (value, index) order.
- * <p/>
- * Example usage:
+ * Returns a List containing the items from the List but with duplicates removed.
+ * The items in the List are compared by the given Closure condition.
+ * For each duplicate, the first member which is returned from the
+ * Iterable is retained, but all other ones are removed.
+ * <p>
+ * If the closure takes a single parameter, each element from the Iterable will be passed to the closure. The closure
+ * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+ * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the Iterable
+ * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+ * <p>
* <pre class="groovyTestCase">
- * assert [["a", 0], ["b", 1]] == ["a", "b"].iterator().withIndex().toList()
- * assert ["0: a", "1: b"] == ["a", "b"].iterator().withIndex().collect { str, idx {@code ->} "$idx: $str" }.toList()
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
+ *
+ * Person a = new Person(fname:"John", lname:"Taylor")
+ * Person b = new Person(fname:"Clark", lname:"Taylor")
+ * Person c = new Person(fname:"Tom", lname:"Cruz")
+ * Person d = new Person(fname:"Clark", lname:"Taylor")
+ *
+ * def list = [a, b, c, d]
+ * def list2 = list.toUnique{ p1, p2 {@code ->} p1.lname != p2.lname ? p1.lname <=> p2.lname : p1.fname <=> p2.fname }
+ * assert( list2 == [a, b, c] {@code &&} list == [a, b, c, d] )
+ * def list3 = list.toUnique{ it.toString() }
+ * assert( list3 == [a, b, c] {@code &&} list == [a, b, c, d] )
* </pre>
*
- * @param self an iterator
- * @return a zipped iterator with indices
- * @see #indexed(Iterator)
+ * @param self a List
+ * @param condition a Closure used to determine unique items
+ * @return a new List
+ * @see #toUnique(Iterable, Comparator)
* @since 2.4.0
*/
- public static <E> Iterator<Tuple2<E, Integer>> withIndex(Iterator<E> self) {
- return withIndex(self, 0);
+ public static <T> List<T> toUnique(List<T> self, @ClosureParams(value = FromString.class, options = {"T", "T,T"}) Closure condition) {
+ return (List<T>) toUnique((Iterable<T>) self, condition);
}
/**
- * Zips an iterator with indices in (value, index) order.
- * <p/>
- * Example usage:
- * <pre class="groovyTestCase">
- * assert [["a", 5], ["b", 6]] == ["a", "b"].iterator().withIndex(5).toList()
- * assert ["1: a", "2: b"] == ["a", "b"].iterator().withIndex(1).collect { str, idx {@code ->} "$idx: $str" }.toList()
- * </pre>
+ * Allows a Map to be iterated through in reverse order using a closure.
*
- * @param self an iterator
- * @param offset an index to start from
- * @return a zipped iterator with indices
- * @see #indexed(Iterator, int)
- * @since 2.4.0
+ * In general, the order in which the map contents are processed
+ * cannot be guaranteed. In practise, specialized forms of Map,
+ * e.g. a TreeMap will have its contents processed according to the
+ * reverse of the natural ordering of the map.
+ *
+ * @param self the map over which we iterate
+ * @param closure the 1 or 2 arg closure applied on each entry of the map
+ * @return returns the self parameter
+ * @see #each(Map, Closure)
+ * @since 1.7.2
*/
- public static <E> Iterator<Tuple2<E, Integer>> withIndex(Iterator<E> self, int offset) {
- return new ZipPostIterator<>(self, offset);
- }
-
- private static final class ZipPostIterator<E> implements Iterator<Tuple2<E, Integer>> {
- private final Iterator<E> delegate;
- private int index;
-
- private ZipPostIterator(Iterator<E> delegate, int offset) {
- this.delegate = delegate;
- this.index = offset;
- }
-
- @Override
- public boolean hasNext() {
- return delegate.hasNext();
- }
-
- @Override
- public Tuple2<E, Integer> next() {
- if (!hasNext()) throw new NoSuchElementException();
- return new Tuple2<>(delegate.next(), index++);
- }
-
- @Override
- public void remove() {
- delegate.remove();
+ @SuppressWarnings("unchecked")
+ public static <K, V> Map<K, V> reverseEach(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<?> closure) {
+ final Iterator<Map.Entry<K, V>> entries = reverse(self.entrySet().iterator());
+ while (entries.hasNext()) {
+ callClosureForMapEntry(closure, entries.next());
}
+ return self;
}
/**
- * Sorts the Collection. Assumes that the collection items are comparable
- * and uses their natural ordering to determine the resulting order.
- * If the Collection is a List, it is sorted in place and returned.
- * Otherwise, the elements are first placed into a new list which is then
- * sorted and returned - leaving the original Collection unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [3,1,2].sort()</pre>
+ * Iterate over each element of the list in the reverse order.
+ * <pre class="groovyTestCase">def result = []
+ * [1,2,3].reverseEach { result << it }
+ * assert result == [3,2,1]</pre>
*
- * @param self the Iterable to be sorted
- * @return the sorted Iterable as a List
- * @see #sort(Iterable, boolean)
- * @since 2.2.0
+ * @param self a List
+ * @param closure a closure to which each item is passed.
+ * @return the original list
+ * @since 1.5.0
*/
- public static <T> List<T> sort(Iterable<T> self) {
- return sort(self, true);
+ public static <T> List<T> reverseEach(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ each(new ReverseListIterator<>(self), closure);
+ return self;
}
/**
- * Sorts the Iterable. Assumes that the Iterable items are
- * comparable and uses their natural ordering to determine the resulting order.
- * If the Iterable is a List and mutate is true,
- * it is sorted in place and returned. Otherwise, the elements are first placed
- * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [3,1,2].sort()</pre>
+ * Iterate over each element of the set in reverse order.
* <pre class="groovyTestCase">
- * def orig = [1, 3, 2]
- * def sorted = orig.sort(false)
- * assert orig == [1, 3, 2]
- * assert sorted == [1, 2, 3]
+ * TreeSet navSet = [2, 4, 1, 3] // natural order is sorted
+ * List result = []
+ * navSet.reverseEach { result << it }
+ * assert result == [4, 3, 2, 1]
* </pre>
*
- * @param self the iterable to be sorted
- * @param mutate false will always cause a new list to be created, true will mutate lists in place
- * @return the sorted iterable as a List
- * @since 2.2.0
+ * @param self a NavigableSet
+ * @param closure a closure to which each item is passed.
+ * @return the original NavigableSet
+ * @since 4.0.11
*/
- public static <T> List<T> sort(Iterable<T> self, boolean mutate) {
- List<T> answer = mutate ? asList(self) : toList(self);
- answer.sort(new NumberAwareComparator<>());
- return answer;
+ public static <T> NavigableSet<T> reverseEach(NavigableSet<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ each(self.descendingIterator(), closure);
+ return self;
}
/**
- * Sorts the elements from the given map into a new ordered map using
- * the closure as a comparator to determine the ordering.
- * The original map is unchanged.
- * <pre class="groovyTestCase">def map = [a:5, b:3, c:6, d:4].sort { a, b {@code ->} a.value {@code <=>} b.value }
- * assert map == [b:3, d:4, a:5, c:6]</pre>
+ * Convert an iterator to a List. The iterator will become
+ * exhausted of elements after making this conversion.
*
- * @param self the original unsorted map
- * @param closure a Closure used as a comparator
- * @return the sorted map
- * @since 1.6.0
+ * @param self an iterator
+ * @return a List
+ * @since 1.5.0
*/
- public static <K, V> Map<K, V> sort(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>","Map.Entry<K,V>,Map.Entry<K,V>"}) Closure closure) {
- Map<K, V> result = new LinkedHashMap<>();
- List<Map.Entry<K, V>> entries = asList(self.entrySet());
- sort((Iterable<Map.Entry<K, V>>) entries, closure);
- for (Map.Entry<K, V> entry : entries) {
- result.put(entry.getKey(), entry.getValue());
+ public static <T> List<T> toList(Iterator<T> self) {
+
+ List<T> answer = new ArrayList<>();
+ while (self.hasNext()) {
+ answer.add(self.next());
}
- return result;
+ return answer;
}
/**
- * Sorts the elements from the given map into a new ordered Map using
- * the specified key comparator to determine the ordering.
- * The original map is unchanged.
- * <pre class="groovyTestCase">def map = [ba:3, cz:6, ab:5].sort({ a, b {@code ->} a[-1] {@code <=>} b[-1] } as Comparator)
- * assert map*.value == [3, 5, 6]</pre>
+ * Convert an Iterable to a List. The Iterable's iterator will
+ * become exhausted of elements after making this conversion.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">def x = [1,2,3] as HashSet
+ * assert x.class == HashSet
+ * assert x.toList() instanceof List</pre>
*
- * @param self the original unsorted map
- * @param comparator a Comparator
- * @return the sorted map
- * @since 1.7.2
+ * @param self an Iterable
+ * @return a List
+ * @since 1.8.7
*/
- public static <K, V> Map<K, V> sort(Map<K, V> self, Comparator<? super K> comparator) {
- Map<K, V> result = new TreeMap<>(comparator);
- result.putAll(self);
- return result;
+ public static <T> List<T> toList(Iterable<T> self) {
+ return toList(self.iterator());
}
/**
- * Sorts the elements from the given map into a new ordered Map using
- * the natural ordering of the keys to determine the ordering.
- * The original map is unchanged.
- * <pre class="groovyTestCase">map = [ba:3, cz:6, ab:5].sort()
- * assert map*.value == [5, 3, 6]
- * </pre>
+ * Convert an enumeration to a List.
*
- * @param self the original unsorted map
- * @return the sorted map
- * @since 1.7.2
+ * @param self an enumeration
+ * @return a List
+ * @since 1.5.0
+ */
+ public static <T> List<T> toList(Enumeration<T> self) {
+ List<T> answer = new ArrayList<>();
+ while (self.hasMoreElements()) {
+ answer.add(self.nextElement());
+ }
+ return answer;
+ }
+
+ /**
+ * A variant of withCollectedValues for Iterators.
+ *
+ * @param keys an Iterator
+ * @param collector the Map into which the transformed entries are put
+ * @param valueTransform a function for transforming Iterator elements into values
+ * @return the collector with all transformed values added to it
+ * @see #withCollectedValues(Iterable, Map, Function)
+ * @since 5.0.0
*/
- public static <K, V> Map<K, V> sort(Map<K, V> self) {
- return new TreeMap<>(self);
+ public static <K, V> Map<K, V> withCollectedValues(Iterator<K> keys, Map<K, V> collector, Function<? super K, V> valueTransform) {
+ return collectEntries(keys, collector, Function.identity(), valueTransform);
}
/**
- * Sorts the given iterator items into a sorted iterator. The items are
- * assumed to be comparable. The original iterator will become
- * exhausted of elements after completing this method call.
- * A new iterator is produced that traverses the items in sorted order.
+ * A variant of withCollectedValues for Iterators.
*
- * @param self the Iterator to be sorted
- * @return the sorted items as an Iterator
- * @since 1.5.5
+ * @param keys an Iterator
+ * @param valueTransform a function for transforming Iterator elements into values
+ * @return a Map of the transformed entries
+ * @see #withCollectedValues(Iterable, Function)
+ * @since 5.0.0
*/
- public static <T> Iterator<T> sort(Iterator<T> self) {
- return sort((Iterable<T>) toList(self)).listIterator();
+ public static <K, V> Map<K, V> withCollectedValues(Iterator<K> keys, Function<? super K, V> valueTransform) {
+ return withCollectedValues(keys, new LinkedHashMap<>(), valueTransform);
}
/**
- * Sorts the given iterator items into a sorted iterator using the comparator. The
- * original iterator will become exhausted of elements after completing this method call.
- * A new iterator is produced that traverses the items in sorted order.
+ * Transform Iterable elements into Map entries with keys unchanged
+ * and values transformed using the supplied function.
+ * The supplied collector map is used as the destination for transformed entries.
+ * <pre class="groovyTestCase">
+ * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
+ * assert languages.withCollectedValues([Clojure:7], String::size) ==
+ * [Clojure:7, Groovy:6, Java:4, Kotlin:6, Scala:5]
+ * </pre>
+ * This method is a convenience method for {@link #collectEntries(Iterable, Map, Function, Function)}
+ * with the {@code keyTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
*
- * @param self the Iterator to be sorted
- * @param comparator a Comparator used for comparing items
- * @return the sorted items as an Iterator
- * @since 1.5.5
+ * @param keys an Iterable
+ * @param collector the Map into which the transformed entries are put
+ * @param valueTransform a function for transforming Iterable elements into values
+ * @return the collector with all transformed values added to it
+ * @since 5.0.0
*/
- public static <T> Iterator<T> sort(Iterator<T> self, Comparator<? super T> comparator) {
- return sort(toList(self), true, comparator).listIterator();
+ public static <K, V> Map<K, V> withCollectedValues(Iterable<K> keys, Map<K, V> collector, Function<? super K, V> valueTransform) {
+ return collectEntries(keys.iterator(), collector, Function.identity(), valueTransform);
}
/**
- * Sorts the Iterable using the given Comparator. If the Iterable is a List and mutate
- * is true, it is sorted in place and returned. Otherwise, the elements are first placed
- * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
- * <pre class="groovyTestCase">
- * assert ["hi","hey","hello"] == ["hello","hi","hey"].sort(false, { a, b {@code ->} a.length() {@code <=>} b.length() } as Comparator )
- * </pre>
+ * Transform Iterable elements into Map entries with keys unchanged
+ * and values transformed using the supplied function.
* <pre class="groovyTestCase">
- * def orig = ["hello","hi","Hey"]
- * def sorted = orig.sort(false, String.CASE_INSENSITIVE_ORDER)
- * assert orig == ["hello","hi","Hey"]
- * assert sorted == ["hello","Hey","hi"]
+ * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
+ * assert languages.withCollectedValues(String::size) ==
+ * [Groovy:6, Java:4, Kotlin:6, Scala:5]
* </pre>
+ * This method is a convenience method for {@link #collectEntries(Iterable, Function, Function)}
+ * with the {@code keyTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
*
- * @param self the Iterable to be sorted
- * @param mutate false will always cause a new list to be created, true will mutate lists in place
- * @param comparator a Comparator used for the comparison
- * @return a sorted List
- * @since 2.2.0
+ * @param keys an Iterable
+ * @param valueTransform a function for transforming Iterable elements into values
+ * @return a Map of the transformed entries
+ * @since 5.0.0
*/
- public static <T> List<T> sort(Iterable<T> self, boolean mutate, Comparator<? super T> comparator) {
- List<T> list = mutate ? asList(self) : toList(self);
- list.sort(comparator);
- return list;
+ public static <K, V> Map<K, V> withCollectedValues(Iterable<K> keys, Function<? super K, V> valueTransform) {
+ return withCollectedValues(keys.iterator(), new LinkedHashMap<>(), valueTransform);
}
/**
- * Sorts the given iterator items into a sorted iterator using the Closure to determine the correct ordering.
- * The original iterator will be fully processed after the method call.
- * <p>
- * If the closure has two parameters it is used like a traditional Comparator.
- * I.e. it should compare its two parameters for order, returning a negative integer,
- * zero, or a positive integer when the first parameter is less than, equal to,
- * or greater than the second respectively. Otherwise, the Closure is assumed
- * to take a single parameter and return a Comparable (typically an Integer)
- * which is then used for further comparison.
+ * A variant of withCollectedKeys for Iterators.
*
- * @param self the Iterator to be sorted
- * @param closure a Closure used to determine the correct ordering
- * @return the sorted items as an Iterator
- * @since 1.5.5
+ * @param values an Iterator
+ * @param collector the Map into which the transformed entries are put
+ * @param keyTransform a function for transforming Iterator elements into values
+ * @return the collector with all transformed values added to it
+ * @see #withCollectedKeys(Iterable, Map, Function)
+ * @since 5.0.0
*/
- public static <T> Iterator<T> sort(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return sort((Iterable<T>) toList(self), closure).listIterator();
+ public static <K, V> Map<K, V> withCollectedKeys(Iterator<V> values, Map<K, V> collector, Function<? super V, K> keyTransform) {
+ return collectEntries(values, collector, keyTransform, Function.identity());
}
/**
- * Sorts this Iterable using the given Closure to determine the correct ordering. If the Iterable is a List,
- * it is sorted in place and returned. Otherwise, the elements are first placed
- * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
- * <p>
- * If the Closure has two parameters
- * it is used like a traditional Comparator. I.e. it should compare
- * its two parameters for order, returning a negative integer,
- * zero, or a positive integer when the first parameter is less than,
- * equal to, or greater than the second respectively. Otherwise,
- * the Closure is assumed to take a single parameter and return a
- * Comparable (typically an Integer) which is then used for
- * further comparison.
- * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre>
- * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
+ * A variant of withCollectedKeys for Iterators.
*
- * @param self the Iterable to be sorted
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return a newly created sorted List
- * @see #sort(Iterable, boolean, Closure)
- * @since 2.2.0
+ * @param values an Iterator
+ * @param keyTransform a function for transforming Iterator elements into values
+ * @return a Map of the transformed entries
+ * @see #withCollectedKeys(Iterable, Function)
+ * @since 5.0.0
*/
- public static <T> List<T> sort(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return sort(self, true, closure);
+ public static <K, V> Map<K, V> withCollectedKeys(Iterator<V> values, Function<? super V, K> keyTransform) {
+ return withCollectedKeys(values, new LinkedHashMap<>(), keyTransform);
}
/**
- * Sorts this Iterable using the given Closure to determine the correct ordering. If the Iterable is a List
- * and mutate is true, it is sorted in place and returned. Otherwise, the elements are first placed
- * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
- * <p>
- * If the closure has two parameters
- * it is used like a traditional Comparator. I.e. it should compare
- * its two parameters for order, returning a negative integer,
- * zero, or a positive integer when the first parameter is less than,
- * equal to, or greater than the second respectively. Otherwise,
- * the Closure is assumed to take a single parameter and return a
- * Comparable (typically an Integer) which is then used for
- * further comparison.
- * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre>
- * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
+ * Transform Iterable elements into Map entries with values unchanged
+ * and keys transformed using the supplied function.
+ * The supplied map is used as the destination for transformed entries.
* <pre class="groovyTestCase">
- * def orig = ["hello","hi","Hey"]
- * def sorted = orig.sort(false) { it.toUpperCase() }
- * assert orig == ["hello","hi","Hey"]
- * assert sorted == ["hello","Hey","hi"]
+ * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
+ * def firstLetter = s {@code ->} s[0]
+ * assert languages.withCollectedKeys([C:'Clojure'], firstLetter) ==
+ * [C:'Clojure', G:'Groovy', J:'Java', K:'Kotlin', S:'Scala']
* </pre>
+ * This method is a convenience method for {@link #collectEntries(Iterable, Map, Function, Function)}
+ * with the {@code valueTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
*
- * @param self the Iterable to be sorted
- * @param mutate false will always cause a new list to be created, true will mutate lists in place
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return a newly created sorted List
- * @since 2.2.0
+ * @param values an Iterable that will become the added entry values
+ * @param collector the Map into which the transformed entries are put
+ * @param keyTransform a function for transforming Iterator elements into keys
+ * @return the collector with all transformed values added to it
+ * @since 5.0.0
*/
- public static <T> List<T> sort(Iterable<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- List<T> list = mutate ? asList(self) : toList(self);
- // use a comparator of one item or two
- int params = closure.getMaximumNumberOfParameters();
- if (params == 1) {
- list.sort(new OrderBy<>(closure));
- } else {
- list.sort(new ClosureComparator<>(closure));
- }
- return list;
+ public static <K, V> Map<K, V> withCollectedKeys(Iterable<V> values, Map<K, V> collector, Function<? super V, K> keyTransform) {
+ return collectEntries(values.iterator(), collector, keyTransform, Function.identity());
}
/**
- * Avoids doing unnecessary work when sorting an already sorted set (i.e. an identity function for an already sorted set).
+ * Transform Iterable elements into Map entries with values unchanged
+ * and keys transformed using the supplied function.
+ * <pre class="groovyTestCase">
+ * def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']
+ * def firstLetter = s {@code ->} s[0]
+ * assert languages.withCollectedKeys(firstLetter) ==
+ * [G:'Groovy', J:'Java', K:'Kotlin', S:'Scala']
+ * </pre>
+ * This method is a convenience method for {@link #collectEntries(Iterable, Function, Function)}
+ * with the {@code valueTransform} replaced by {@code Function.identity()} or {Closure.IDENTITY}.
*
- * @param self an already sorted set
- * @return the set
- * @since 1.0
+ * @param values an Iterable that will become the added entry values
+ * @param keyTransform a function for transforming Iterator elements into keys
+ * @return the collector with all transformed values added to it
+ * @since 5.0.0
*/
- public static <T> SortedSet<T> sort(SortedSet<T> self) {
- return self;
+ public static <K, V> Map<K, V> withCollectedKeys(Iterable<V> values, Function<? super V, K> keyTransform) {
+ return withCollectedKeys(values.iterator(), new LinkedHashMap<>(), keyTransform);
}
/**
- * Avoids doing unnecessary work when sorting an already sorted map (i.e. an identity function for an already sorted map).
+ * Modifies this collection by removing its elements that are contained
+ * within the specified object array.
*
- * @param self an already sorted map
- * @return the map
- * @since 1.8.1
+ * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list
+ * containing items which don't match some criteria while leaving the original collection unchanged.
+ *
+ * @param self a Collection to be modified
+ * @param items array containing elements to be removed from this collection
+ * @return <tt>true</tt> if this collection changed as a result of the call
+ * @see Collection#removeAll(Collection)
+ * @since 1.7.2
*/
- public static <K, V> SortedMap<K, V> sort(SortedMap<K, V> self) {
- return self;
+ @SuppressWarnings("unchecked")
+ public static boolean removeAll(Collection self, Object[] items) {
+ Collection pickFrom = new TreeSet(new NumberAwareComparator());
+ pickFrom.addAll(Arrays.asList(items));
+ return self.removeAll(pickFrom);
}
/**
- * Sorts the Iterable. Assumes that the Iterable elements are
- * comparable and uses a {@link NumberAwareComparator} to determine the resulting order.
- * {@code NumberAwareComparator} has special treatment for numbers but otherwise uses the
- * natural ordering of the Iterable elements. The elements are first placed into a new list which
- * is then sorted and returned - leaving the original Iterable unchanged.
- * <pre class="groovyTestCase">
- * def orig = [1, 3, 2]
- * def sorted = orig.toSorted()
- * assert orig == [1, 3, 2]
- * assert sorted == [1, 2, 3]
- * </pre>
+ * Modifies this collection so that it retains only its elements that are contained
+ * in the specified array. In other words, removes from this collection all of
+ * its elements that are not contained in the specified array.
+ *
+ * See also <code>grep</code> and <code>findAll</code> when wanting to produce a new list
+ * containing items which match some specified items but leaving the original collection unchanged.
+ *
+ * @param self a Collection to be modified
+ * @param items array containing elements to be retained from this collection
+ * @return <tt>true</tt> if this collection changed as a result of the call
+ * @see Collection#retainAll(Collection)
+ * @since 1.7.2
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean retainAll(Collection self, Object[] items) {
+ Collection pickFrom = new TreeSet(new NumberAwareComparator());
+ pickFrom.addAll(Arrays.asList(items));
+ return self.retainAll(pickFrom);
+ }
+
+ /**
+ * Modifies this collection so that it retains only its elements
+ * that are matched according to the specified closure condition. In other words,
+ * removes from this collection all of its elements that don't match.
*
- * @param self the Iterable to be sorted
- * @return the sorted iterable as a List
- * @see #toSorted(Iterable, Comparator)
- * @since 2.4.0
+ * <pre class="groovyTestCase">def list = ['a', 'b']
+ * list.retainAll { it == 'b' }
+ * assert list == ['b']</pre>
+ *
+ * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list
+ * containing items which match some criteria but leaving the original collection unchanged.
+ *
+ * @param self a Collection to be modified
+ * @param condition a closure condition
+ * @return <tt>true</tt> if this collection changed as a result of the call
+ * @see Iterator#remove()
+ * @since 1.7.2
*/
- public static <T> List<T> toSorted(Iterable<T> self) {
- return toSorted(self, new NumberAwareComparator<>());
+ public static <T> boolean retainAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ Iterator iter = InvokerHelper.asIterator(self);
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ boolean result = false;
+ while (iter.hasNext()) {
+ Object value = iter.next();
+ if (!bcw.call(value)) {
+ iter.remove();
+ result = true;
+ }
+ }
+ return result;
}
/**
- * Sorts the Iterable using the given Comparator. The elements are first placed
- * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
- * <pre class="groovyTestCase">
- * def orig = ["hello","hi","Hey"]
- * def sorted = orig.toSorted(String.CASE_INSENSITIVE_ORDER)
- * assert orig == ["hello","hi","Hey"]
- * assert sorted == ["hello","Hey","hi"]
- * </pre>
+ * Modifies this map so that it retains only its elements that are matched
+ * according to the specified closure condition. In other words, removes from
+ * this map all of its elements that don't match. If the closure takes one
+ * parameter then it will be passed the <code>Map.Entry</code>. Otherwise the closure should
+ * take two parameters, which will be the key and the value.
*
- * @param self the Iterable to be sorted
- * @param comparator a Comparator used for the comparison
- * @return a sorted List
- * @since 2.4.0
+ * <pre class="groovyTestCase">def map = [a:1, b:2]
+ * map.retainAll { k,v {@code ->} k == 'b' }
+ * assert map == [b:2]</pre>
+ *
+ * See also <code>findAll</code> when wanting to produce a new map containing items
+ * which match some criteria but leaving the original map unchanged.
+ *
+ * @param self a Map to be modified
+ * @param condition a 1 or 2 arg Closure condition applying on the entries
+ * @return <tt>true</tt> if this map changed as a result of the call
+ * @since 2.5.0
*/
- public static <T> List<T> toSorted(Iterable<T> self, Comparator<? super T> comparator) {
- List<T> list = toList(self);
- list.sort(comparator);
- return list;
+ public static <K, V> boolean retainAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
+ Iterator<Map.Entry<K, V>> iter = self.entrySet().iterator();
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ boolean result = false;
+ while (iter.hasNext()) {
+ Map.Entry<K, V> entry = iter.next();
+ if (!bcw.callForMap(entry)) {
+ iter.remove();
+ result = true;
+ }
+ }
+ return result;
}
/**
- * Sorts this Iterable using the given Closure to determine the correct ordering. The elements are first placed
- * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
- * <p>
- * If the Closure has two parameters
- * it is used like a traditional Comparator. I.e. it should compare
- * its two parameters for order, returning a negative integer,
- * zero, or a positive integer when the first parameter is less than,
- * equal to, or greater than the second respectively. Otherwise,
- * the Closure is assumed to take a single parameter and return a
- * Comparable (typically an Integer) which is then used for
- * further comparison.
- * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre>
- * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
+ * Modifies this collection by removing the elements that are matched according
+ * to the specified closure condition.
*
- * @param self the Iterable to be sorted
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return a newly created sorted List
- * @see #toSorted(Iterable, Comparator)
- * @since 2.4.0
+ * <pre class="groovyTestCase">def list = ['a', 'b']
+ * list.removeAll { it == 'b' }
+ * assert list == ['a']</pre>
+ *
+ * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list
+ * containing items which match some criteria but leaving the original collection unchanged.
+ *
+ * @param self a Collection to be modified
+ * @param condition a closure condition
+ * @return <tt>true</tt> if this collection changed as a result of the call
+ * @see Iterator#remove()
+ * @since 1.7.2
*/
- public static <T> List<T> toSorted(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(
- closure);
- return toSorted(self, comparator);
+ public static <T> boolean removeAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return self.removeIf(new BooleanClosurePredicate<>(condition));
}
/**
- * Sorts the Iterator. Assumes that the Iterator elements are
- * comparable and uses a {@link NumberAwareComparator} to determine the resulting order.
- * {@code NumberAwareComparator} has special treatment for numbers but otherwise uses the
- * natural ordering of the Iterator elements.
- * A new iterator is produced that traverses the items in sorted order.
+ * Modifies this map by removing the elements that are matched according to the
+ * specified closure condition. If the closure takes one parameter then it will be
+ * passed the <code>Map.Entry</code>. Otherwise the closure should take two parameters, which
+ * will be the key and the value.
*
- * @param self the Iterator to be sorted
- * @return the sorted items as an Iterator
- * @see #toSorted(Iterator, Comparator)
- * @since 2.4.0
+ * <pre class="groovyTestCase">def map = [a:1, b:2]
+ * map.removeAll { k,v {@code ->} k == 'b' }
+ * assert map == [a:1]</pre>
+ *
+ * See also <code>findAll</code> when wanting to produce a new map containing items
+ * which match some criteria but leaving the original map unchanged.
+ *
+ * @param self a Map to be modified
+ * @param condition a 1 or 2 arg Closure condition applying on the entries
+ * @return <tt>true</tt> if this map changed as a result of the call
+ * @since 2.5.0
*/
- public static <T> Iterator<T> toSorted(Iterator<T> self) {
- return toSorted(self, new NumberAwareComparator<>());
+ public static <K, V> boolean removeAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
+ return self.entrySet().removeIf(new BooleanClosureForMapPredicate<>(condition));
}
/**
- * Sorts the given iterator items using the comparator. The
- * original iterator will become exhausted of elements after completing this method call.
- * A new iterator is produced that traverses the items in sorted order.
+ * Splits all items into two lists based on the closure condition.
+ * The first list contains all items matching the closure expression.
+ * The second list all those that don't.
*
- * @param self the Iterator to be sorted
- * @param comparator a Comparator used for comparing items
- * @return the sorted items as an Iterator
- * @since 2.4.0
+ * @param self an Object with an Iterator returning its values
+ * @param closure a closure condition
+ * @return a List whose first item is the accepted values and whose second item is the rejected values
+ * @since 1.6.0
*/
- public static <T> Iterator<T> toSorted(Iterator<T> self, Comparator<? super T> comparator) {
- return toSorted(toList(self), comparator).listIterator();
+ @SuppressWarnings("unchecked")
+ public static Collection split(Object self, Closure closure) {
+ List accept = new ArrayList();
+ List reject = new ArrayList();
+ return split(closure, accept, reject, InvokerHelper.asIterator(self));
}
/**
- * Sorts the given iterator items into a sorted iterator using the Closure to determine the correct ordering.
- * The original iterator will be fully processed after the method call.
+ * Splits all items into two collections based on the closure condition.
+ * The first list contains all items which match the closure expression.
+ * The second list all those that don't.
* <p>
- * If the closure has two parameters it is used like a traditional Comparator.
- * I.e. it should compare its two parameters for order, returning a negative integer,
- * zero, or a positive integer when the first parameter is less than, equal to,
- * or greater than the second respectively. Otherwise, the Closure is assumed
- * to take a single parameter and return a Comparable (typically an Integer)
- * which is then used for further comparison.
+ * Example usage:
+ * <pre class="groovyTestCase">assert [[2,4],[1,3]] == [1,2,3,4].split { it % 2 == 0 }</pre>
*
- * @param self the Iterator to be sorted
- * @param closure a Closure used to determine the correct ordering
- * @return the sorted items as an Iterator
- * @see #toSorted(Iterator, Comparator)
- * @since 2.4.0
+ * @param self a Collection of values
+ * @param closure a closure condition
+ * @return a List whose first item is the accepted values and whose second item is the rejected values
+ * @since 1.6.0
*/
- public static <T> Iterator<T> toSorted(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(
- closure);
- return toSorted(self, comparator);
+ public static <T> Collection<Collection<T>> split(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ Collection<T> accept = createSimilarCollection(self);
+ Collection<T> reject = createSimilarCollection(self);
+ Iterator<T> iter = self.iterator();
+ return split(closure, accept, reject, iter);
+ }
+
+ static <T> Collection<Collection<T>> split(Closure closure, Collection<T> accept, Collection<T> reject, Iterator<T> iter) {
+ List<Collection<T>> answer = new ArrayList<>();
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ while (iter.hasNext()) {
+ T value = iter.next();
+ if (bcw.call(value)) {
+ accept.add(value);
+ } else {
+ reject.add(value);
+ }
+ }
+ answer.add(accept);
+ answer.add(reject);
+ return answer;
}
/**
- * Sorts the elements from the given map into a new ordered map using
- * a {@link NumberAwareComparator} on map entry values to determine the resulting order.
- * {@code NumberAwareComparator} has special treatment for numbers but otherwise uses the
- * natural ordering of the Iterator elements. The original map is unchanged.
- * <pre class="groovyTestCase">
- * def map = [a:5L, b:3, c:6, d:4.0].toSorted()
- * assert map.toString() == '[b:3, d:4.0, a:5, c:6]'
- * </pre>
+ * Splits all items into two collections based on the closure condition.
+ * The first list contains all items which match the closure expression.
+ * The second list all those that don't.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">assert [[2,4],[1,3]] == [1,2,3,4].split { it % 2 == 0 }</pre>
*
- * @param self the original unsorted map
- * @return the sorted map
+ * @param self a List of values
+ * @param closure a closure condition
+ * @return a List whose first item is the accepted values and whose second item is the rejected values
* @since 2.4.0
*/
- public static <K, V> Map<K, V> toSorted(Map<K, V> self) {
- return toSorted(self, new NumberAwareValueComparator<>());
+ @SuppressWarnings("unchecked")
+ public static <T> List<List<T>> split(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return (List<List<T>>) (List<?>) split((Collection<T>) self, closure);
}
/**
- * Sorts the elements from the given map into a new ordered map using
- * the supplied comparator to determine the ordering. The original map is unchanged.
- * <pre class="groovyTestCase">
- * def keyComparator = [compare: { e1, e2 {@code ->} e1.key {@code <=>} e2.key }] as Comparator
- * def valueComparator = [compare: { e1, e2 {@code ->} e1.value {@code <=>} e2.value }] as Comparator
- * def map1 = [a:5, b:3, d:4, c:6].toSorted(keyComparator)
- * assert map1.toString() == '[a:5, b:3, c:6, d:4]'
- * def map2 = [a:5, b:3, d:4, c:6].toSorted(valueComparator)
- * assert map2.toString() == '[b:3, d:4, a:5, c:6]'
- * </pre>
- *
- * @param self the original unsorted map
- * @param comparator a Comparator used for the comparison
- * @return the sorted map
+ * Splits all items into two collections based on the closure condition.
+ * The first list contains all items which match the closure expression.
+ * The second list all those that don't.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">assert [[2,4] as Set, [1,3] as Set] == ([1,2,3,4] as Set).split { it % 2 == 0 }</pre>
+ *
+ * @param self a Set of values
+ * @param closure a closure condition
+ * @return a List whose first item is the accepted values and whose second item is the rejected values
* @since 2.4.0
*/
- public static <K, V> Map<K, V> toSorted(Map<K, V> self, Comparator<Map.Entry<K, V>> comparator) {
- List<Map.Entry<K, V>> sortedEntries = toSorted(self.entrySet(), comparator);
- Map<K, V> result = new LinkedHashMap<>();
- for (Map.Entry<K, V> entry : sortedEntries) {
- result.put(entry.getKey(), entry.getValue());
- }
- return result;
+ @SuppressWarnings("unchecked")
+ public static <T> List<Set<T>> split(Set<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return (List<Set<T>>) (List<?>) split((Collection<T>) self, closure);
}
/**
- * Sorts the elements from the given map into a new ordered map using
- * the supplied Closure condition as a comparator to determine the ordering. The original map is unchanged.
+ * Finds all non-null subsequences of a list.
* <p>
- * If the closure has two parameters it is used like a traditional Comparator. I.e. it should compare
- * its two entry parameters for order, returning a negative integer, zero, or a positive integer when the
- * first parameter is less than, equal to, or greater than the second respectively. Otherwise,
- * the Closure is assumed to take a single entry parameter and return a Comparable (typically an Integer)
- * which is then used for further comparison.
+ * Example usage:
+ * <pre class="groovyTestCase">def result = [1, 2, 3].subsequences()
+ * assert result == [[1, 2, 3], [1, 3], [2, 3], [1, 2], [1], [2], [3]] as Set</pre>
+ *
+ * @param self the List of items
+ * @return the subsequences from the list
+ * @since 1.7.0
+ */
+ public static <T> Set<List<T>> subsequences(List<T> self) {
+ return GroovyCollections.subsequences(self);
+ }
+
+ /**
+ * Adds GroovyCollections#transpose(List) as a method on lists.
+ * A Transpose Function takes a collection of columns and returns a collection of
+ * rows. The first row consists of the first element from each column. Successive
+ * rows are constructed similarly.
+ * <p>
+ * Example usage:
* <pre class="groovyTestCase">
- * def map = [a:5, b:3, c:6, d:4].toSorted { a, b {@code ->} a.value {@code <=>} b.value }
- * assert map.toString() == '[b:3, d:4, a:5, c:6]'
+ * def result = [['a', 'b'], [1, 2]].transpose()
+ * assert result == [['a', 1], ['b', 2]]
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def result = [['a', 'b'], [1, 2], [3, 4]].transpose()
+ * assert result == [['a', 1, 3], ['b', 2, 4]]
* </pre>
*
- * @param self the original unsorted map
- * @param condition a Closure used as a comparator
- * @return the sorted map
- * @since 2.4.0
+ * @param self a List of lists
+ * @return a List of the transposed lists
+ * @see groovy.util.GroovyCollections#transpose(java.util.List)
+ * @since 1.5.0
*/
- public static <K, V> Map<K, V> toSorted(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>","Map.Entry<K,V>,Map.Entry<K,V>"}) Closure condition) {
- Comparator<Map.Entry<K,V>> comparator = (condition.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(
- condition) : new ClosureComparator<>(condition);
- return toSorted(self, comparator);
+ public static List transpose(List self) {
+ return GroovyCollections.transpose(self);
}
/**
- * Avoids doing unnecessary work when sorting an already sorted set
+ * Sums the items in an Iterable. This is equivalent to invoking the
+ * "plus" method on all items in the Iterable.
+ * <pre class="groovyTestCase">assert 1+2+3+4 == [1,2,3,4].sum()</pre>
*
- * @param self an already sorted set
- * @return an ordered copy of the sorted set
- * @since 2.4.0
+ * @param self Iterable of values to add together
+ * @return The sum of all the items
+ * @see #sum(Iterator)
+ * @since 2.2.0
*/
- public static <T> Set<T> toSorted(SortedSet<T> self) {
- return new LinkedHashSet<>(self);
+ public static Object sum(Iterable<?> self) {
+ return sum(self.iterator(), null, true);
}
/**
- * Avoids doing unnecessary work when sorting an already sorted map
+ * Sums the items from an Iterator. This is equivalent to invoking the
+ * "plus" method on all items from the Iterator. The iterator will become
+ * exhausted of elements after determining the sum value.
*
- * @param self an already sorted map
- * @return an ordered copy of the map
- * @since 2.4.0
+ * @param self an Iterator for the values to add together
+ * @return The sum of all the items
+ * @since 1.5.5
*/
- public static <K, V> Map<K, V> toSorted(SortedMap<K, V> self) {
- return new LinkedHashMap<>(self);
+ public static Object sum(Iterator<Object> self) {
+ return sum(self, null, true);
}
/**
- * Removes the initial item from the List.
- *
+ * Sums the items in an Iterable, adding the result to some initial value.
* <pre class="groovyTestCase">
- * def list = ["a", false, 2]
- * assert list.pop() == 'a'
- * assert list == [false, 2]
+ * assert 5+1+2+3+4 == [1,2,3,4].sum(5)
* </pre>
*
- * This is similar to pop on a Stack where the first item in the list
- * represents the top of the stack.
- *
- * Note: The behavior of this method changed in Groovy 2.5 to align with Java.
- * If you need the old behavior use 'removeLast'.
- *
- * @param self a List
- * @return the item removed from the List
- * @throws NoSuchElementException if the list is empty
- * @since 1.0
+ * @param self an Iterable of values to sum
+ * @param initialValue the items in the collection will be summed to this initial value
+ * @return The sum of all the items.
+ * @see #sum(Iterator, Object)
+ * @since 2.2.0
*/
- public static <T> T pop(List<T> self) {
- if (self.isEmpty()) {
- throw new NoSuchElementException("Cannot pop() an empty List");
- }
- return self.remove(0);
+ public static Object sum(Iterable<?> self, Object initialValue) {
+ return sum(self.iterator(), initialValue, false);
}
/**
- * Removes the last item from the List.
- *
- * <pre class="groovyTestCase">
- * def list = ["a", false, 2]
- * assert list.removeLast() == 2
- * assert list == ["a", false]
- * </pre>
- *
- * Using add() and removeLast() is similar to push and pop on a Stack
- * where the last item in the list represents the top of the stack.
+ * Sums the items from an Iterator, adding the result to some initial value. This is
+ * equivalent to invoking the "plus" method on all items from the Iterator. The iterator
+ * will become exhausted of elements after determining the sum value.
*
- * @param self a List
- * @return the item removed from the List
- * @throws NoSuchElementException if the list is empty
- * @since 2.5.0
+ * @param self an Iterator for the values to add together
+ * @param initialValue the items in the collection will be summed to this initial value
+ * @return The sum of all the items
+ * @since 1.5.5
*/
- public static <T> T removeLast(List<T> self) {
- if (self.isEmpty()) {
- throw new NoSuchElementException("Cannot removeLast() an empty List");
+ public static Object sum(Iterator<?> self, Object initialValue) {
+ return sum(self, initialValue, false);
+ }
+
+ static Object sum(Iterator<?> self, Object initialValue, boolean first) {
+ Object result = initialValue;
+ Object[] param = new Object[1];
+ while (self.hasNext()) {
+ Object next = self.next();
+ param[0] = next;
+ if (first) {
+ result = param[0];
+ first = false;
+ continue;
+ }
+ MetaClass metaClass = InvokerHelper.getMetaClass(result);
+ result = metaClass.invokeMethod(result, "plus", param);
}
- return self.remove(self.size() - 1);
+ return result;
}
/**
- * Provides an easy way to append multiple Map.Entry values to a Map.
+ * Sums the result of applying a closure to each item of an Iterable.
+ * <code>coll.sum(closure)</code> is equivalent to:
+ * <code>coll.collect(closure).sum()</code>.
+ * <pre class="groovyTestCase">assert 4+6+10+12 == [2,3,5,6].sum { it * 2 }</pre>
*
- * @param self a Map
- * @param entries a Collection of Map.Entry items to be added to the Map.
- * @return the same map, after the items have been added to it.
- * @since 1.6.1
+ * @param self an Iterable
+ * @param closure a single parameter closure that returns a (typically) numeric value.
+ * @return The sum of the values returned by applying the closure to each
+ * item of the Iterable.
+ * @since 2.2.0
*/
- public static <K, V> Map<K, V> putAll(Map<K, V> self, Collection<? extends Map.Entry<? extends K, ? extends V>> entries) {
- for (Map.Entry<? extends K, ? extends V> entry : entries) {
- self.put(entry.getKey(), entry.getValue());
- }
- return self;
+ public static <T> Object sum(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self.iterator(), null, closure, true);
}
/**
- * Returns a new <code>Map</code> containing all entries from <code>self</code> and <code>entries</code>,
- * giving precedence to <code>entries</code>. Any keys appearing in both Maps
- * will appear in the resultant map with values from the <code>entries</code>
- * operand. If <code>self</code> map is one of TreeMap, LinkedHashMap, Hashtable
- * or Properties, the returned Map will preserve that type, otherwise a HashMap will
- * be returned.
+ * Sums the result of applying a closure to each item returned from an iterator.
+ * <code>iter.sum(closure)</code> is equivalent to:
+ * <code>iter.collect(closure).sum()</code>. The iterator will become
+ * exhausted of elements after determining the sum value.
*
- * @param self a Map
- * @param entries a Collection of Map.Entry items to be added to the Map.
- * @return a new Map containing all key, value pairs from self and entries
- * @since 1.6.1
+ * @param self An Iterator
+ * @param closure a single parameter closure that returns a (typically) numeric value.
+ * @return The sum of the values returned by applying the closure to each
+ * item from the Iterator.
+ * @since 1.7.1
*/
- public static <K, V> Map<K, V> plus(Map<K, V> self, Collection<? extends Map.Entry<? extends K, ? extends V>> entries) {
- Map<K, V> map = cloneSimilarMap(self);
- putAll(map, entries);
- return map;
+ public static <T> Object sum(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self, null, closure, true);
}
/**
- * Prepends an item to the start of the List.
- *
- * <pre class="groovyTestCase">
- * def list = [3, 4, 2]
- * list.push("x")
- * assert list == ['x', 3, 4, 2]
- * </pre>
- *
- * This is similar to push on a Stack where the first item in the list
- * represents the top of the stack.
- *
- * Note: The behavior of this method changed in Groovy 2.5 to align with Java.
- * If you need the old behavior use 'add'.
+ * Sums the result of applying a closure to each item of an Iterable to some initial value.
+ * <code>iter.sum(initVal, closure)</code> is equivalent to:
+ * <code>iter.collect(closure).sum(initVal)</code>.
+ * <pre class="groovyTestCase">assert 50+4+6+10+12 == [2,3,5,6].sum(50) { it * 2 }</pre>
*
- * @param self a List
- * @param value element to be prepended to this list.
- * @return <tt>true</tt> (for legacy compatibility reasons).
- * @since 1.5.5
+ * @param self an Iterable
+ * @param closure a single parameter closure that returns a (typically) numeric value.
+ * @param initialValue the closure results will be summed to this initial value
+ * @return The sum of the values returned by applying the closure to each
+ * item of the collection.
+ * @since 1.5.0
*/
- public static <T> boolean push(List<T> self, T value) {
- self.add(0, value);
- return true;
+ public static <T> Object sum(Iterable<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self.iterator(), initialValue, closure, false);
}
/**
- * Returns the items from the List excluding the first item.
- * <pre class="groovyTestCase">
- * def list = [3, 4, 2]
- * assert list.tail() == [4, 2]
- * assert list == [3, 4, 2]
- * </pre>
+ * Sums the result of applying a closure to each item of an Iterator to some initial value.
+ * <code>iter.sum(initVal, closure)</code> is equivalent to:
+ * <code>iter.collect(closure).sum(initVal)</code>. The iterator will become
+ * exhausted of elements after determining the sum value.
*
- * @param self a List
- * @return a List without its first element
- * @throws NoSuchElementException if you try to access tail() for an empty List
- * @since 1.5.6
+ * @param self an Iterator
+ * @param closure a single parameter closure that returns a (typically) numeric value.
+ * @param initialValue the closure results will be summed to this initial value
+ * @return The sum of the values returned by applying the closure to each
+ * item from the Iterator.
+ * @since 1.7.1
*/
- public static <T> List<T> tail(List<T> self) {
- return (List<T>) tail((Iterable<T>)self);
+ public static <T> Object sum(Iterator<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self, initialValue, closure, false);
}
- /**
- * Returns the items from the SortedSet excluding the first item.
- * <pre class="groovyTestCase">
- * def sortedSet = [3, 4, 2] as SortedSet
- * assert sortedSet.tail() == [3, 4] as SortedSet
- * assert sortedSet == [3, 4, 2] as SortedSet
- * </pre>
- *
- * @param self a SortedSet
- * @return a SortedSet without its first element
- * @throws NoSuchElementException if you try to access tail() for an empty SortedSet
- * @since 2.4.0
- */
- public static <T> SortedSet<T> tail(SortedSet<T> self) {
- return (SortedSet<T>) tail((Iterable<T>) self);
+ static Object sum(Iterator<?> self, Object initialValue, Closure closure, boolean first) {
+ Object result = initialValue;
+ Object[] closureParam = new Object[1];
+ Object[] plusParam = new Object[1];
+ while (self.hasNext()) {
+ closureParam[0] = self.next();
+ plusParam[0] = closure.call(closureParam);
+ if (first) {
+ result = plusParam[0];
+ first = false;
+ continue;
+ }
+ MetaClass metaClass = InvokerHelper.getMetaClass(result);
+ result = metaClass.invokeMethod(result, "plus", plusParam);
+ }
+ return result;
}
/**
- * Calculates the tail values of this Iterable: the first value will be this list of all items from the iterable and the final one will be an empty list, with the intervening values the results of successive applications of tail on the items.
- * <pre class="groovyTestCase">
- * assert [1, 2, 3, 4].tails() == [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []]
- * </pre>
+ * Provide the standard Groovy <code>size()</code> method for <code>Iterator</code>.
+ * The iterator will become exhausted of elements after determining the size value.
*
- * @param self an Iterable
- * @return a List of the tail values from the given Iterable
- * @since 2.5.0
+ * @param self an Iterator
+ * @return the length of the Iterator
+ * @since 1.5.5
*/
- public static <T> List<List<T>> tails(Iterable<T> self) {
- return GroovyCollections.tails(self);
+ public static int size(Iterator self) {
+ int count = 0;
+ while (self.hasNext()) {
+ self.next();
+ count++;
+ }
+ return count;
}
/**
- * Returns the items from the Iterable excluding the first item.
+ * Provide the standard Groovy <code>size()</code> method for <code>Iterable</code>.
* <pre class="groovyTestCase">
- * def list = [3, 4, 2]
- * assert list.tail() == [4, 2]
- * assert list == [3, 4, 2]
+ * def items = [1, 2, 3]
+ * def iterable = { [ hasNext:{ !items.isEmpty() }, next:{ items.pop() } ] as Iterator } as Iterable
+ * assert iterable.size() == 3
* </pre>
*
* @param self an Iterable
- * @return a collection without its first element
- * @throws NoSuchElementException if you try to access tail() for an empty Iterable
- * @since 2.4.0
+ * @return the length of the Iterable
+ * @since 2.3.8
*/
- public static <T> Collection<T> tail(Iterable<T> self) {
- if (!self.iterator().hasNext()) {
- throw new NoSuchElementException("Cannot access tail() for an empty iterable");
- }
- Collection<T> result = createSimilarCollection(self);
- addAll(result, tail(self.iterator()));
- return result;
+ public static int size(Iterable self) {
+ return size(self.iterator());
}
/**
- * Returns the original iterator after throwing away the first element.
+ * Creates a sub-Map containing the given keys. This method is similar to
+ * List.subList() but uses keys rather than index ranges.
+ * <pre class="groovyTestCase">assert [1:10, 2:20, 4:40].subMap( [2, 4] ) == [2:20, 4:40]</pre>
*
- * @param self the original iterator
- * @return the iterator without its first element
- * @throws NoSuchElementException if you try to access tail() for an exhausted/empty Iterator
- * @since 1.8.1
+ * @param map a Map
+ * @param keys a Collection of keys
+ * @return a new Map containing the given keys
+ * @since 1.0
*/
- public static <T> Iterator<T> tail(Iterator<T> self) {
- if (!self.hasNext()) {
- throw new NoSuchElementException("Cannot access tail() for an empty Iterator");
+ public static <K, V> Map<K, V> subMap(Map<K, V> map, Collection<K> keys) {
+ Map<K, V> answer = new LinkedHashMap<>(keys.size());
+ for (K key : keys) {
+ if (map.containsKey(key)) {
+ answer.put(key, map.get(key));
+ }
}
- self.next();
- return self;
+ return answer;
}
/**
- * Returns the first <code>num</code> elements from the head of this List.
+ * Creates a sub-Map containing the given keys. This method is similar to
+ * List.subList() but uses keys rather than index ranges. The original
+ * map is unaltered.
* <pre class="groovyTestCase">
- * def strings = [ 'a', 'b', 'c' ]
- * assert strings.take( 0 ) == []
- * assert strings.take( 2 ) == [ 'a', 'b' ]
- * assert strings.take( 5 ) == [ 'a', 'b', 'c' ]
+ * def orig = [1:10, 2:20, 3:30, 4:40]
+ * assert orig.subMap([1, 3] as int[]) == [1:10, 3:30]
+ * assert orig.subMap([2, 4] as Integer[]) == [2:20, 4:40]
+ * assert orig.size() == 4
* </pre>
*
- * @param self the original List
- * @param num the number of elements to take from this List
- * @return a List consisting of the first <code>num</code> elements from this List,
- * or else all the elements from the List if it has less than <code>num</code> elements.
- * @since 1.8.1
+ * @param map a Map
+ * @param keys an array of keys
+ * @return a new Map containing the given keys
+ * @since 2.1.0
*/
- public static <T> List<T> take(List<T> self, int num) {
- return (List<T>) take((Iterable<T>)self, num);
+ public static <K, V> Map<K, V> subMap(Map<K, V> map, K[] keys) {
+ Map<K, V> answer = new LinkedHashMap<>(keys.length);
+ for (K key : keys) {
+ if (map.containsKey(key)) {
+ answer.put(key, map.get(key));
+ }
+ }
+ return answer;
}
/**
- * Returns the first <code>num</code> elements from the head of this SortedSet.
- * <pre class="groovyTestCase">
- * def strings = [ 'a', 'b', 'c' ] as SortedSet
- * assert strings.take( 0 ) == [] as SortedSet
- * assert strings.take( 2 ) == [ 'a', 'b' ] as SortedSet
- * assert strings.take( 5 ) == [ 'a', 'b', 'c' ] as SortedSet
- * </pre>
+ * Synonym for {@link #toSpreadMap(java.util.Map)}.
*
- * @param self the original SortedSet
- * @param num the number of elements to take from this SortedSet
- * @return a SortedSet consisting of the first <code>num</code> elements from this List,
- * or else all the elements from the SortedSet if it has less than <code>num</code> elements.
- * @since 2.4.0
+ * @param self a map
+ * @return a newly created SpreadMap
+ * @since 1.0
*/
- public static <T> SortedSet<T> take(SortedSet<T> self, int num) {
- return (SortedSet<T>) take((Iterable<T>) self, num);
+ public static SpreadMap spread(Map self) {
+ return toSpreadMap(self);
}
/**
- * Returns the first <code>num</code> elements from the head of this Iterable.
+ * Returns a new <code>SpreadMap</code> from this map.
+ * <p>
+ * The example below shows the various possible use cases:
* <pre class="groovyTestCase">
- * def strings = [ 'a', 'b', 'c' ]
- * assert strings.take( 0 ) == []
- * assert strings.take( 2 ) == [ 'a', 'b' ]
- * assert strings.take( 5 ) == [ 'a', 'b', 'c' ]
+ * def fn(Map m) { return m.a + m.b + m.c + m.d }
*
- * class AbcIterable implements Iterable<String> {
- * Iterator<String> iterator() { "abc".iterator() }
- * }
- * def abc = new AbcIterable()
- * assert abc.take(0) == []
- * assert abc.take(1) == ['a']
- * assert abc.take(3) == ['a', 'b', 'c']
- * assert abc.take(5) == ['a', 'b', 'c']
+ * assert fn(a:1, b:2, c:3, d:4) == 10
+ * assert fn(a:1, *:[b:2, c:3], d:4) == 10
+ * assert fn([a:1, b:2, c:3, d:4].toSpreadMap()) == 10
+ * assert fn((['a', 1, 'b', 2, 'c', 3, 'd', 4] as Object[]).toSpreadMap()) == 10
+ * assert fn(['a', 1, 'b', 2, 'c', 3, 'd', 4].toSpreadMap()) == 10
+ * assert fn(['abcd'.toList(), 1..4].transpose().flatten().toSpreadMap()) == 10
* </pre>
+ * Note that toSpreadMap() is not normally used explicitly but under the covers by Groovy.
*
- * @param self the original Iterable
- * @param num the number of elements to take from this Iterable
- * @return a Collection consisting of the first <code>num</code> elements from this Iterable,
- * or else all the elements from the Iterable if it has less than <code>num</code> elements.
- * @since 1.8.7
+ * @param self a map to be converted into a SpreadMap
+ * @return a newly created SpreadMap if this map is not null and its size is positive.
+ * @see groovy.lang.SpreadMap#SpreadMap(java.util.Map)
+ * @since 1.0
*/
- public static <T> Collection<T> take(Iterable<T> self, int num) {
- Collection<T> result = self instanceof Collection ? createSimilarCollection((Collection<T>) self, Math.max(num, 0)) : new ArrayList<>();
- addAll(result, take(self.iterator(), num));
- return result;
+ public static SpreadMap toSpreadMap(Map self) {
+ if (self == null)
+ throw new GroovyRuntimeException("Fail to convert Map to SpreadMap, because it is null.");
+ else
+ return new SpreadMap(self);
}
/**
- * Returns a new map containing the first <code>num</code> elements from the head of this map.
- * If the map instance does not have ordered keys, then this function could return a random <code>num</code>
- * entries. Groovy by default uses LinkedHashMap, so this shouldn't be an issue in the main.
- * <pre class="groovyTestCase">
- * def strings = [ 'a':10, 'b':20, 'c':30 ]
- * assert strings.take( 0 ) == [:]
- * assert strings.take( 2 ) == [ 'a':10, 'b':20 ]
- * assert strings.take( 5 ) == [ 'a':10, 'b':20, 'c':30 ]
- * </pre>
- *
- * @param self the original map
- * @param num the number of elements to take from this map
- * @return a new map consisting of the first <code>num</code> elements of this map,
- * or else the whole map if it has less than <code>num</code> elements.
- * @since 1.8.1
+ * Creates a spreadable map from this list.
+ * <p>
+ * @param self a list
+ * @return a newly created SpreadMap
+ * @see groovy.lang.SpreadMap#SpreadMap(java.util.List)
+ * @see #toSpreadMap(java.util.Map)
+ * @since 1.8.0
*/
- public static <K, V> Map<K, V> take(Map<K, V> self, int num) {
- if (self.isEmpty() || num <= 0) {
- return createSimilarMap(self);
- }
- Map<K, V> ret = createSimilarMap(self);
- for (Map.Entry<K, V> entry : self.entrySet()) {
- K key = entry.getKey();
- V value = entry.getValue();
+ public static SpreadMap toSpreadMap(List self) {
+ if (self == null)
+ throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because it is null.");
+ else if (self.size() % 2 != 0)
+ throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because its size is not even.");
+ else
+ return new SpreadMap(self);
+ }
- ret.put(key, value);
- if (--num <= 0) {
- break;
- }
- }
- return ret;
+ /**
+ * Creates a spreadable map from this iterable.
+ * <p>
+ * @param self an iterable
+ * @return a newly created SpreadMap
+ * @see groovy.lang.SpreadMap#SpreadMap(java.util.List)
+ * @see #toSpreadMap(java.util.Map)
+ * @since 2.4.0
+ */
+ @SuppressWarnings("unchecked")
+ public static SpreadMap toSpreadMap(Iterable self) {
+ if (self == null)
+ throw new GroovyRuntimeException("Fail to convert Iterable to SpreadMap, because it is null.");
+ else
+ return toSpreadMap(asList(self));
}
/**
- * Returns an iterator of up to the first <code>num</code> elements from this iterator.
- * The original iterator is stepped along by <code>num</code> elements.
+ * Wraps a map using the decorator pattern with a wrapper that intercepts all calls
+ * to <code>get(key)</code>. If an unknown key is found, a default value will be
+ * stored into the Map before being returned. The default value stored will be the
+ * result of calling the supplied Closure with the key as the parameter to the Closure.
+ *
+ * Example usage:
* <pre class="groovyTestCase">
- * def a = 0
- * def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator
- * def iteratorCompare( Iterator a, List b ) {
- * a.collect { it } == b
- * }
- * assert iteratorCompare( iter.take( 0 ), [] )
- * assert iteratorCompare( iter.take( 2 ), [ 0, 1 ] )
- * assert iteratorCompare( iter.take( 5 ), [ 2, 3, 4, 5, 6 ] )
+ * def map = [a:1, b:2].withDefault{ k {@code ->} k.toCharacter().isLowerCase() ? 10 : -10 }
+ * def expected = [a:1, b:2, c:10, D:-10]
+ * assert expected.every{ e {@code ->} e.value == map[e.key] }
+ *
+ * def constMap = [:].withDefault{ 42 }
+ * assert constMap.foo == 42
+ * assert constMap.size() == 1
* </pre>
*
- * @param self the Iterator
- * @param num the number of elements to take from this iterator
- * @return an iterator consisting of up to the first <code>num</code> elements of this iterator.
- * @since 1.8.1
+ * @param self a Map
+ * @param init a Closure which is passed the unknown key
+ * @return the wrapped Map
+ * @since 1.7.1
+ * @see #withDefault(Map, boolean, boolean, Closure)
*/
- public static <T> Iterator<T> take(Iterator<T> self, int num) {
- return new TakeIterator<>(self, num);
- }
-
- private static final class TakeIterator<E> implements Iterator<E> {
- private final Iterator<E> delegate;
- private Integer num;
-
- private TakeIterator(Iterator<E> delegate, Integer num) {
- this.delegate = delegate;
- this.num = num;
- }
-
- @Override
- public boolean hasNext() {
- return num > 0 && delegate.hasNext();
- }
-
- @Override
- public E next() {
- if (num <= 0) throw new NoSuchElementException();
- num--;
- return delegate.next();
- }
-
- @Override
- public void remove() {
- delegate.remove();
- }
+ public static <K, V> Map<K, V> withDefault(Map<K, V> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> init) {
+ return MapWithDefault.newInstance(self, true, false, init);
}
/**
- * Returns the last <code>num</code> elements from the tail of this Iterable.
+ * Wraps a map using the decorator pattern with a wrapper that intercepts all calls
+ * to <code>get(key)</code> and <code>put(key, value)</code>.
+ * If an unknown key is found for <code>get</code>, a default value will be returned.
+ * The default value will be the result of calling the supplied Closure with the key
+ * as the parameter to the Closure.
+ * If <code>autoGrow</code> is set, the value will be stored into the map.
+ *
+ * If <code>autoShrink</code> is set, then an attempt to <code>put</code> the default value
+ * into the map is ignored and indeed any existing value would be removed.
+ *
+ * If you wish the backing map to be as small as possible, consider setting <code>autoGrow</code>
+ * to <code>false</code> and <code>autoShrink</code> to <code>true</code>.
+ * This keeps the backing map as small as possible, i.e. sparse, but also means that
+ * <code>containsKey</code>, <code>keySet</code>, <code>size</code>, and other methods
+ * will only reflect the sparse values.
+ *
+ * If you are wrapping an immutable map, you should set <code>autoGrow</code>
+ * and <code>autoShrink</code> to <code>false</code>.
+ * In this scenario, the <code>get</code> method is essentially a shorthand
+ * for calling <code>getOrDefault</code> with the default value supplied once as a Closure.
+ *
+ * Example usage:
* <pre class="groovyTestCase">
- * def strings = [ 'a', 'b', 'c' ]
- * assert strings.takeRight( 0 ) == []
- * assert strings.takeRight( 2 ) == [ 'b', 'c' ]
- * assert strings.takeRight( 5 ) == [ 'a', 'b', 'c' ]
+ * // sparse map example
+ * def answers = [life: 100].withDefault(false, true){ 42 }
+ * assert answers.size() == 1
+ * assert answers.foo == 42
+ * assert answers.size() == 1
+ * answers.life = 42
+ * answers.putAll(universe: 42, everything: 42)
+ * assert answers.size() == 0
*
- * class AbcIterable implements Iterable<String> {
- * Iterator<String> iterator() { "abc".iterator() }
- * }
- * def abc = new AbcIterable()
- * assert abc.takeRight(0) == []
- * assert abc.takeRight(1) == ['c']
- * assert abc.takeRight(3) == ['a', 'b', 'c']
- * assert abc.takeRight(5) == ['a', 'b', 'c']
+ * // immutable map example
+ * def certainties = [death: true, taxes: true].asImmutable().withDefault(false, false){ false }
+ * assert certainties.size() == 2
+ * assert certainties.wealth == false
+ * assert certainties.size() == 2
* </pre>
*
- * @param self the original Iterable
- * @param num the number of elements to take from this Iterable
- * @return a Collection consisting of the last <code>num</code> elements from this Iterable,
- * or else all the elements from the Iterable if it has less than <code>num</code> elements.
- * @since 2.4.0
+ * @param self a Map
+ * @param autoGrow whether calling get could potentially grow the map if the key isn't found
+ * @param autoShrink whether calling put with the default value could potentially shrink the map
+ * @param init a Closure which is passed the unknown key
+ * @return the wrapped Map
+ * @since 4.0.1
*/
- public static <T> Collection<T> takeRight(Iterable<T> self, int num) {
- if (num <= 0 || !self.iterator().hasNext()) {
- return self instanceof Collection ? createSimilarCollection((Collection<T>) self, 0) : new ArrayList<>();
- }
- Collection<T> selfCol = self instanceof Collection ? (Collection<T>) self : toList(self);
- if (selfCol.size() <= num) {
- Collection<T> ret = createSimilarCollection(selfCol, selfCol.size());
- ret.addAll(selfCol);
- return ret;
- }
- Collection<T> ret = createSimilarCollection(selfCol, num);
- ret.addAll(asList(selfCol).subList(selfCol.size() - num, selfCol.size()));
- return ret;
+ public static <K, V> Map<K, V> withDefault(Map<K, V> self, boolean autoGrow, boolean autoShrink, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> init) {
+ return MapWithDefault.newInstance(self, autoGrow, autoShrink, init);
}
/**
- * Returns the last <code>num</code> elements from the tail of this List.
- * <pre class="groovyTestCase">
- * def strings = [ 'a', 'b', 'c' ]
- * assert strings.takeRight( 0 ) == []
- * assert strings.takeRight( 2 ) == [ 'b', 'c' ]
- * assert strings.takeRight( 5 ) == [ 'a', 'b', 'c' ]
- * </pre>
+ * An alias for <code>withLazyDefault</code> which decorates a list allowing
+ * it to grow when called with index values outside the normal list bounds.
*
- * @param self the original List
- * @param num the number of elements to take from this List
- * @return a List consisting of the last <code>num</code> elements from this List,
- * or else all the elements from the List if it has less than <code>num</code> elements.
- * @since 2.4.0
+ * @param self a List
+ * @param init a Closure with the target index as parameter which generates the default value
+ * @return the decorated List
+ * @see #withLazyDefault(java.util.List, groovy.lang.Closure)
+ * @see #withEagerDefault(java.util.List, groovy.lang.Closure)
+ * @since 1.8.7
*/
- public static <T> List<T> takeRight(List<T> self, int num) {
- return (List<T>) takeRight((Iterable<T>) self, num);
+ public static <T> ListWithDefault<T> withDefault(List<T> self, @ClosureParams(value=SimpleType.class, options = "int") Closure<T> init) {
+ return withLazyDefault(self, init);
}
/**
- * Returns the last <code>num</code> elements from the tail of this SortedSet.
+ * Decorates a list allowing it to grow when called with a non-existent index value.
+ * When called with such values, the list is grown in size and a default value
+ * is placed in the list by calling a supplied <code>init</code> Closure. Subsequent
+ * retrieval operations if finding a null value in the list assume it was set
+ * as null from an earlier growing operation and again call the <code>init</code> Closure
+ * to populate the retrieved value; consequently the list can't be used to store null values.
+ * <p>
+ * How it works: The decorated list intercepts all calls
+ * to <code>getAt(index)</code> and <code>get(index)</code>. If an index greater than
+ * or equal to the current <code>size()</code> is used, the list will grow automatically
+ * up to the specified index. Gaps will be filled by {@code null}. If a default value
+ * should also be used to fill gaps instead of {@code null}, use <code>withEagerDefault</code>.
+ * If <code>getAt(index)</code> or <code>get(index)</code> are called and a null value
+ * is found, it is assumed that the null value was a consequence of an earlier grow list
+ * operation and the <code>init</code> Closure is called to populate the value.
+ * <p>
+ * Example usage:
* <pre class="groovyTestCase">
- * def strings = [ 'a', 'b', 'c' ] as SortedSet
- * assert strings.takeRight( 0 ) == [] as SortedSet
- * assert strings.takeRight( 2 ) == [ 'b', 'c' ] as SortedSet
- * assert strings.takeRight( 5 ) == [ 'a', 'b', 'c' ] as SortedSet
+ * def list = [0, 1].withLazyDefault{ 42 }
+ * assert list[0] == 0
+ * assert list[1] == 1
+ * assert list[3] == 42 // default value
+ * assert list == [0, 1, null, 42] // gap filled with null
+ *
+ * // illustrate using the index when generating default values
+ * def list2 = [5].withLazyDefault{ index {@code ->} index * index }
+ * assert list2[3] == 9
+ * assert list2 == [5, null, null, 9]
+ * assert list2[2] == 4
+ * assert list2 == [5, null, 4, 9]
+ *
+ * // illustrate what happens with null values
+ * list2[2] = null
+ * assert list2[2] == 4
* </pre>
*
- * @param self the original SortedSet
- * @param num the number of elements to take from this SortedSet
- * @return a SortedSet consisting of the last <code>num</code> elements from this SortedSet,
- * or else all the elements from the SortedSet if it has less than <code>num</code> elements.
- * @since 2.4.0
+ * @param self a List
+ * @param init a Closure with the target index as parameter which generates the default value
+ * @return the decorated List
+ * @since 1.8.7
*/
- public static <T> SortedSet<T> takeRight(SortedSet<T> self, int num) {
- return (SortedSet<T>) takeRight((Iterable<T>) self, num);
+ public static <T> ListWithDefault<T> withLazyDefault(List<T> self, @ClosureParams(value=SimpleType.class, options="int") Closure<T> init) {
+ return ListWithDefault.newInstance(self, true, init);
}
/**
- * Returns the longest prefix of this list where each element
- * passed to the given closure condition evaluates to true.
- * Similar to {@link #takeWhile(Iterable, groovy.lang.Closure)}
- * except that it attempts to preserve the type of the original list.
+ * Decorates a list allowing it to grow when called with a non-existent index value.
+ * When called with such values, the list is grown in size and a default value
+ * is placed in the list by calling a supplied <code>init</code> Closure. Null values
+ * can be stored in the list.
+ * <p>
+ * How it works: The decorated list intercepts all calls
+ * to <code>getAt(index)</code> and <code>get(index)</code>. If an index greater than
+ * or equal to the current <code>size()</code> is used, the list will grow automatically
+ * up to the specified index. Gaps will be filled by calling the <code>init</code> Closure.
+ * If generating a default value is a costly operation consider using <code>withLazyDefault</code>.
+ * <p>
+ * Example usage:
* <pre class="groovyTestCase">
- * def nums = [ 1, 3, 2 ]
- * assert nums.takeWhile{ it {@code <} 1 } == []
- * assert nums.takeWhile{ it {@code <} 3 } == [ 1 ]
- * assert nums.takeWhile{ it {@code <} 4 } == [ 1, 3, 2 ]
- * </pre>
+ * def list = [0, 1].withEagerDefault{ 42 }
+ * assert list[0] == 0
+ * assert list[1] == 1
+ * assert list[3] == 42 // default value
+ * assert list == [0, 1, 42, 42] // gap filled with default value
*
- * @param self the original list
- * @param condition the closure that must evaluate to true to
- * continue taking elements
- * @return a prefix of the given list where each element passed to
- * the given closure evaluates to true
+ * // illustrate using the index when generating default values
+ * def list2 = [5].withEagerDefault{ index {@code ->} index * index }
+ * assert list2[3] == 9
+ * assert list2 == [5, 1, 4, 9]
+ *
+ * // illustrate what happens with null values
+ * list2[2] = null
+ * assert list2[2] == null
+ * assert list2 == [5, 1, null, 9]
+ * </pre>
+ *
+ * @param self a List
+ * @param init a Closure with the target index as parameter which generates the default value
+ * @return the wrapped List
* @since 1.8.7
*/
- public static <T> List<T> takeWhile(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- int num = 0;
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
- for (T value : self) {
- if (bcw.call(value)) {
- num += 1;
- } else {
- break;
- }
- }
- return take(self, num);
+ public static <T> ListWithDefault<T> withEagerDefault(List<T> self, @ClosureParams(value=SimpleType.class, options="int") Closure<T> init) {
+ return ListWithDefault.newInstance(self, false, init);
}
/**
- * Returns a Collection containing the longest prefix of the elements from this Iterable
- * where each element passed to the given closure evaluates to true.
+ * Zips an Iterable with indices in (value, index) order.
+ * <p/>
+ * Example usage:
* <pre class="groovyTestCase">
- * class AbcIterable implements Iterable<String> {
- * Iterator<String> iterator() { "abc".iterator() }
- * }
- * def abc = new AbcIterable()
- * assert abc.takeWhile{ it {@code <} 'b' } == ['a']
- * assert abc.takeWhile{ it {@code <=} 'b' } == ['a', 'b']
+ * assert [["a", 0], ["b", 1]] == ["a", "b"].withIndex()
+ * assert ["0: a", "1: b"] == ["a", "b"].withIndex().collect { str, idx {@code ->} "$idx: $str" }
* </pre>
*
- * @param self an Iterable
- * @param condition the closure that must evaluate to true to
- * continue taking elements
- * @return a Collection containing a prefix of the elements from the given Iterable where
- * each element passed to the given closure evaluates to true
- * @since 1.8.7
+ * @param self an Iterable
+ * @return a zipped list with indices
+ * @see #indexed(Iterable)
+ * @since 2.4.0
*/
- public static <T> Collection<T> takeWhile(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- Collection<T> result = createSimilarCollection(self);
- addAll(result, takeWhile(self.iterator(), condition));
- return result;
+ public static <E> List<Tuple2<E, Integer>> withIndex(Iterable<E> self) {
+ return withIndex(self, 0);
}
/**
- * Returns the longest prefix of this SortedSet where each element
- * passed to the given closure condition evaluates to true.
- * Similar to {@link #takeWhile(Iterable, groovy.lang.Closure)}
- * except that it attempts to preserve the type of the original SortedSet.
+ * Zips an Iterable with indices in (value, index) order.
+ * <p/>
+ * Example usage:
* <pre class="groovyTestCase">
- * def nums = [ 1, 2, 3 ] as SortedSet
- * assert nums.takeWhile{ it {@code <} 1 } == [] as SortedSet
- * assert nums.takeWhile{ it {@code <} 2 } == [ 1 ] as SortedSet
- * assert nums.takeWhile{ it {@code <} 4 } == [ 1, 2, 3 ] as SortedSet
+ * assert [["a", 5], ["b", 6]] == ["a", "b"].withIndex(5)
+ * assert ["1: a", "2: b"] == ["a", "b"].withIndex(1).collect { str, idx {@code ->} "$idx: $str" }
* </pre>
*
- * @param self the original SortedSet
- * @param condition the closure that must evaluate to true to
- * continue taking elements
- * @return a prefix of the given SortedSet where each element passed to
- * the given closure evaluates to true
+ * @param self an Iterable
+ * @param offset an index to start from
+ * @return a zipped list with indices
+ * @see #indexed(Iterable, int)
* @since 2.4.0
*/
- public static <T> SortedSet<T> takeWhile(SortedSet<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- return (SortedSet<T>) takeWhile((Iterable<T>) self, condition);
+ public static <E> List<Tuple2<E, Integer>> withIndex(Iterable<E> self, int offset) {
+ return toList(withIndex(self.iterator(), offset));
}
/**
- * Returns the longest prefix of this Map where each entry (or key/value pair) when
- * passed to the given closure evaluates to true.
+ * Zips an iterator with indices in (value, index) order.
+ * <p/>
+ * Example usage:
* <pre class="groovyTestCase">
- * def shopping = [milk:1, bread:2, chocolate:3]
- * assert shopping.takeWhile{ it.key.size() {@code <} 6 } == [milk:1, bread:2]
- * assert shopping.takeWhile{ it.value % 2 } == [milk:1]
- * assert shopping.takeWhile{ k, v {@code ->} k.size() + v {@code <=} 7 } == [milk:1, bread:2]
+ * assert [["a", 0], ["b", 1]] == ["a", "b"].iterator().withIndex().toList()
+ * assert ["0: a", "1: b"] == ["a", "b"].iterator().withIndex().collect { str, idx {@code ->} "$idx: $str" }.toList()
* </pre>
- * If the map instance does not have ordered keys, then this function could appear to take random
- * entries. Groovy by default uses LinkedHashMap, so this shouldn't be an issue in the main.
*
- * @param self a Map
- * @param condition a 1 (or 2) arg Closure that must evaluate to true for the
- * entry (or key and value) to continue taking elements
- * @return a prefix of the given Map where each entry (or key/value pair) passed to
- * the given closure evaluates to true
- * @since 1.8.7
+ * @param self an iterator
+ * @return a zipped iterator with indices
+ * @see #indexed(Iterator)
+ * @since 2.4.0
*/
- public static <K, V> Map<K, V> takeWhile(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
- if (self.isEmpty()) {
- return createSimilarMap(self);
- }
- Map<K, V> ret = createSimilarMap(self);
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
- for (Map.Entry<K, V> entry : self.entrySet()) {
- if (!bcw.callForMap(entry)) break;
- ret.put(entry.getKey(), entry.getValue());
- }
- return ret;
+ public static <E> Iterator<Tuple2<E, Integer>> withIndex(Iterator<E> self) {
+ return withIndex(self, 0);
}
/**
- * Returns the longest prefix of elements in this iterator where
- * each element passed to the given condition closure evaluates to true.
- * <p>
+ * Zips an iterator with indices in (value, index) order.
+ * <p/>
+ * Example usage:
* <pre class="groovyTestCase">
- * def a = 0
- * def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator
- *
- * assert [].iterator().takeWhile{ it {@code <} 3 }.toList() == []
- * assert [1, 2, 3, 4, 5].iterator().takeWhile{ it {@code <} 3 }.toList() == [ 1, 2 ]
- * assert iter.takeWhile{ it {@code <} 5 }.toList() == [ 0, 1, 2, 3, 4 ]
+ * assert [["a", 5], ["b", 6]] == ["a", "b"].iterator().withIndex(5).toList()
+ * assert ["1: a", "2: b"] == ["a", "b"].iterator().withIndex(1).collect { str, idx {@code ->} "$idx: $str" }.toList()
* </pre>
*
- * @param self the Iterator
- * @param condition the closure that must evaluate to true to
- * continue taking elements
- * @return a prefix of elements in the given iterator where each
- * element passed to the given closure evaluates to true
- * @since 1.8.7
+ * @param self an iterator
+ * @param offset an index to start from
+ * @return a zipped iterator with indices
+ * @see #indexed(Iterator, int)
+ * @since 2.4.0
*/
- public static <T> Iterator<T> takeWhile(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- return new TakeWhileIterator<>(self, condition);
+ public static <E> Iterator<Tuple2<E, Integer>> withIndex(Iterator<E> self, int offset) {
+ return new ZipPostIterator<>(self, offset);
}
- private static final class TakeWhileIterator<E> implements Iterator<E> {
+ private static final class ZipPostIterator<E> implements Iterator<Tuple2<E, Integer>> {
private final Iterator<E> delegate;
- private final BooleanClosureWrapper condition;
- private boolean exhausted;
- private E next;
+ private int index;
- private TakeWhileIterator(Iterator<E> delegate, Closure condition) {
+ private ZipPostIterator(Iterator<E> delegate, int offset) {
this.delegate = delegate;
- this.condition = new BooleanClosureWrapper(condition);
- advance();
+ this.index = offset;
}
@Override
public boolean hasNext() {
- return !exhausted;
+ return delegate.hasNext();
}
@Override
- public E next() {
- if (exhausted) throw new NoSuchElementException();
- E result = next;
- advance();
- return result;
+ public Tuple2<E, Integer> next() {
+ if (!hasNext()) throw new NoSuchElementException();
+ return new Tuple2<>(delegate.next(), index++);
+ }
+
+ @Override
+ public void remove() {
+ delegate.remove();
+ }
+ }
+
+ /**
+ * Sorts the Collection. Assumes that the collection items are comparable
+ * and uses their natural ordering to determine the resulting order.
+ * If the Collection is a List, it is sorted in place and returned.
+ * Otherwise, the elements are first placed into a new list which is then
+ * sorted and returned - leaving the original Collection unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [3,1,2].sort()</pre>
+ *
+ * @param self the Iterable to be sorted
+ * @return the sorted Iterable as a List
+ * @see #sort(Iterable, boolean)
+ * @since 2.2.0
+ */
+ public static <T> List<T> sort(Iterable<T> self) {
+ return sort(self, true);
+ }
+
+ /**
+ * Sorts the Iterable. Assumes that the Iterable items are
+ * comparable and uses their natural ordering to determine the resulting order.
+ * If the Iterable is a List and mutate is true,
+ * it is sorted in place and returned. Otherwise, the elements are first placed
+ * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
+ * <pre class="groovyTestCase">assert [1,2,3] == [3,1,2].sort()</pre>
+ * <pre class="groovyTestCase">
+ * def orig = [1, 3, 2]
+ * def sorted = orig.sort(false)
+ * assert orig == [1, 3, 2]
+ * assert sorted == [1, 2, 3]
+ * </pre>
+ *
+ * @param self the iterable to be sorted
+ * @param mutate false will always cause a new list to be created, true will mutate lists in place
+ * @return the sorted iterable as a List
+ * @since 2.2.0
+ */
+ public static <T> List<T> sort(Iterable<T> self, boolean mutate) {
+ List<T> answer = mutate ? asList(self) : toList(self);
+ answer.sort(new NumberAwareComparator<>());
+ return answer;
+ }
+
+ /**
+ * Sorts the elements from the given map into a new ordered map using
+ * the closure as a comparator to determine the ordering.
+ * The original map is unchanged.
+ * <pre class="groovyTestCase">def map = [a:5, b:3, c:6, d:4].sort { a, b {@code ->} a.value {@code <=>} b.value }
+ * assert map == [b:3, d:4, a:5, c:6]</pre>
+ *
+ * @param self the original unsorted map
+ * @param closure a Closure used as a comparator
+ * @return the sorted map
+ * @since 1.6.0
+ */
+ public static <K, V> Map<K, V> sort(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>","Map.Entry<K,V>,Map.Entry<K,V>"}) Closure closure) {
+ Map<K, V> result = new LinkedHashMap<>();
+ List<Map.Entry<K, V>> entries = asList(self.entrySet());
+ sort((Iterable<Map.Entry<K, V>>) entries, closure);
+ for (Map.Entry<K, V> entry : entries) {
+ result.put(entry.getKey(), entry.getValue());
}
+ return result;
+ }
+
+ /**
+ * Sorts the elements from the given map into a new ordered Map using
+ * the specified key comparator to determine the ordering.
+ * The original map is unchanged.
+ * <pre class="groovyTestCase">def map = [ba:3, cz:6, ab:5].sort({ a, b {@code ->} a[-1] {@code <=>} b[-1] } as Comparator)
+ * assert map*.value == [3, 5, 6]</pre>
+ *
+ * @param self the original unsorted map
+ * @param comparator a Comparator
+ * @return the sorted map
+ * @since 1.7.2
+ */
+ public static <K, V> Map<K, V> sort(Map<K, V> self, Comparator<? super K> comparator) {
+ Map<K, V> result = new TreeMap<>(comparator);
+ result.putAll(self);
+ return result;
+ }
+
+ /**
+ * Sorts the elements from the given map into a new ordered Map using
+ * the natural ordering of the keys to determine the ordering.
+ * The original map is unchanged.
+ * <pre class="groovyTestCase">map = [ba:3, cz:6, ab:5].sort()
+ * assert map*.value == [5, 3, 6]
+ * </pre>
+ *
+ * @param self the original unsorted map
+ * @return the sorted map
+ * @since 1.7.2
+ */
+ public static <K, V> Map<K, V> sort(Map<K, V> self) {
+ return new TreeMap<>(self);
+ }
- @Override
- public void remove() {
- if (exhausted) throw new NoSuchElementException();
- delegate.remove();
- }
+ /**
+ * Sorts the given iterator items into a sorted iterator. The items are
+ * assumed to be comparable. The original iterator will become
+ * exhausted of elements after completing this method call.
+ * A new iterator is produced that traverses the items in sorted order.
+ *
+ * @param self the Iterator to be sorted
+ * @return the sorted items as an Iterator
+ * @since 1.5.5
+ */
+ public static <T> Iterator<T> sort(Iterator<T> self) {
+ return sort((Iterable<T>) toList(self)).listIterator();
+ }
- private void advance() {
- exhausted = !delegate.hasNext();
- if (!exhausted) {
- next = delegate.next();
- if (!condition.call(next)) {
- exhausted = true;
- next = null;
- }
- }
- }
+ /**
+ * Sorts the given iterator items into a sorted iterator using the comparator. The
+ * original iterator will become exhausted of elements after completing this method call.
+ * A new iterator is produced that traverses the items in sorted order.
+ *
+ * @param self the Iterator to be sorted
+ * @param comparator a Comparator used for comparing items
+ * @return the sorted items as an Iterator
+ * @since 1.5.5
+ */
+ public static <T> Iterator<T> sort(Iterator<T> self, Comparator<? super T> comparator) {
+ return sort(toList(self), true, comparator).listIterator();
}
/**
- * Randomly reorders the elements of the specified list.
+ * Sorts the Iterable using the given Comparator. If the Iterable is a List and mutate
+ * is true, it is sorted in place and returned. Otherwise, the elements are first placed
+ * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
* <pre class="groovyTestCase">
- * def list = ["a", 4, false]
- * def origSize = list.size()
- * def origCopy = new ArrayList(list)
- * list.shuffle()
- * assert list.size() == origSize
- * assert origCopy.every{ list.contains(it) }
+ * assert ["hi","hey","hello"] == ["hello","hi","hey"].sort(false, { a, b {@code ->} a.length() {@code <=>} b.length() } as Comparator )
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def orig = ["hello","hi","Hey"]
+ * def sorted = orig.sort(false, String.CASE_INSENSITIVE_ORDER)
+ * assert orig == ["hello","hi","Hey"]
+ * assert sorted == ["hello","Hey","hi"]
* </pre>
*
- * @param self a List
- * @see Collections#shuffle(List)
- * @since 3.0.0
+ * @param self the Iterable to be sorted
+ * @param mutate false will always cause a new list to be created, true will mutate lists in place
+ * @param comparator a Comparator used for the comparison
+ * @return a sorted List
+ * @since 2.2.0
*/
- public static void shuffle(List<?> self) {
- Collections.shuffle(self);
+ public static <T> List<T> sort(Iterable<T> self, boolean mutate, Comparator<? super T> comparator) {
+ List<T> list = mutate ? asList(self) : toList(self);
+ list.sort(comparator);
+ return list;
}
/**
- * Randomly reorders the elements of the specified list using the
- * specified random instance as the source of randomness.
- * <pre class="groovyTestCase">
- * def r = new Random()
- * def list = ["a", 4, false]
- * def origSize = list.size()
- * def origCopy = new ArrayList(list)
- * list.shuffle(r)
- * assert list.size() == origSize
- * assert origCopy.every{ list.contains(it) }
- * </pre>
+ * Sorts the given iterator items into a sorted iterator using the Closure to determine the correct ordering.
+ * The original iterator will be fully processed after the method call.
+ * <p>
+ * If the closure has two parameters it is used like a traditional Comparator.
+ * I.e. it should compare its two parameters for order, returning a negative integer,
+ * zero, or a positive integer when the first parameter is less than, equal to,
+ * or greater than the second respectively. Otherwise, the Closure is assumed
+ * to take a single parameter and return a Comparable (typically an Integer)
+ * which is then used for further comparison.
*
- * @param self a List
- * @see Collections#shuffle(List)
- * @since 3.0.0
+ * @param self the Iterator to be sorted
+ * @param closure a Closure used to determine the correct ordering
+ * @return the sorted items as an Iterator
+ * @since 1.5.5
*/
- public static void shuffle(List<?> self, Random rnd) {
- Collections.shuffle(self, rnd);
+ public static <T> Iterator<T> sort(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return sort((Iterable<T>) toList(self), closure).listIterator();
}
/**
- * Creates a new list containing the elements of the specified list
- * but in a random order.
- * <pre class="groovyTestCase">
- * def orig = ["a", 4, false]
- * def shuffled = orig.shuffled()
- * assert orig.size() == shuffled.size()
- * assert orig.every{ shuffled.contains(it) }
- * </pre>
+ * Sorts this Iterable using the given Closure to determine the correct ordering. If the Iterable is a List,
+ * it is sorted in place and returned. Otherwise, the elements are first placed
+ * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
+ * <p>
+ * If the Closure has two parameters
+ * it is used like a traditional Comparator. I.e. it should compare
+ * its two parameters for order, returning a negative integer,
+ * zero, or a positive integer when the first parameter is less than,
+ * equal to, or greater than the second respectively. Otherwise,
+ * the Closure is assumed to take a single parameter and return a
+ * Comparable (typically an Integer) which is then used for
+ * further comparison.
+ * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre>
+ * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
*
- * @param self a List
- * @see Collections#shuffle(List)
- * @since 3.0.0
+ * @param self the Iterable to be sorted
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return a newly created sorted List
+ * @see #sort(Iterable, boolean, Closure)
+ * @since 2.2.0
*/
- public static <T> List<T> shuffled(List<T> self) {
- List<T> copy = new ArrayList<>(self);
- Collections.shuffle(copy);
- return copy;
+ public static <T> List<T> sort(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return sort(self, true, closure);
}
/**
- * Creates a new list containing the elements of the specified list but in a random
- * order using the specified random instance as the source of randomness.
+ * Sorts this Iterable using the given Closure to determine the correct ordering. If the Iterable is a List
+ * and mutate is true, it is sorted in place and returned. Otherwise, the elements are first placed
+ * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
+ * <p>
+ * If the closure has two parameters
+ * it is used like a traditional Comparator. I.e. it should compare
+ * its two parameters for order, returning a negative integer,
+ * zero, or a positive integer when the first parameter is less than,
+ * equal to, or greater than the second respectively. Otherwise,
+ * the Closure is assumed to take a single parameter and return a
+ * Comparable (typically an Integer) which is then used for
+ * further comparison.
+ * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre>
+ * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
* <pre class="groovyTestCase">
- * def r = new Random()
- * def orig = ["a", 4, false]
- * def shuffled = orig.shuffled(r)
- * assert orig.size() == shuffled.size()
- * assert orig.every{ shuffled.contains(it) }
+ * def orig = ["hello","hi","Hey"]
+ * def sorted = orig.sort(false) { it.toUpperCase() }
+ * assert orig == ["hello","hi","Hey"]
+ * assert sorted == ["hello","Hey","hi"]
* </pre>
*
- * @param self a List
- * @see Collections#shuffle(List)
- * @since 3.0.0
+ * @param self the Iterable to be sorted
+ * @param mutate false will always cause a new list to be created, true will mutate lists in place
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return a newly created sorted List
+ * @since 2.2.0
*/
- public static <T> List<T> shuffled(List<T> self, Random rnd) {
- List<T> copy = new ArrayList<>(self);
- Collections.shuffle(copy, rnd);
- return copy;
+ public static <T> List<T> sort(Iterable<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ List<T> list = mutate ? asList(self) : toList(self);
+ // use a comparator of one item or two
+ int params = closure.getMaximumNumberOfParameters();
+ if (params == 1) {
+ list.sort(new OrderBy<>(closure));
+ } else {
+ list.sort(new ClosureComparator<>(closure));
+ }
+ return list;
}
/**
- * Creates a new List with the identical contents to this list
- * but in reverse order.
- * <pre class="groovyTestCase">
- * def list = ["a", 4, false]
- * assert list.reverse() == [false, 4, "a"]
- * assert list == ["a", 4, false]
- * </pre>
+ * Avoids doing unnecessary work when sorting an already sorted set (i.e. an identity function for an already sorted set).
*
- * @param self a List
- * @return a reversed List
- * @see #reverse(List, boolean)
+ * @param self an already sorted set
+ * @return the set
* @since 1.0
*/
- public static <T> List<T> reverse(List<T> self) {
- return reverse(self, false);
+ public static <T> SortedSet<T> sort(SortedSet<T> self) {
+ return self;
}
/**
- * Reverses the elements in a list. If mutate is true, the original list is modified in place and returned.
- * Otherwise, a new list containing the reversed items is produced.
- * <pre class="groovyTestCase">
- * def list = ["a", 4, false]
- * assert list.reverse(false) == [false, 4, "a"]
- * assert list == ["a", 4, false]
- * assert list.reverse(true) == [false, 4, "a"]
- * assert list == [false, 4, "a"]
- * </pre>
+ * Avoids doing unnecessary work when sorting an already sorted map (i.e. an identity function for an already sorted map).
*
- * @param self a List
- * @param mutate true if the list itself should be reversed in place and returned, false if a new list should be created
- * @return a reversed List
+ * @param self an already sorted map
+ * @return the map
* @since 1.8.1
*/
- public static <T> List<T> reverse(List<T> self, boolean mutate) {
- if (mutate) {
- Collections.reverse(self);
- return self;
- }
- int size = self.size();
- List<T> answer = new ArrayList<>(size);
- ListIterator<T> iter = self.listIterator(size);
- while (iter.hasPrevious()) {
- answer.add(iter.previous());
- }
- return answer;
+ public static <K, V> SortedMap<K, V> sort(SortedMap<K, V> self) {
+ return self;
}
/**
- * Reverses the iterator. The original iterator will become
- * exhausted of elements after determining the reversed values.
- * A new iterator for iterating through the reversed values is returned.
+ * Sorts the Iterable. Assumes that the Iterable elements are
+ * comparable and uses a {@link NumberAwareComparator} to determine the resulting order.
+ * {@code NumberAwareComparator} has special treatment for numbers but otherwise uses the
+ * natural ordering of the Iterable elements. The elements are first placed into a new list which
+ * is then sorted and returned - leaving the original Iterable unchanged.
+ * <pre class="groovyTestCase">
+ * def orig = [1, 3, 2]
+ * def sorted = orig.toSorted()
+ * assert orig == [1, 3, 2]
+ * assert sorted == [1, 2, 3]
+ * </pre>
*
- * @param self an Iterator
- * @return a reversed Iterator
- * @since 1.5.5
+ * @param self the Iterable to be sorted
+ * @return the sorted iterable as a List
+ * @see #toSorted(Iterable, Comparator)
+ * @since 2.4.0
*/
- public static <T> Iterator<T> reverse(Iterator<T> self) {
- return new ReverseListIterator<>(toList(self));
+ public static <T> List<T> toSorted(Iterable<T> self) {
+ return toSorted(self, new NumberAwareComparator<>());
}
/**
- * Create a Collection as a union of two collections. If the left collection
- * is a Set, then the returned collection will be a Set otherwise a List.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3,4] == [1,2] + [3,4]</pre>
+ * Sorts the Iterable using the given Comparator. The elements are first placed
+ * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
+ * <pre class="groovyTestCase">
+ * def orig = ["hello","hi","Hey"]
+ * def sorted = orig.toSorted(String.CASE_INSENSITIVE_ORDER)
+ * assert orig == ["hello","hi","Hey"]
+ * assert sorted == ["hello","Hey","hi"]
+ * </pre>
*
- * @param left the left Collection
- * @param right the right Collection
- * @return the merged Collection
- * @since 1.5.0
+ * @param self the Iterable to be sorted
+ * @param comparator a Comparator used for the comparison
+ * @return a sorted List
+ * @since 2.4.0
*/
- public static <T> Collection<T> plus(Collection<T> left, Collection<T> right) {
- final Collection<T> answer = cloneSimilarCollection(left, left.size() + right.size());
- answer.addAll(right);
- return answer;
+ public static <T> List<T> toSorted(Iterable<T> self, Comparator<? super T> comparator) {
+ List<T> list = toList(self);
+ list.sort(comparator);
+ return list;
}
/**
- * Create a Collection as a union of two iterables. If the left iterable
- * is a Set, then the returned collection will be a Set otherwise a List.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3,4] == [1,2] + [3,4]</pre>
+ * Sorts this Iterable using the given Closure to determine the correct ordering. The elements are first placed
+ * into a new list which is then sorted and returned - leaving the original Iterable unchanged.
+ * <p>
+ * If the Closure has two parameters
+ * it is used like a traditional Comparator. I.e. it should compare
+ * its two parameters for order, returning a negative integer,
+ * zero, or a positive integer when the first parameter is less than,
+ * equal to, or greater than the second respectively. Otherwise,
+ * the Closure is assumed to take a single parameter and return a
+ * Comparable (typically an Integer) which is then used for
+ * further comparison.
+ * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre>
+ * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
*
- * @param left the left Iterable
- * @param right the right Iterable
- * @return the merged Collection
+ * @param self the Iterable to be sorted
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return a newly created sorted List
+ * @see #toSorted(Iterable, Comparator)
* @since 2.4.0
*/
- public static <T> Collection<T> plus(Iterable<T> left, Iterable<T> right) {
- return plus(asCollection(left), asCollection(right));
+ public static <T> List<T> toSorted(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(
+ closure);
+ return toSorted(self, comparator);
}
/**
- * Create a Collection as a union of a Collection and an Iterable. If the left collection
- * is a Set, then the returned collection will be a Set otherwise a List.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Sorts the Iterator. Assumes that the Iterator elements are
+ * comparable and uses a {@link NumberAwareComparator} to determine the resulting order.
+ * {@code NumberAwareComparator} has special treatment for numbers but otherwise uses the
+ * natural ordering of the Iterator elements.
+ * A new iterator is produced that traverses the items in sorted order.
*
- * @param left the left Collection
- * @param right the right Iterable
- * @return the merged Collection
- * @since 1.8.7
- * @see #plus(Collection, Collection)
+ * @param self the Iterator to be sorted
+ * @return the sorted items as an Iterator
+ * @see #toSorted(Iterator, Comparator)
+ * @since 2.4.0
*/
- public static <T> Collection<T> plus(Collection<T> left, Iterable<T> right) {
- return plus(left, asCollection(right));
+ public static <T> Iterator<T> toSorted(Iterator<T> self) {
+ return toSorted(self, new NumberAwareComparator<>());
}
/**
- * Create a List as a union of a List and an Iterable.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Sorts the given iterator items using the comparator. The
+ * original iterator will become exhausted of elements after completing this method call.
+ * A new iterator is produced that traverses the items in sorted order.
*
- * @param left the left List
- * @param right the right Iterable
- * @return the merged List
+ * @param self the Iterator to be sorted
+ * @param comparator a Comparator used for comparing items
+ * @return the sorted items as an Iterator
* @since 2.4.0
- * @see #plus(Collection, Collection)
*/
- public static <T> List<T> plus(List<T> left, Iterable<T> right) {
- return (List<T>) plus((Collection<T>) left, asCollection(right));
+ public static <T> Iterator<T> toSorted(Iterator<T> self, Comparator<? super T> comparator) {
+ return toSorted(toList(self), comparator).listIterator();
}
/**
- * Create a List as a union of a List and a Collection.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Sorts the given iterator items into a sorted iterator using the Closure to determine the correct ordering.
+ * The original iterator will be fully processed after the method call.
+ * <p>
+ * If the closure has two parameters it is used like a traditional Comparator.
+ * I.e. it should compare its two parameters for order, returning a negative integer,
+ * zero, or a positive integer when the first parameter is less than, equal to,
+ * or greater than the second respectively. Otherwise, the Closure is assumed
+ * to take a single parameter and return a Comparable (typically an Integer)
+ * which is then used for further comparison.
*
- * @param left the left List
- * @param right the right Collection
- * @return the merged List
+ * @param self the Iterator to be sorted
+ * @param closure a Closure used to determine the correct ordering
+ * @return the sorted items as an Iterator
+ * @see #toSorted(Iterator, Comparator)
* @since 2.4.0
- * @see #plus(Collection, Collection)
*/
- public static <T> List<T> plus(List<T> left, Collection<T> right) {
- return (List<T>) plus((Collection<T>) left, right);
+ public static <T> Iterator<T> toSorted(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(
+ closure);
+ return toSorted(self, comparator);
}
/**
- * Create a Set as a union of a Set and an Iterable.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Sorts the elements from the given map into a new ordered map using
+ * a {@link NumberAwareComparator} on map entry values to determine the resulting order.
+ * {@code NumberAwareComparator} has special treatment for numbers but otherwise uses the
+ * natural ordering of the Iterator elements. The original map is unchanged.
+ * <pre class="groovyTestCase">
+ * def map = [a:5L, b:3, c:6, d:4.0].toSorted()
+ * assert map.toString() == '[b:3, d:4.0, a:5, c:6]'
+ * </pre>
*
- * @param left the left Set
- * @param right the right Iterable
- * @return the merged Set
+ * @param self the original unsorted map
+ * @return the sorted map
* @since 2.4.0
- * @see #plus(Collection, Collection)
*/
- public static <T> Set<T> plus(Set<T> left, Iterable<T> right) {
- return (Set<T>) plus((Collection<T>) left, asCollection(right));
+ public static <K, V> Map<K, V> toSorted(Map<K, V> self) {
+ return toSorted(self, new NumberAwareValueComparator<>());
}
/**
- * Create a Set as a union of a Set and a Collection.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Sorts the elements from the given map into a new ordered map using
+ * the supplied comparator to determine the ordering. The original map is unchanged.
+ * <pre class="groovyTestCase">
+ * def keyComparator = [compare: { e1, e2 {@code ->} e1.key {@code <=>} e2.key }] as Comparator
+ * def valueComparator = [compare: { e1, e2 {@code ->} e1.value {@code <=>} e2.value }] as Comparator
+ * def map1 = [a:5, b:3, d:4, c:6].toSorted(keyComparator)
+ * assert map1.toString() == '[a:5, b:3, c:6, d:4]'
+ * def map2 = [a:5, b:3, d:4, c:6].toSorted(valueComparator)
+ * assert map2.toString() == '[b:3, d:4, a:5, c:6]'
+ * </pre>
*
- * @param left the left Set
- * @param right the right Collection
- * @return the merged Set
+ * @param self the original unsorted map
+ * @param comparator a Comparator used for the comparison
+ * @return the sorted map
* @since 2.4.0
- * @see #plus(Collection, Collection)
*/
- public static <T> Set<T> plus(Set<T> left, Collection<T> right) {
- return (Set<T>) plus((Collection<T>) left, right);
+ public static <K, V> Map<K, V> toSorted(Map<K, V> self, Comparator<Map.Entry<K, V>> comparator) {
+ List<Map.Entry<K, V>> sortedEntries = toSorted(self.entrySet(), comparator);
+ Map<K, V> result = new LinkedHashMap<>();
+ for (Map.Entry<K, V> entry : sortedEntries) {
+ result.put(entry.getKey(), entry.getValue());
+ }
+ return result;
}
/**
- * Create a SortedSet as a union of a SortedSet and an Iterable.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Sorts the elements from the given map into a new ordered map using
+ * the supplied Closure condition as a comparator to determine the ordering. The original map is unchanged.
+ * <p>
+ * If the closure has two parameters it is used like a traditional Comparator. I.e. it should compare
+ * its two entry parameters for order, returning a negative integer, zero, or a positive integer when the
+ * first parameter is less than, equal to, or greater than the second respectively. Otherwise,
+ * the Closure is assumed to take a single entry parameter and return a Comparable (typically an Integer)
+ * which is then used for further comparison.
+ * <pre class="groovyTestCase">
+ * def map = [a:5, b:3, c:6, d:4].toSorted { a, b {@code ->} a.value {@code <=>} b.value }
+ * assert map.toString() == '[b:3, d:4, a:5, c:6]'
+ * </pre>
*
- * @param left the left SortedSet
- * @param right the right Iterable
- * @return the merged SortedSet
+ * @param self the original unsorted map
+ * @param condition a Closure used as a comparator
+ * @return the sorted map
* @since 2.4.0
- * @see #plus(Collection, Collection)
*/
- public static <T> SortedSet<T> plus(SortedSet<T> left, Iterable<T> right) {
- return (SortedSet<T>) plus((Collection<T>) left, asCollection(right));
+ public static <K, V> Map<K, V> toSorted(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>","Map.Entry<K,V>,Map.Entry<K,V>"}) Closure condition) {
+ Comparator<Map.Entry<K,V>> comparator = (condition.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(
+ condition) : new ClosureComparator<>(condition);
+ return toSorted(self, comparator);
}
/**
- * Create a SortedSet as a union of a SortedSet and a Collection.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Avoids doing unnecessary work when sorting an already sorted set
*
- * @param left the left SortedSet
- * @param right the right Collection
- * @return the merged SortedSet
+ * @param self an already sorted set
+ * @return an ordered copy of the sorted set
* @since 2.4.0
- * @see #plus(Collection, Collection)
*/
- public static <T> SortedSet<T> plus(SortedSet<T> left, Collection<T> right) {
- return (SortedSet<T>) plus((Collection<T>) left, right);
+ public static <T> Set<T> toSorted(SortedSet<T> self) {
+ return new LinkedHashSet<>(self);
}
/**
- * Creates a new List by inserting all the elements in the specified array
- * to the elements from the original List at the specified index.
- * Shifts the element currently at that index (if any) and any subsequent
- * elements to the right (increasing their indices).
- * The new elements will appear in the resulting List in the order that
- * they occur in the original array.
- * The behavior of this operation is undefined if the list or
- * array operands are modified while the operation is in progress.
- * The original list and array operands remain unchanged.
- *
- * <pre class="groovyTestCase">
- * def items = [1, 2, 3]
- * def newItems = items.plus(2, 'a'..'c' as String[])
- * assert newItems == [1, 2, 'a', 'b', 'c', 3]
- * assert items == [1, 2, 3]
- * </pre>
- *
- * See also <code>addAll</code> for similar functionality with modify semantics, i.e. which performs
- * the changes on the original list itself.
+ * Avoids doing unnecessary work when sorting an already sorted map
*
- * @param self an original list
- * @param items array containing elements to be merged with elements from the original list
- * @param index index at which to insert the first element from the specified array
- * @return the new list
- * @see #plus(List, int, List)
- * @since 1.8.1
+ * @param self an already sorted map
+ * @return an ordered copy of the map
+ * @since 2.4.0
*/
- public static <T> List<T> plus(List<T> self, int index, T[] items) {
- return plus(self, index, Arrays.asList(items));
+ public static <K, V> Map<K, V> toSorted(SortedMap<K, V> self) {
+ return new LinkedHashMap<>(self);
}
/**
- * Creates a new List by inserting all the elements in the given additions List
- * to the elements from the original List at the specified index.
- * Shifts the element currently at that index (if any) and any subsequent
- * elements to the right (increasing their indices). The new elements
- * will appear in the resulting List in the order that they occur in the original lists.
- * The behavior of this operation is undefined if the original lists
- * are modified while the operation is in progress. The original lists remain unchanged.
+ * Removes the last item from the List.
*
* <pre class="groovyTestCase">
- * def items = [1, 2, 3]
- * def newItems = items.plus(2, 'a'..'c')
- * assert newItems == [1, 2, 'a', 'b', 'c', 3]
- * assert items == [1, 2, 3]
+ * def list = ["a", false, 2]
+ * assert list.removeLast() == 2
+ * assert list == ["a", false]
* </pre>
*
- * See also <code>addAll</code> for similar functionality with modify semantics, i.e. which performs
- * the changes on the original list itself.
+ * Using add() and removeLast() is similar to push and pop on a Stack
+ * where the last item in the list represents the top of the stack.
*
- * @param self an original List
- * @param additions a List containing elements to be merged with elements from the original List
- * @param index index at which to insert the first element from the given additions List
- * @return the new list
- * @since 1.8.1
+ * @param self a List
+ * @return the item removed from the List
+ * @throws NoSuchElementException if the list is empty
+ * @since 2.5.0
*/
- public static <T> List<T> plus(List<T> self, int index, List<T> additions) {
- final List<T> answer = new ArrayList<>(self);
- answer.addAll(index, additions);
- return answer;
+ public static <T> T removeLast(List<T> self) {
+ if (self.isEmpty()) {
+ throw new NoSuchElementException("Cannot removeLast() an empty List");
+ }
+ return self.remove(self.size() - 1);
}
/**
- * Creates a new List by inserting all the elements in the given Iterable
- * to the elements from this List at the specified index.
+ * Returns the items from the List excluding the first item.
+ * <pre class="groovyTestCase">
+ * def list = [3, 4, 2]
+ * assert list.tail() == [4, 2]
+ * assert list == [3, 4, 2]
+ * </pre>
*
- * @param self an original list
- * @param additions an Iterable containing elements to be merged with the elements from the original List
- * @param index index at which to insert the first element from the given additions Iterable
- * @return the new list
- * @since 1.8.7
- * @see #plus(List, int, List)
+ * @param self a List
+ * @return a List without its first element
+ * @throws NoSuchElementException if you try to access tail() for an empty List
+ * @since 1.5.6
*/
- public static <T> List<T> plus(List<T> self, int index, Iterable<T> additions) {
- return plus(self, index, toList(additions));
+ public static <T> List<T> tail(List<T> self) {
+ return (List<T>) tail((Iterable<T>)self);
}
/**
- * Create a collection as a union of a Collection and an Object. If the collection
- * is a Set, then the returned collection will be a Set otherwise a List.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
+ * Returns the items from the SortedSet excluding the first item.
+ * <pre class="groovyTestCase">
+ * def sortedSet = [3, 4, 2] as SortedSet
+ * assert sortedSet.tail() == [3, 4] as SortedSet
+ * assert sortedSet == [3, 4, 2] as SortedSet
+ * </pre>
*
- * @param left a Collection
- * @param right an object to add/append
- * @return the resulting Collection
- * @since 1.5.0
+ * @param self a SortedSet
+ * @return a SortedSet without its first element
+ * @throws NoSuchElementException if you try to access tail() for an empty SortedSet
+ * @since 2.4.0
*/
- public static <T> Collection<T> plus(Collection<T> left, T right) {
- final Collection<T> answer = cloneSimilarCollection(left, left.size() + 1);
- answer.add(right);
- return answer;
+ public static <T> SortedSet<T> tail(SortedSet<T> self) {
+ return (SortedSet<T>) tail((Iterable<T>) self);
}
/**
- * Create a collection as a union of an Iterable and an Object. If the iterable
- * is a Set, then the returned collection will be a Set otherwise a List.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
+ * Calculates the tail values of this Iterable: the first value will be this list of all items from the iterable and the final one will be an empty list, with the intervening values the results of successive applications of tail on the items.
+ * <pre class="groovyTestCase">
+ * assert [1, 2, 3, 4].tails() == [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []]
+ * </pre>
*
- * @param left an Iterable
- * @param right an object to add/append
- * @return the resulting Collection
- * @since 2.4.0
+ * @param self an Iterable
+ * @return a List of the tail values from the given Iterable
+ * @since 2.5.0
*/
- public static <T> Collection<T> plus(Iterable<T> left, T right) {
- return plus(asCollection(left), right);
+ public static <T> List<List<T>> tails(Iterable<T> self) {
+ return GroovyCollections.tails(self);
}
/**
- * Create a List as a union of a List and an Object.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
+ * Returns the items from the Iterable excluding the first item.
+ * <pre class="groovyTestCase">
+ * def list = [3, 4, 2]
+ * assert list.tail() == [4, 2]
+ * assert list == [3, 4, 2]
+ * </pre>
*
- * @param left a List
- * @param right an object to add/append
- * @return the resulting List
+ * @param self an Iterable
+ * @return a collection without its first element
+ * @throws NoSuchElementException if you try to access tail() for an empty Iterable
* @since 2.4.0
*/
- public static <T> List<T> plus(List<T> left, T right) {
- return (List<T>) plus((Collection<T>) left, right);
+ public static <T> Collection<T> tail(Iterable<T> self) {
+ if (!self.iterator().hasNext()) {
+ throw new NoSuchElementException("Cannot access tail() for an empty iterable");
+ }
+ Collection<T> result = createSimilarCollection(self);
+ addAll(result, tail(self.iterator()));
+ return result;
}
/**
- * Create a Set as a union of a Set and an Object.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
+ * Returns the original iterator after throwing away the first element.
*
- * @param left a Set
- * @param right an object to add/append
- * @return the resulting Set
- * @since 2.4.0
+ * @param self the original iterator
+ * @return the iterator without its first element
+ * @throws NoSuchElementException if you try to access tail() for an exhausted/empty Iterator
+ * @since 1.8.1
*/
- public static <T> Set<T> plus(Set<T> left, T right) {
- return (Set<T>) plus((Collection<T>) left, right);
+ public static <T> Iterator<T> tail(Iterator<T> self) {
+ if (!self.hasNext()) {
+ throw new NoSuchElementException("Cannot access tail() for an empty Iterator");
+ }
+ self.next();
+ return self;
}
/**
- * Create a SortedSet as a union of a SortedSet and an Object.
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre>
+ * Returns the first <code>num</code> elements from the head of this List.
+ * <pre class="groovyTestCase">
+ * def strings = [ 'a', 'b', 'c' ]
+ * assert strings.take( 0 ) == []
+ * assert strings.take( 2 ) == [ 'a', 'b' ]
+ * assert strings.take( 5 ) == [ 'a', 'b', 'c' ]
+ * </pre>
*
- * @param left a SortedSet
- * @param right an object to add/append
- * @return the resulting SortedSet
- * @since 2.4.0
+ * @param self the original List
+ * @param num the number of elements to take from this List
+ * @return a List consisting of the first <code>num</code> elements from this List,
+ * or else all the elements from the List if it has less than <code>num</code> elements.
+ * @since 1.8.1
*/
- public static <T> SortedSet<T> plus(SortedSet<T> left, T right) {
- return (SortedSet<T>) plus((Collection<T>) left, right);
+ public static <T> List<T> take(List<T> self, int num) {
+ return (List<T>) take((Iterable<T>)self, num);
}
/**
- * Create a Collection composed of the union of both collections. Any
- * elements that exist in either collections are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
- * For collections of custom objects; the objects should implement java.lang.Comparable
- * <pre class="groovyTestCase">assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])</pre>
- * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
- * element exists in the resultant collection.
+ * Returns the first <code>num</code> elements from the head of this SortedSet.
+ * <pre class="groovyTestCase">
+ * def strings = [ 'a', 'b', 'c' ] as SortedSet
+ * assert strings.take( 0 ) == [] as SortedSet
+ * assert strings.take( 2 ) == [ 'a', 'b' ] as SortedSet
+ * assert strings.take( 5 ) == [ 'a', 'b', 'c' ] as SortedSet
+ * </pre>
*
- * @param left a Collection
- * @param right a Collection
- * @return a Collection as a union of both collections
- * @see #union(Collection, Collection, Comparator)
- * @since 5.0.0
+ * @param self the original SortedSet
+ * @param num the number of elements to take from this SortedSet
+ * @return a SortedSet consisting of the first <code>num</code> elements from this List,
+ * or else all the elements from the SortedSet if it has less than <code>num</code> elements.
+ * @since 2.4.0
*/
- public static <T> Collection<T> union(Collection<T> left, Collection<T> right) {
- return union(left, right, new NumberAwareComparator<>());
+ public static <T> SortedSet<T> take(SortedSet<T> self, int num) {
+ return (SortedSet<T>) take((Iterable<T>) self, num);
}
/**
- * Create a Collection composed of the union of both collections. Any
- * elements that exist in either collections are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
- * For collections of custom objects; the objects should implement java.lang.Comparable
- * <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6], Comparator.naturalOrder())
- * assert [4,8,12,16,20,1,3] == [4,8,12,16,20].union([1,2,3,4], (x, y) {@code -> x * x <=> y})
- * </pre>
+ * Returns the first <code>num</code> elements from the head of this Iterable.
* <pre class="groovyTestCase">
- * def one = ['a', 'B', 'c', 'd']
- * def two = ['b', 'C', 'd', 'e']
- * def compareIgnoreCase = { a, b {@code ->} a.toLowerCase() {@code <=>} b.toLowerCase() }
- * assert one.union(two) == ['a', 'B', 'c', 'd', 'b', 'C', 'e']
- * assert two.union(one) == ['b', 'C', 'd', 'e', 'a', 'B', 'c']
- * assert one.union(two, compareIgnoreCase) == ['a', 'B', 'c', 'd', 'e']
- * assert two.union(one, compareIgnoreCase) == ['b', 'C', 'd', 'e', 'a']
- * </pre>
+ * def strings = [ 'a', 'b', 'c' ]
+ * assert strings.take( 0 ) == []
+ * assert strings.take( 2 ) == [ 'a', 'b' ]
+ * assert strings.take( 5 ) == [ 'a', 'b', 'c' ]
*
- * @param left a Collection
- * @param right a Collection
- * @param comparator a Comparator
- * @return a Collection as a union of both collections
- * @since 5.0.0
- */
- public static <T> Collection<T> union(Collection<T> left, Collection<T> right, Comparator<? super T> comparator) {
- if (left.isEmpty() && right.isEmpty()) {
- return createSimilarCollection(left, 0);
- }
-
- Collection<T> result = createSimilarCollection(left, left.size() + right.size());
- //creates the collection to look for values.
- Collection<T> compareWith = new TreeSet<>(comparator);
-
- for (final T leftElement : left) {
- if (compareWith.add(leftElement)) {
- result.add(leftElement);
- }
- }
-
- for (final T rightElement : right) {
- if (compareWith.add(rightElement)) {
- result.add(rightElement);
- }
- }
+ * class AbcIterable implements Iterable<String> {
+ * Iterator<String> iterator() { "abc".iterator() }
+ * }
+ * def abc = new AbcIterable()
+ * assert abc.take(0) == []
+ * assert abc.take(1) == ['a']
+ * assert abc.take(3) == ['a', 'b', 'c']
+ * assert abc.take(5) == ['a', 'b', 'c']
+ * </pre>
+ *
+ * @param self the original Iterable
+ * @param num the number of elements to take from this Iterable
+ * @return a Collection consisting of the first <code>num</code> elements from this Iterable,
+ * or else all the elements from the Iterable if it has less than <code>num</code> elements.
+ * @since 1.8.7
+ */
+ public static <T> Collection<T> take(Iterable<T> self, int num) {
+ Collection<T> result = self instanceof Collection ? createSimilarCollection((Collection<T>) self, Math.max(num, 0)) : new ArrayList<>();
+ addAll(result, take(self.iterator(), num));
return result;
}
/**
- * Create a Collection composed of the union of both iterables. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
- * For iterables of custom objects; the objects should implement java.lang.Comparable
+ * Returns a new map containing the first <code>num</code> elements from the head of this map.
+ * If the map instance does not have ordered keys, then this function could return a random <code>num</code>
+ * entries. Groovy by default uses LinkedHashMap, so this shouldn't be an issue in the main.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])
+ * def strings = [ 'a':10, 'b':20, 'c':30 ]
+ * assert strings.take( 0 ) == [:]
+ * assert strings.take( 2 ) == [ 'a':10, 'b':20 ]
+ * assert strings.take( 5 ) == [ 'a':10, 'b':20, 'c':30 ]
* </pre>
- * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
- * element already exists in the resultant collection.
*
- * @param left an Iterable
- * @param right an Iterable
- * @return a Collection as a union of both iterables
- * @see #union(Iterable, Iterable, Comparator)
- * @since 5.0.0
+ * @param self the original map
+ * @param num the number of elements to take from this map
+ * @return a new map consisting of the first <code>num</code> elements of this map,
+ * or else the whole map if it has less than <code>num</code> elements.
+ * @since 1.8.1
*/
- public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right) {
- return union(asCollection(left), asCollection(right));
+ public static <K, V> Map<K, V> take(Map<K, V> self, int num) {
+ if (self.isEmpty() || num <= 0) {
+ return createSimilarMap(self);
+ }
+ Map<K, V> ret = createSimilarMap(self);
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ K key = entry.getKey();
+ V value = entry.getValue();
+
+ ret.put(key, value);
+ if (--num <= 0) {
+ break;
+ }
+ }
+ return ret;
}
/**
- * Create a Collection composed of the union of both iterables. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
- * For iterables of custom objects; the objects should implement java.lang.Comparable
+ * Returns an iterator of up to the first <code>num</code> elements from this iterator.
+ * The original iterator is stepped along by <code>num</code> elements.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6], Comparator.naturalOrder())
+ * def a = 0
+ * def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator
+ * def iteratorCompare( Iterator a, List b ) {
+ * a.collect { it } == b
+ * }
+ * assert iteratorCompare( iter.take( 0 ), [] )
+ * assert iteratorCompare( iter.take( 2 ), [ 0, 1 ] )
+ * assert iteratorCompare( iter.take( 5 ), [ 2, 3, 4, 5, 6 ] )
* </pre>
*
- * @param left an Iterable
- * @param right an Iterable
- * @param comparator a Comparator
- * @return a Collection as a union of both iterables
- * @since 5.0.0
+ * @param self the Iterator
+ * @param num the number of elements to take from this iterator
+ * @return an iterator consisting of up to the first <code>num</code> elements of this iterator.
+ * @since 1.8.1
*/
- public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right, Comparator<? super T> comparator) {
- return union(asCollection(left), asCollection(right), comparator);
+ public static <T> Iterator<T> take(Iterator<T> self, int num) {
+ return new TakeIterator<>(self, num);
+ }
+
+ private static final class TakeIterator<E> implements Iterator<E> {
+ private final Iterator<E> delegate;
+ private Integer num;
+
+ private TakeIterator(Iterator<E> delegate, Integer num) {
+ this.delegate = delegate;
+ this.num = num;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return num > 0 && delegate.hasNext();
+ }
+
+ @Override
+ public E next() {
+ if (num <= 0) throw new NoSuchElementException();
+ num--;
+ return delegate.next();
+ }
+
+ @Override
+ public void remove() {
+ delegate.remove();
+ }
}
/**
- * Create a Collection composed of the union of both iterables.
- * Elements from the first iterable and second iterable are added to the result if the elements are not
- * already in the result (according to the comparator closure).
- * If the closure takes a single parameter, the argument passed will be each element,
- * and the closure should return a value used for comparison (either using
- * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
- * If the closure takes two parameters, two items from the Iterator
- * will be passed as arguments, and the closure should return an
- * int value (with 0 indicating the items are deemed equal).
+ * Returns the last <code>num</code> elements from the tail of this Iterable.
* <pre class="groovyTestCase">
- * def one = ['a', 'B', 'c', 'd']
- * def two = ['b', 'C', 'd', 'e']
- * def compareIgnoreCase = { it.toLowerCase() }
- * assert one.union(two, compareIgnoreCase) == ['a', 'B', 'c', 'd', 'e']
- * assert two.union(one, compareIgnoreCase) == ['b', 'C', 'd', 'e', 'a']
+ * def strings = [ 'a', 'b', 'c' ]
+ * assert strings.takeRight( 0 ) == []
+ * assert strings.takeRight( 2 ) == [ 'b', 'c' ]
+ * assert strings.takeRight( 5 ) == [ 'a', 'b', 'c' ]
+ *
+ * class AbcIterable implements Iterable<String> {
+ * Iterator<String> iterator() { "abc".iterator() }
+ * }
+ * def abc = new AbcIterable()
+ * assert abc.takeRight(0) == []
+ * assert abc.takeRight(1) == ['c']
+ * assert abc.takeRight(3) == ['a', 'b', 'c']
+ * assert abc.takeRight(5) == ['a', 'b', 'c']
* </pre>
*
- * @param left an Iterable
- * @param right an Iterable
- * @param condition a Closure used to determine unique items
- * @return a Collection as a union of both iterables
- * @since 5.0.0
+ * @param self the original Iterable
+ * @param num the number of elements to take from this Iterable
+ * @return a Collection consisting of the last <code>num</code> elements from this Iterable,
+ * or else all the elements from the Iterable if it has less than <code>num</code> elements.
+ * @since 2.4.0
*/
- public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
- Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
- ? new OrderBy<>(condition, true)
- : new ClosureComparator<>(condition);
- return union(left, right, comparator);
+ public static <T> Collection<T> takeRight(Iterable<T> self, int num) {
+ if (num <= 0 || !self.iterator().hasNext()) {
+ return self instanceof Collection ? createSimilarCollection((Collection<T>) self, 0) : new ArrayList<>();
+ }
+ Collection<T> selfCol = self instanceof Collection ? (Collection<T>) self : toList(self);
+ if (selfCol.size() <= num) {
+ Collection<T> ret = createSimilarCollection(selfCol, selfCol.size());
+ ret.addAll(selfCol);
+ return ret;
+ }
+ Collection<T> ret = createSimilarCollection(selfCol, num);
+ ret.addAll(asList(selfCol).subList(selfCol.size() - num, selfCol.size()));
+ return ret;
}
/**
- * Create a List composed of the union of a List and an Iterable. Any
- * elements that exist in both iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
+ * Returns the last <code>num</code> elements from the tail of this List.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])
+ * def strings = [ 'a', 'b', 'c' ]
+ * assert strings.takeRight( 0 ) == []
+ * assert strings.takeRight( 2 ) == [ 'b', 'c' ]
+ * assert strings.takeRight( 5 ) == [ 'a', 'b', 'c' ]
* </pre>
- * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
- * element already exists in the resultant collection.
*
- * @param left a List
- * @param right an Iterable
- * @return a List as a union of a List and an Iterable
- * @see #union(List, Iterable, Comparator)
- * @since 5.0.0
+ * @param self the original List
+ * @param num the number of elements to take from this List
+ * @return a List consisting of the last <code>num</code> elements from this List,
+ * or else all the elements from the List if it has less than <code>num</code> elements.
+ * @since 2.4.0
*/
- public static <T> List<T> union(List<T> left, Iterable<T> right) {
- return (List<T>) union((Collection<T>) left, asCollection(right));
+ public static <T> List<T> takeRight(List<T> self, int num) {
+ return (List<T>) takeRight((Iterable<T>) self, num);
}
/**
- * Create a List composed of the union of a List and an Iterable. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
+ * Returns the last <code>num</code> elements from the tail of this SortedSet.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6])
+ * def strings = [ 'a', 'b', 'c' ] as SortedSet
+ * assert strings.takeRight( 0 ) == [] as SortedSet
+ * assert strings.takeRight( 2 ) == [ 'b', 'c' ] as SortedSet
+ * assert strings.takeRight( 5 ) == [ 'a', 'b', 'c' ] as SortedSet
* </pre>
*
- * @param left a List
- * @param right an Iterable
- * @param comparator a Comparator
- * @return a List as a union of a List and an Iterable
- * @since 5.0.0
+ * @param self the original SortedSet
+ * @param num the number of elements to take from this SortedSet
+ * @return a SortedSet consisting of the last <code>num</code> elements from this SortedSet,
+ * or else all the elements from the SortedSet if it has less than <code>num</code> elements.
+ * @since 2.4.0
*/
- public static <T> List<T> union(List<T> left, Iterable<T> right, Comparator<? super T> comparator) {
- return (List<T>) union((Collection<T>) left, asCollection(right), comparator);
+ public static <T> SortedSet<T> takeRight(SortedSet<T> self, int num) {
+ return (SortedSet<T>) takeRight((Iterable<T>) self, num);
}
/**
- * Create a Set composed of the union of a Set and an Iterable. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
+ * Returns the longest prefix of this list where each element
+ * passed to the given closure condition evaluates to true.
+ * Similar to {@link #takeWhile(Iterable, groovy.lang.Closure)}
+ * except that it attempts to preserve the type of the original list.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6,7,8] as Set == ([1,2,3,4,5] as Set).union([4,5,6,7,8])
+ * def nums = [ 1, 3, 2 ]
+ * assert nums.takeWhile{ it {@code <} 1 } == []
+ * assert nums.takeWhile{ it {@code <} 3 } == [ 1 ]
+ * assert nums.takeWhile{ it {@code <} 4 } == [ 1, 3, 2 ]
* </pre>
- * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
- * element already exists in the resultant collection.
*
- * @param left a Set
- * @param right an Iterable
- * @return a Set as a union of a Set and an Iterable
- * @see #union(Set, Iterable, Comparator)
- * @since 5.0.0
+ * @param self the original list
+ * @param condition the closure that must evaluate to true to
+ * continue taking elements
+ * @return a prefix of the given list where each element passed to
+ * the given closure evaluates to true
+ * @since 1.8.7
*/
- public static <T> Set<T> union(Set<T> left, Iterable<T> right) {
- return (Set<T>) union((Collection<T>) left, asCollection(right));
+ public static <T> List<T> takeWhile(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ int num = 0;
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ for (T value : self) {
+ if (bcw.call(value)) {
+ num += 1;
+ } else {
+ break;
+ }
+ }
+ return take(self, num);
}
/**
- * Create a Set composed of the union of a Set and an Iterable. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
+ * Returns a Collection containing the longest prefix of the elements from this Iterable
+ * where each element passed to the given closure evaluates to true.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6] as Set == ([1,2,3,4] as Set).union([3,4,5,6], Comparator.naturalOrder())
+ * class AbcIterable implements Iterable<String> {
+ * Iterator<String> iterator() { "abc".iterator() }
+ * }
+ * def abc = new AbcIterable()
+ * assert abc.takeWhile{ it {@code <} 'b' } == ['a']
+ * assert abc.takeWhile{ it {@code <=} 'b' } == ['a', 'b']
* </pre>
*
- * @param left a Set
- * @param right an Iterable
- * @param comparator a Comparator
- * @return a Set as a union of a Set and an Iterable
- * @since 5.0.0
+ * @param self an Iterable
+ * @param condition the closure that must evaluate to true to
+ * continue taking elements
+ * @return a Collection containing a prefix of the elements from the given Iterable where
+ * each element passed to the given closure evaluates to true
+ * @since 1.8.7
*/
- public static <T> Set<T> union(Set<T> left, Iterable<T> right, Comparator<? super T> comparator) {
- return (Set<T>) union((Collection<T>) left, asCollection(right), comparator);
+ public static <T> Collection<T> takeWhile(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ Collection<T> result = createSimilarCollection(self);
+ addAll(result, takeWhile(self.iterator(), condition));
+ return result;
}
/**
- * Create a SortedSet composed of the union of a SortedSet and an Iterable. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
+ * Returns the longest prefix of this SortedSet where each element
+ * passed to the given closure condition evaluates to true.
+ * Similar to {@link #takeWhile(Iterable, groovy.lang.Closure)}
+ * except that it attempts to preserve the type of the original SortedSet.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6,7,8] as SortedSet == ([1,2,3,4,5] as SortedSet).union([4,5,6,7,8])
+ * def nums = [ 1, 2, 3 ] as SortedSet
+ * assert nums.takeWhile{ it {@code <} 1 } == [] as SortedSet
+ * assert nums.takeWhile{ it {@code <} 2 } == [ 1 ] as SortedSet
+ * assert nums.takeWhile{ it {@code <} 4 } == [ 1, 2, 3 ] as SortedSet
* </pre>
- * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
- * element already exists in the resultant collection.
*
- * @param left a SortedSet
- * @param right an Iterable
- * @return a Set as a union of a SortedSet and an Iterable
- * @see #intersect(SortedSet, Iterable, Comparator)
- * @since 5.0.0
+ * @param self the original SortedSet
+ * @param condition the closure that must evaluate to true to
+ * continue taking elements
+ * @return a prefix of the given SortedSet where each element passed to
+ * the given closure evaluates to true
+ * @since 2.4.0
*/
- public static <T> SortedSet<T> union(SortedSet<T> left, Iterable<T> right) {
- return (SortedSet<T>) union((Collection<T>) left, asCollection(right));
+ public static <T> SortedSet<T> takeWhile(SortedSet<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return (SortedSet<T>) takeWhile((Iterable<T>) self, condition);
}
/**
- * Create a SortedSet composed of the intersection of a SortedSet and an Iterable. Any
- * elements that exist in either iterables are added to the resultant collection, such
- * that no elements are duplicated in the resultant collection.
+ * Returns the longest prefix of this Map where each entry (or key/value pair) when
+ * passed to the given closure evaluates to true.
* <pre class="groovyTestCase">
- * assert [1,2,3,4,5,6,7,8] as SortedSet == ([1,2,3,4,5] as SortedSet).union([4,5,6,7,8])
+ * def shopping = [milk:1, bread:2, chocolate:3]
+ * assert shopping.takeWhile{ it.key.size() {@code <} 6 } == [milk:1, bread:2]
+ * assert shopping.takeWhile{ it.value % 2 } == [milk:1]
+ * assert shopping.takeWhile{ k, v {@code ->} k.size() + v {@code <=} 7 } == [milk:1, bread:2]
* </pre>
+ * If the map instance does not have ordered keys, then this function could appear to take random
+ * entries. Groovy by default uses LinkedHashMap, so this shouldn't be an issue in the main.
*
- * @param left a SortedSet
- * @param right an Iterable
- * @param comparator a Comparator
- * @return a Set as a union of a SortedSet and an Iterable
- * @since 2.5.0
+ * @param self a Map
+ * @param condition a 1 (or 2) arg Closure that must evaluate to true for the
+ * entry (or key and value) to continue taking elements
+ * @return a prefix of the given Map where each entry (or key/value pair) passed to
+ * the given closure evaluates to true
+ * @since 1.8.7
*/
- public static <T> SortedSet<T> union(SortedSet<T> left, Iterable<T> right, Comparator<? super T> comparator) {
- return (SortedSet<T>) union((Collection<T>) left, asCollection(right), comparator);
+ public static <K, V> Map<K, V> takeWhile(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
+ if (self.isEmpty()) {
+ return createSimilarMap(self);
+ }
+ Map<K, V> ret = createSimilarMap(self);
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ if (!bcw.callForMap(entry)) break;
+ ret.put(entry.getKey(), entry.getValue());
+ }
+ return ret;
}
/**
- * Implementation of the right shift operator for integral types. Non-integral
- * Number types throw UnsupportedOperationException.
+ * Returns the longest prefix of elements in this iterator where
+ * each element passed to the given condition closure evaluates to true.
+ * <p>
+ * <pre class="groovyTestCase">
+ * def a = 0
+ * def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator
*
- * @param self a Number object
- * @param operand the shift distance by which to right shift the number
- * @return the resulting number
- * @since 1.5.0
+ * assert [].iterator().takeWhile{ it {@code <} 3 }.toList() == []
+ * assert [1, 2, 3, 4, 5].iterator().takeWhile{ it {@code <} 3 }.toList() == [ 1, 2 ]
+ * assert iter.takeWhile{ it {@code <} 5 }.toList() == [ 0, 1, 2, 3, 4 ]
+ * </pre>
+ *
+ * @param self the Iterator
+ * @param condition the closure that must evaluate to true to
+ * continue taking elements
+ * @return a prefix of elements in the given iterator where each
+ * element passed to the given closure evaluates to true
+ * @since 1.8.7
*/
- public static Number rightShift(Number self, Number operand) {
- return NumberMath.rightShift(self, operand);
+ public static <T> Iterator<T> takeWhile(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return new TakeWhileIterator<>(self, condition);
}
- /**
- * Implementation of the right shift (unsigned) operator for integral types. Non-integral
- * Number types throw UnsupportedOperationException.
- *
- * @param self a Number object
- * @param operand the shift distance by which to right shift (unsigned) the number
- * @return the resulting number
- * @since 1.5.0
- */
- public static Number rightShiftUnsigned(Number self, Number operand) {
- return NumberMath.rightShiftUnsigned(self, operand);
+ private static final class TakeWhileIterator<E> implements Iterator<E> {
+ private final Iterator<E> delegate;
+ private final BooleanClosureWrapper condition;
+ private boolean exhausted;
+ private E next;
+
+ private TakeWhileIterator(Iterator<E> delegate, Closure condition) {
+ this.delegate = delegate;
+ this.condition = new BooleanClosureWrapper(condition);
+ advance();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !exhausted;
+ }
+
+ @Override
+ public E next() {
+ if (exhausted) throw new NoSuchElementException();
+ E result = next;
+ advance();
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ if (exhausted) throw new NoSuchElementException();
+ delegate.remove();
+ }
+
+ private void advance() {
+ exhausted = !delegate.hasNext();
+ if (!exhausted) {
+ next = delegate.next();
+ if (!condition.call(next)) {
+ exhausted = true;
+ next = null;
+ }
+ }
+ }
}
/**
- * Support assigning a range of values with a single assignment statement.
+ * Randomly reorders the elements of the specified list.
+ * <pre class="groovyTestCase">
+ * def list = ["a", 4, false]
+ * def origSize = list.size()
+ * def origCopy = new ArrayList(list)
+ * list.shuffle()
+ * assert list.size() == origSize
+ * assert origCopy.every{ list.contains(it) }
+ * </pre>
*
- * @param self a BitSet
- * @param range the range of values to set
- * @param value value
- * @see java.util.BitSet
- * @see groovy.lang.Range
- * @since 1.5.0
+ * @param self a List
+ * @see Collections#shuffle(List)
+ * @since 3.0.0
*/
- public static void putAt(BitSet self, IntRange range, boolean value) {
- RangeInfo info = subListBorders(self.length(), range);
- self.set(info.from, info.to, value);
+ public static void shuffle(List<?> self) {
+ Collections.shuffle(self);
}
/**
- * Support subscript-style assignment for a BitSet.
+ * Randomly reorders the elements of the specified list using the
+ * specified random instance as the source of randomness.
+ * <pre class="groovyTestCase">
+ * def r = new Random()
+ * def list = ["a", 4, false]
+ * def origSize = list.size()
+ * def origCopy = new ArrayList(list)
+ * list.shuffle(r)
+ * assert list.size() == origSize
+ * assert origCopy.every{ list.contains(it) }
+ * </pre>
*
- * @param self a BitSet
- * @param index index of the entry to set
- * @param value value
- * @see java.util.BitSet
- * @since 1.5.0
+ * @param self a List
+ * @see Collections#shuffle(List)
+ * @since 3.0.0
*/
- public static void putAt(BitSet self, int index, boolean value) {
- self.set(index, value);
+ public static void shuffle(List<?> self, Random rnd) {
+ Collections.shuffle(self, rnd);
}
/**
- * Convert a Collection to a Set. Always returns a new Set
- * even if the Collection is already a Set.
- * <p>
- * Example usage:
+ * Creates a new list containing the elements of the specified list
+ * but in a random order.
* <pre class="groovyTestCase">
- * def result = [1, 2, 2, 2, 3].toSet()
- * assert result instanceof Set
- * assert result == [1, 2, 3] as Set
+ * def orig = ["a", 4, false]
+ * def shuffled = orig.shuffled()
+ * assert orig.size() == shuffled.size()
+ * assert orig.every{ shuffled.contains(it) }
* </pre>
*
- * @param self a collection
- * @return a Set
- * @since 1.8.0
+ * @param self a List
+ * @see Collections#shuffle(List)
+ * @since 3.0.0
*/
- public static <T> Set<T> toSet(Collection<T> self) {
- Set<T> answer = new HashSet<>(self.size());
- answer.addAll(self);
- return answer;
+ public static <T> List<T> shuffled(List<T> self) {
+ List<T> copy = new ArrayList<>(self);
+ Collections.shuffle(copy);
+ return copy;
}
/**
- * Convert an Iterable to a Set. Always returns a new Set
- * even if the Iterable is already a Set.
- * <p>
- * Example usage:
+ * Creates a new list containing the elements of the specified list but in a random
+ * order using the specified random instance as the source of randomness.
* <pre class="groovyTestCase">
- * def result = [1, 2, 2, 2, 3].toSet()
- * assert result instanceof Set
- * assert result == [1, 2, 3] as Set
+ * def r = new Random()
+ * def orig = ["a", 4, false]
+ * def shuffled = orig.shuffled(r)
+ * assert orig.size() == shuffled.size()
+ * assert orig.every{ shuffled.contains(it) }
* </pre>
*
- * @param self an Iterable
- * @return a Set
- * @since 2.4.0
+ * @param self a List
+ * @see Collections#shuffle(List)
+ * @since 3.0.0
*/
- public static <T> Set<T> toSet(Iterable<T> self) {
- return toSet(self.iterator());
+ public static <T> List<T> shuffled(List<T> self, Random rnd) {
+ List<T> copy = new ArrayList<>(self);
+ Collections.shuffle(copy, rnd);
+ return copy;
}
/**
- * Convert an iterator to a Set. The iterator will become
- * exhausted of elements after making this conversion.
+ * Creates a new List with the identical contents to this list
+ * but in reverse order.
+ * <pre class="groovyTestCase">
+ * def list = ["a", 4, false]
+ * assert list.reverse() == [false, 4, "a"]
+ * assert list == ["a", 4, false]
+ * </pre>
*
- * @param self an iterator
- * @return a Set
- * @since 1.8.0
+ * @param self a List
+ * @return a reversed List
+ * @see #reverse(List, boolean)
+ * @since 1.0
*/
- public static <T> Set<T> toSet(Iterator<T> self) {
- Set<T> answer = new HashSet<>();
- while (self.hasNext()) {
- answer.add(self.next());
- }
- return answer;
+ public static <T> List<T> reverse(List<T> self) {
+ return reverse(self, false);
}
/**
- * Convert an enumeration to a Set.
+ * Reverses the elements in a list. If mutate is true, the original list is modified in place and returned.
+ * Otherwise, a new list containing the reversed items is produced.
+ * <pre class="groovyTestCase">
+ * def list = ["a", 4, false]
+ * assert list.reverse(false) == [false, 4, "a"]
+ * assert list == ["a", 4, false]
+ * assert list.reverse(true) == [false, 4, "a"]
+ * assert list == [false, 4, "a"]
+ * </pre>
*
- * @param self an enumeration
- * @return a Set
- * @since 1.8.0
+ * @param self a List
+ * @param mutate true if the list itself should be reversed in place and returned, false if a new list should be created
+ * @return a reversed List
+ * @since 1.8.1
*/
- public static <T> Set<T> toSet(Enumeration<T> self) {
- Set<T> answer = new HashSet<>();
- while (self.hasMoreElements()) {
- answer.add(self.nextElement());
+ public static <T> List<T> reverse(List<T> self, boolean mutate) {
+ if (mutate) {
+ Collections.reverse(self);
+ return self;
+ }
+ int size = self.size();
+ List<T> answer = new ArrayList<>(size);
+ ListIterator<T> iter = self.listIterator(size);
+ while (iter.hasPrevious()) {
+ answer.add(iter.previous());
}
return answer;
}
/**
- * Implements the setAt(int idx) method for primitive type arrays.
+ * Reverses the iterator. The original iterator will become
+ * exhausted of elements after determining the reversed values.
+ * A new iterator for iterating through the reversed values is returned.
*
- * @param self an object
- * @param idx the index of interest
- * @param newValue the new value to be put into the index of interest
- * @return the added value
- * @since 1.5.0
+ * @param self an Iterator
+ * @return a reversed Iterator
+ * @since 1.5.5
*/
- protected static Object primitiveArrayPut(Object self, int idx, Object newValue) {
- Array.set(self, normaliseIndex(idx, Array.getLength(self)), newValue);
- return newValue;
+ public static <T> Iterator<T> reverse(Iterator<T> self) {
+ return new ReverseListIterator<>(toList(self));
}
/**
- * Identity conversion which returns Boolean.TRUE for a true Boolean and Boolean.FALSE for a false Boolean.
+ * Create a Collection composed of the union of both collections. Any
+ * elements that exist in either collections are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * For collections of custom objects; the objects should implement java.lang.Comparable
+ * <pre class="groovyTestCase">assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])</pre>
+ * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+ * element exists in the resultant collection.
*
- * @param self a Boolean
- * @return the original Boolean
- * @since 1.7.6
+ * @param left a Collection
+ * @param right a Collection
+ * @return a Collection as a union of both collections
+ * @see #union(Collection, Collection, Comparator)
+ * @since 5.0.0
*/
- public static Boolean toBoolean(Boolean self) {
- return self;
+ public static <T> Collection<T> union(Collection<T> left, Collection<T> right) {
+ return union(left, right, new NumberAwareComparator<>());
}
/**
- * Returns the string representation of the given map.
+ * Create a Collection composed of the union of both collections. Any
+ * elements that exist in either collections are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * For collections of custom objects; the objects should implement java.lang.Comparable
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6], Comparator.naturalOrder())
+ * assert [4,8,12,16,20,1,3] == [4,8,12,16,20].union([1,2,3,4], (x, y) {@code -> x * x <=> y})
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def one = ['a', 'B', 'c', 'd']
+ * def two = ['b', 'C', 'd', 'e']
+ * def compareIgnoreCase = { a, b {@code ->} a.toLowerCase() {@code <=>} b.toLowerCase() }
+ * assert one.union(two) == ['a', 'B', 'c', 'd', 'b', 'C', 'e']
+ * assert two.union(one) == ['b', 'C', 'd', 'e', 'a', 'B', 'c']
+ * assert one.union(two, compareIgnoreCase) == ['a', 'B', 'c', 'd', 'e']
+ * assert two.union(one, compareIgnoreCase) == ['b', 'C', 'd', 'e', 'a']
+ * </pre>
*
- * @param self a Map
- * @return the string representation
- * @see #toMapString(java.util.Map)
- * @since 1.0
+ * @param left a Collection
+ * @param right a Collection
+ * @param comparator a Comparator
+ * @return a Collection as a union of both collections
+ * @since 5.0.0
*/
- public static String toString(AbstractMap self) {
- return toMapString(self);
+ public static <T> Collection<T> union(Collection<T> left, Collection<T> right, Comparator<? super T> comparator) {
+ if (left.isEmpty() && right.isEmpty()) {
+ return createSimilarCollection(left, 0);
+ }
+
+ Collection<T> result = createSimilarCollection(left, left.size() + right.size());
+ //creates the collection to look for values.
+ Collection<T> compareWith = new TreeSet<>(comparator);
+
+ for (final T leftElement : left) {
+ if (compareWith.add(leftElement)) {
+ result.add(leftElement);
+ }
+ }
+
+ for (final T rightElement : right) {
+ if (compareWith.add(rightElement)) {
+ result.add(rightElement);
+ }
+ }
+ return result;
}
/**
- * Returns the string representation of this map. The string displays the
- * contents of the map, i.e. <code>[one:1, two:2, three:3]</code>.
+ * Create a Collection composed of the union of both iterables. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * For iterables of custom objects; the objects should implement java.lang.Comparable
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])
+ * </pre>
+ * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+ * element already exists in the resultant collection.
*
- * @param self a Map
- * @return the string representation
- * @since 1.0
+ * @param left an Iterable
+ * @param right an Iterable
+ * @return a Collection as a union of both iterables
+ * @see #union(Iterable, Iterable, Comparator)
+ * @since 5.0.0
*/
- public static String toMapString(Map self) {
- return toMapString(self, -1);
+ public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right) {
+ return union(asCollection(left), asCollection(right));
}
/**
- * Returns the string representation of this map. The string displays the
- * contents of the map, i.e. <code>[one:1, two:2, three:3]</code>.
+ * Create a Collection composed of the union of both iterables. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * For iterables of custom objects; the objects should implement java.lang.Comparable
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6], Comparator.naturalOrder())
+ * </pre>
*
- * @param self a Map
- * @param maxSize stop after approximately this many characters and append '...'
- * @return the string representation
- * @since 1.0
+ * @param left an Iterable
+ * @param right an Iterable
+ * @param comparator a Comparator
+ * @return a Collection as a union of both iterables
+ * @since 5.0.0
*/
- public static String toMapString(Map self, int maxSize) {
- return (self == null) ? "null" : FormatHelper.toMapString(self, maxSize);
+ public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+ return union(asCollection(left), asCollection(right), comparator);
}
/**
- * Returns the string representation of the given collection. The string
- * displays the contents of the collection, i.e.
- * <code>[1, 2, a]</code>.
+ * Create a Collection composed of the union of both iterables.
+ * Elements from the first iterable and second iterable are added to the result if the elements are not
+ * already in the result (according to the comparator closure).
+ * If the closure takes a single parameter, the argument passed will be each element,
+ * and the closure should return a value used for comparison (either using
+ * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+ * If the closure takes two parameters, two items from the Iterator
+ * will be passed as arguments, and the closure should return an
+ * int value (with 0 indicating the items are deemed equal).
+ * <pre class="groovyTestCase">
+ * def one = ['a', 'B', 'c', 'd']
+ * def two = ['b', 'C', 'd', 'e']
+ * def compareIgnoreCase = { it.toLowerCase() }
+ * assert one.union(two, compareIgnoreCase) == ['a', 'B', 'c', 'd', 'e']
+ * assert two.union(one, compareIgnoreCase) == ['b', 'C', 'd', 'e', 'a']
+ * </pre>
*
- * @param self a Collection
- * @return the string representation
- * @see #toListString(java.util.Collection)
- * @since 1.0
+ * @param left an Iterable
+ * @param right an Iterable
+ * @param condition a Closure used to determine unique items
+ * @return a Collection as a union of both iterables
+ * @since 5.0.0
*/
- public static String toString(AbstractCollection self) {
- return toListString(self);
+ public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+ Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+ ? new OrderBy<>(condition, true)
+ : new ClosureComparator<>(condition);
+ return union(left, right, comparator);
}
/**
- * Returns the string representation of the given list. The string
- * displays the contents of the list, similar to a list literal, i.e.
- * <code>[1, 2, a]</code>.
+ * Create a List composed of the union of a List and an Iterable. Any
+ * elements that exist in both iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])
+ * </pre>
+ * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+ * element already exists in the resultant collection.
*
- * @param self a Collection
- * @return the string representation
- * @since 1.0
+ * @param left a List
+ * @param right an Iterable
+ * @return a List as a union of a List and an Iterable
+ * @see #union(List, Iterable, Comparator)
+ * @since 5.0.0
*/
- public static String toListString(Collection self) {
- return toListString(self, -1);
+ public static <T> List<T> union(List<T> left, Iterable<T> right) {
+ return (List<T>) union((Collection<T>) left, asCollection(right));
}
/**
- * Returns the string representation of the given list. The string
- * displays the contents of the list, similar to a list literal, i.e.
- * <code>[1, 2, a]</code>.
+ * Create a List composed of the union of a List and an Iterable. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6])
+ * </pre>
*
- * @param self a Collection
- * @param maxSize stop after approximately this many characters and append '...'
- * @return the string representation
- * @since 1.7.3
+ * @param left a List
+ * @param right an Iterable
+ * @param comparator a Comparator
+ * @return a List as a union of a List and an Iterable
+ * @since 5.0.0
*/
- public static String toListString(Collection self, int maxSize) {
- return (self == null) ? "null" : FormatHelper.toListString(self, maxSize);
+ public static <T> List<T> union(List<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+ return (List<T>) union((Collection<T>) left, asCollection(right), comparator);
}
/**
- * Create a String representation of this object.
- * @param value an object
- * @return a string.
- * @since 1.0
+ * Create a Set composed of the union of a Set and an Iterable. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6,7,8] as Set == ([1,2,3,4,5] as Set).union([4,5,6,7,8])
+ * </pre>
+ * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+ * element already exists in the resultant collection.
+ *
+ * @param left a Set
+ * @param right an Iterable
+ * @return a Set as a union of a Set and an Iterable
+ * @see #union(Set, Iterable, Comparator)
+ * @since 5.0.0
*/
- public static String toString(Object value) {
- return FormatHelper.toString(value);
+ public static <T> Set<T> union(Set<T> left, Iterable<T> right) {
+ return (Set<T>) union((Collection<T>) left, asCollection(right));
}
- //-------------------------------------------------------------------------
- // Number based methods
-
/**
- * Decrement a Character by one.
+ * Create a Set composed of the union of a Set and an Iterable. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6] as Set == ([1,2,3,4] as Set).union([3,4,5,6], Comparator.naturalOrder())
+ * </pre>
*
- * @param self a Character
- * @return a decremented Character
- * @since 1.5.7
+ * @param left a Set
+ * @param right an Iterable
+ * @param comparator a Comparator
+ * @return a Set as a union of a Set and an Iterable
+ * @since 5.0.0
*/
- public static Character previous(Character self) {
- return (char) (self - 1);
+ public static <T> Set<T> union(Set<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+ return (Set<T>) union((Collection<T>) left, asCollection(right), comparator);
}
/**
- * Decrement a Number by one.
+ * Create a SortedSet composed of the union of a SortedSet and an Iterable. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6,7,8] as SortedSet == ([1,2,3,4,5] as SortedSet).union([4,5,6,7,8])
+ * </pre>
+ * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+ * element already exists in the resultant collection.
*
- * @param self a Number
- * @return a decremented Number
- * @since 1.0
+ * @param left a SortedSet
+ * @param right an Iterable
+ * @return a Set as a union of a SortedSet and an Iterable
+ * @see #intersect(SortedSet, Iterable, Comparator)
+ * @since 5.0.0
*/
- public static Number previous(Number self) {
- return NumberNumberMinus.minus(self, Integer.valueOf(1));
+ public static <T> SortedSet<T> union(SortedSet<T> left, Iterable<T> right) {
+ return (SortedSet<T>) union((Collection<T>) left, asCollection(right));
}
/**
- * Add a Character and a Number. The ordinal value of the Character
- * is used in the addition (the ordinal value is the unicode
- * value which for simple character sets is the ASCII value).
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Create a SortedSet composed of the intersection of a SortedSet and an Iterable. Any
+ * elements that exist in either iterables are added to the resultant collection, such
+ * that no elements are duplicated in the resultant collection.
+ * <pre class="groovyTestCase">
+ * assert [1,2,3,4,5,6,7,8] as SortedSet == ([1,2,3,4,5] as SortedSet).union([4,5,6,7,8])
+ * </pre>
*
- * @see java.lang.Integer#valueOf(int)
- * @param left a Character
- * @param right a Number
- * @return the Number corresponding to the addition of left and right
- * @since 1.0
+ * @param left a SortedSet
+ * @param right an Iterable
+ * @param comparator a Comparator
+ * @return a Set as a union of a SortedSet and an Iterable
+ * @since 2.5.0
*/
- public static Number plus(Character left, Number right) {
- return NumberNumberPlus.plus(Integer.valueOf(left), right);
+ public static <T> SortedSet<T> union(SortedSet<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+ return (SortedSet<T>) union((Collection<T>) left, asCollection(right), comparator);
}
/**
- * Add a Number and a Character. The ordinal value of the Character
- * is used in the addition (the ordinal value is the unicode
- * value which for simple character sets is the ASCII value).
+ * Implementation of the right shift operator for integral types. Non-integral
+ * Number types throw UnsupportedOperationException.
*
- * @see java.lang.Integer#valueOf(int)
- * @param left a Number
- * @param right a Character
- * @return The Number corresponding to the addition of left and right
- * @since 1.0
+ * @param self a Number object
+ * @param operand the shift distance by which to right shift the number
+ * @return the resulting number
+ * @since 1.5.0
*/
- public static Number plus(Number left, Character right) {
- return NumberNumberPlus.plus(left, Integer.valueOf(right));
+ public static Number rightShift(Number self, Number operand) {
+ return NumberMath.rightShift(self, operand);
}
/**
- * Add one Character to another. The ordinal values of the Characters
- * are used in the addition (the ordinal value is the unicode
- * value which for simple character sets is the ASCII value).
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Implementation of the right shift (unsigned) operator for integral types. Non-integral
+ * Number types throw UnsupportedOperationException.
*
- * @see #plus(java.lang.Number, java.lang.Character)
- * @param left a Character
- * @param right a Character
- * @return the Number corresponding to the addition of left and right
- * @since 1.0
+ * @param self a Number object
+ * @param operand the shift distance by which to right shift (unsigned) the number
+ * @return the resulting number
+ * @since 1.5.0
*/
- public static Number plus(Character left, Character right) {
- return plus(Integer.valueOf(left), right);
+ public static Number rightShiftUnsigned(Number self, Number operand) {
+ return NumberMath.rightShiftUnsigned(self, operand);
}
/**
- * Appends a String to the literal of the Map instance.
- *
+ * Convert a Collection to a Set. Always returns a new Set
+ * even if the Collection is already a Set.
+ * <p>
+ * Example usage:
* <pre class="groovyTestCase">
- * assert '[a:1] is a map' == [a:1] + ' is a map'
+ * def result = [1, 2, 2, 2, 3].toSet()
+ * assert result instanceof Set
+ * assert result == [1, 2, 3] as Set
* </pre>
*
- * @param left a Map
- * @param right a String
- * @return the concatenated string
- * @since 4.0.3
+ * @param self a collection
+ * @return a Set
+ * @since 1.8.0
*/
- public static String plus(Map left, String right) {
- return DefaultGroovyMethods.toString(left) + right;
+ public static <T> Set<T> toSet(Collection<T> self) {
+ Set<T> answer = new HashSet<>(self.size());
+ answer.addAll(self);
+ return answer;
}
/**
- * Appends a GString to the literal of the Map instance.
- *
+ * Convert an Iterable to a Set. Always returns a new Set
+ * even if the Iterable is already a Set.
+ * <p>
+ * Example usage:
* <pre class="groovyTestCase">
- * assert '[a:1] is a map' == [a:1] + " is ${'a'} map"
+ * def result = [1, 2, 2, 2, 3].toSet()
+ * assert result instanceof Set
+ * assert result == [1, 2, 3] as Set
* </pre>
*
- * @param left a Map
- * @param right a GString
- * @return the concatenated string
- * @since 4.0.3
- */
- public static String plus(Map left, GString right) {
- return DefaultGroovyMethods.toString(left) + right;
- }
-
- /**
- * Power of a Number to a certain exponent. Called by the '**' operator.
- *
- * @param self a Number
- * @param exponent a Number exponent
- * @return a Number to the power of a certain exponent
- * @since 1.0
+ * @param self an Iterable
+ * @return a Set
+ * @since 2.4.0
*/
- public static Number power(Number self, Number exponent) {
- double base, exp, answer;
- base = self.doubleValue();
- exp = exponent.doubleValue();
-
- answer = Math.pow(base, exp);
- if ((double) ((int) answer) == answer) {
- return (int) answer;
- } else if ((double) ((long) answer) == answer) {
- return (long) answer;
- } else {
- return answer;
- }
+ public static <T> Set<T> toSet(Iterable<T> self) {
+ return toSet(self.iterator());
}
/**
- * Power of a BigDecimal to an integer certain exponent. If the
- * exponent is positive, call the BigDecimal.pow(int) method to
- * maintain precision. Called by the '**' operator.
+ * Convert an iterator to a Set. The iterator will become
+ * exhausted of elements after making this conversion.
*
- * @param self a BigDecimal
- * @param exponent an Integer exponent
- * @return a Number to the power of the exponent
+ * @param self an iterator
+ * @return a Set
+ * @since 1.8.0
*/
- public static Number power(BigDecimal self, Integer exponent) {
- if (exponent >= 0) {
- return self.pow(exponent);
- } else {
- return power(self, (double) exponent);
+ public static <T> Set<T> toSet(Iterator<T> self) {
+ Set<T> answer = new HashSet<>();
+ while (self.hasNext()) {
+ answer.add(self.next());
}
+ return answer;
}
/**
- * Power of a BigInteger to an integer certain exponent. If the
- * exponent is positive, call the BigInteger.pow(int) method to
- * maintain precision. Called by the '**' operator.
+ * Convert an enumeration to a Set.
*
- * @param self a BigInteger
- * @param exponent an Integer exponent
- * @return a Number to the power of the exponent
+ * @param self an enumeration
+ * @return a Set
+ * @since 1.8.0
*/
- public static Number power(BigInteger self, Integer exponent) {
- if (exponent >= 0) {
- return self.pow(exponent);
- } else {
- return power(self, (double) exponent);
+ public static <T> Set<T> toSet(Enumeration<T> self) {
+ Set<T> answer = new HashSet<>();
+ while (self.hasMoreElements()) {
+ answer.add(self.nextElement());
}
+ return answer;
}
/**
- * Power of an integer to an integer certain exponent. If the
- * exponent is positive, convert to a BigInteger and call
- * BigInteger.pow(int) method to maintain precision. Called by the
- * '**' operator.
+ * Identity conversion which returns Boolean.TRUE for a true Boolean and Boolean.FALSE for a false Boolean.
*
- * @param self an Integer
- * @param exponent an Integer exponent
- * @return a Number to the power of the exponent
+ * @param self a Boolean
+ * @return the original Boolean
+ * @since 1.7.6
*/
- public static Number power(Integer self, Integer exponent) {
- if (exponent >= 0) {
- BigInteger answer = BigInteger.valueOf(self).pow(exponent);
- if (answer.compareTo(BI_INT_MIN) >= 0 && answer.compareTo(BI_INT_MAX) <= 0) {
- return answer.intValue();
- } else {
- return answer;
- }
- } else {
- return power(self, (double) exponent);
- }
+ public static Boolean toBoolean(Boolean self) {
+ return self;
}
/**
- * Power of a long to an integer certain exponent. If the
- * exponent is positive, convert to a BigInteger and call
- * BigInteger.pow(int) method to maintain precision. Called by the
- * '**' operator.
+ * Returns the string representation of the given map.
*
- * @param self a Long
- * @param exponent an Integer exponent
- * @return a Number to the power of the exponent
+ * @param self a Map
+ * @return the string representation
+ * @see #toMapString(java.util.Map)
+ * @since 1.0
*/
- public static Number power(Long self, Integer exponent) {
- if (exponent >= 0) {
- BigInteger answer = BigInteger.valueOf(self).pow(exponent);
- if (answer.compareTo(BI_LONG_MIN) >= 0 && answer.compareTo(BI_LONG_MAX) <= 0) {
- return answer.longValue();
- } else {
- return answer;
- }
- } else {
- return power(self, (double) exponent);
- }
+ public static String toString(AbstractMap self) {
+ return toMapString(self);
}
/**
- * Power of a BigInteger to a BigInteger certain exponent. Called by the '**' operator.
+ * Returns the string representation of this map. The string displays the
+ * contents of the map, i.e. <code>[one:1, two:2, three:3]</code>.
*
- * @param self a BigInteger
- * @param exponent a BigInteger exponent
- * @return a BigInteger to the power of the exponent
- * @since 2.3.8
+ * @param self a Map
+ * @return the string representation
+ * @since 1.0
*/
- public static BigInteger power(BigInteger self, BigInteger exponent) {
- if ((exponent.signum() >= 0) && (exponent.compareTo(BI_INT_MAX) <= 0)) {
- return self.pow(exponent.intValue());
- } else {
- return BigDecimal.valueOf(Math.pow(self.doubleValue(), exponent.doubleValue())).toBigInteger();
- }
+ public static String toMapString(Map self) {
+ return toMapString(self, -1);
}
/**
- * Bitwise OR together two numbers.
+ * Returns the string representation of this map. The string displays the
+ * contents of the map, i.e. <code>[one:1, two:2, three:3]</code>.
*
- * @param left a Number
- * @param right another Number to bitwise OR
- * @return the bitwise OR of both Numbers
+ * @param self a Map
+ * @param maxSize stop after approximately this many characters and append '...'
+ * @return the string representation
* @since 1.0
*/
- public static Number or(Number left, Number right) {
- return NumberMath.or(left, right);
+ public static String toMapString(Map self, int maxSize) {
+ return (self == null) ? "null" : FormatHelper.toMapString(self, maxSize);
}
/**
- * Bitwise XOR together two BitSets. Called when the '^' operator is used
- * between two bit sets.
+ * Returns the string representation of the given collection. The string
+ * displays the contents of the collection, i.e.
+ * <code>[1, 2, a]</code>.
*
- * @param left a BitSet
- * @param right another BitSet to bitwise XOR
- * @return the bitwise XOR of both BitSets
- * @since 1.5.0
+ * @param self a Collection
+ * @return the string representation
+ * @see #toListString(java.util.Collection)
+ * @since 1.0
*/
- public static BitSet xor(BitSet left, BitSet right) {
- BitSet result = (BitSet) left.clone();
- result.xor(right);
- return result;
+ public static String toString(AbstractCollection self) {
+ return toListString(self);
}
/**
- * Bitwise OR together two BitSets. Called when the '|' operator is used
- * between two bit sets.
+ * Returns the string representation of the given list. The string
+ * displays the contents of the list, similar to a list literal, i.e.
+ * <code>[1, 2, a]</code>.
*
- * @param left a BitSet
- * @param right another BitSet to bitwise OR
- * @return the bitwise OR of both BitSets
- * @since 1.5.0
+ * @param self a Collection
+ * @return the string representation
+ * @since 1.0
*/
- public static BitSet or(BitSet left, BitSet right) {
- BitSet result = (BitSet) left.clone();
- result.or(right);
- return result;
+ public static String toListString(Collection self) {
+ return toListString(self, -1);
}
/**
- * Bitwise XOR together two Numbers. Called when the '^' operator is used.
+ * Returns the string representation of the given list. The string
+ * displays the contents of the list, similar to a list literal, i.e.
+ * <code>[1, 2, a]</code>.
*
- * @param left a Number
- * @param right another Number to bitwise XOR
- * @return the bitwise XOR of both Numbers
+ * @param self a Collection
+ * @param maxSize stop after approximately this many characters and append '...'
+ * @return the string representation
+ * @since 1.7.3
+ */
+ public static String toListString(Collection self, int maxSize) {
+ return (self == null) ? "null" : FormatHelper.toListString(self, maxSize);
+ }
+
+ /**
+ * Create a String representation of this object.
+ * @param value an object
+ * @return a string.
* @since 1.0
*/
- public static Number xor(Number left, Number right) {
- return NumberMath.xor(left, right);
+ public static String toString(Object value) {
+ return FormatHelper.toString(value);
}
+ //-------------------------------------------------------------------------
+ // Number based methods
+
/**
* Performs a division modulus operation. Called by the '%' operator.
*
@@ -15106,33 +15155,6 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
return NumberMath.toBigInteger(self);
}
- //--------------------------------------------------------------------------
- // Boolean based methods
-
- /**
- * Logical disjunction of two boolean operators
- *
- * @param left left operator
- * @param right right operator
- * @return result of logical disjunction
- * @since 1.0
- */
- public static Boolean or(Boolean left, Boolean right) {
- return left || Boolean.TRUE.equals(right);
- }
-
- /**
- * Exclusive disjunction of two boolean operators
- *
- * @param left left operator
- * @param right right operator
- * @return result of exclusive disjunction
- * @since 1.0
- */
- public static Boolean xor(Boolean left, Boolean right) {
- return left ^ Boolean.TRUE.equals(right);
- }
-
//--------------------------------------------------------------------------
/**
@@ -15339,50 +15361,46 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
return self.remove(o);
}
+ //--------------------------------------------------------------------------
+ // xor
+
/**
- * Create a Set as a union of a Set and an Iterable. Any
- * elements that exist in either are added to the resultant Set.
- * <p>
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
- *
- * <pre class="groovyTestCase">
- * def a = [1,2,3,4] as Set
- * def b = [3,4,5,6] as Set
- * assert (a | b) == [1,2,3,4,5,6] as Set
- * </pre>
+ * Bitwise XOR together two Numbers. Called when the '^' operator is used.
*
- * @param left the left Set
- * @param right the right Iterable
- * @return the merged Set
- * @since 5.0.0
- * @see #plus(Set, Iterable)
+ * @param left a Number
+ * @param right another Number to bitwise XOR
+ * @return the bitwise XOR of both Numbers
+ * @since 1.0
*/
- public static <T> Set<T> or(Set<T> left, Iterable<T> right) {
- return plus(left, right);
+ public static Number xor(Number left, Number right) {
+ return NumberMath.xor(left, right);
}
/**
- * Create a SortedSet as a union of a SortedSet and an Iterable. Any
- * elements that exist in either are added to the resultant SortedSet.
- * <p>
- * This operation will always create a new object for the result,
- * while the operands remain unchanged.
+ * Bitwise XOR together two BitSets. Called when the '^' operator is used
+ * between two bit sets.
*
- * <pre class="groovyTestCase">
- * def a = [1,2,3,4] as SortedSet
- * def b = [3,4,5,6] as Set
- * assert (a | b) == [1,2,3,4,5,6] as SortedSet
- * </pre>
+ * @param left a BitSet
+ * @param right another BitSet to bitwise XOR
+ * @return the bitwise XOR of both BitSets
+ * @since 1.5.0
+ */
+ public static BitSet xor(BitSet left, BitSet right) {
+ BitSet result = (BitSet) left.clone();
+ result.xor(right);
+ return result;
+ }
+
+ /**
+ * Exclusive disjunction of two boolean operators
*
- * @param left the left SortedSet
- * @param right the right Iterable
- * @return the merged SortedSet
- * @since 5.0.0
- * @see #plus(SortedSet, Iterable)
+ * @param left left operator
+ * @param right right operator
+ * @return result of exclusive disjunction
+ * @since 1.0
*/
- public static <T> SortedSet<T> or(SortedSet<T> left, Iterable<T> right) {
- return plus(left, right);
+ public static Boolean xor(Boolean left, Boolean right) {
+ return left ^ Boolean.TRUE.equals(right);
}
/**
@@ -16456,6 +16474,26 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
return answer;
}
+ /**
+ * Implements the putAt(int) method for primitive type arrays.
+ *
+ * @param self an object
+ * @param idx the index of interest
+ * @param newValue the new value to be put into the index of interest
+ * @return the added value
+ * @since 1.5.0
+ */
+ @Deprecated(since = "5.0.0")
+ protected static Object primitiveArrayPut(Object self, int idx, Object newValue) {
+ Array.set(self, normaliseIndex(idx, Array.getLength(self)), newValue);
+ return newValue;
+ }
+
+ @Deprecated(since = "2.4.0")
+ public static void putAt(List self, List splice, List values) {
+ putAt(self, splice, (Collection) values);
+ }
+
@Deprecated(since = "5.0.0")
public static <T> T[] reverse(T[] self) {
return ArrayGroovyMethods.reverse(self);