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 18:59:52 UTC
[groovy] 03/03: sort `f` and `g` 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 70c17c26afbed767dc4af08b49ad0f2e185ef792
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Aug 2 13:33:22 2023 -0500
sort `f` and `g` methods
---
.../groovy/runtime/DefaultGroovyMethods.java | 14998 ++++++++++---------
1 file changed, 7528 insertions(+), 7470 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 3ef8d4f78d..dfd3220778 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -28,7 +28,6 @@ import groovy.lang.GString;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.GroovySystem;
-import groovy.lang.Groovydoc;
import groovy.lang.IntRange;
import groovy.lang.ListWithDefault;
import groovy.lang.MapWithDefault;
@@ -4522,1905 +4521,1727 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
//--------------------------------------------------------------------------
+ // find
/**
- * Identity check. Since == is overridden in Groovy with the meaning of equality
- * we need some fallback to check for object identity. Invoke using the
- * 'is' method, like so: <code>def same = this.is(that)</code>
+ * Finds the first value matching the closure condition.
*
- * @param self an object
- * @param other an object to compare identity with
- * @return true if self and other are both references to the same
- * instance, false otherwise
- * @since 1.0
- */
- public static boolean is(Object self, Object other) {
- return self == other;
- }
-
- /**
- * Allows the closure to be called for the object reference self.
- * Synonym for 'with()'.
+ * <pre class="groovyTestCase">
+ * def numbers = [1, 2, 3]
+ * def result = numbers.find { it {@code >} 1}
+ * assert result == 2
+ * </pre>
*
- * @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, Closure)
+ * @param self an Object with an iterator returning its values
+ * @param closure a closure condition
+ * @return the first Object found or null if none was found
* @since 1.0
*/
- public static <T,U> T identity(
- @DelegatesTo.Target("self") U self,
- @DelegatesTo(value=DelegatesTo.Target.class,
- target="self",
- strategy=Closure.DELEGATE_FIRST)
- @ClosureParams(FirstParam.class)
- Closure<T> closure) {
- return DefaultGroovyMethods.with(self, closure);
+ public static Object find(Object self, Closure closure) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+ Object value = iter.next();
+ if (bcw.call(value)) {
+ return value;
+ }
+ }
+ return null;
}
/**
- * Allows the closure to be called for the object reference self.
+ * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth).
* <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:
+ * Example:
* <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" }
+ * def items = [null, 0, 0.0, false, '', [], 42, 43]
+ * assert items.find() == 42
* </pre>
*
- * @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 self an Object with an Iterator returning its values
+ * @return the first Object found or null if none was found
+ * @since 1.8.1
+ * @see Closure#IDENTITY
*/
- @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 Object find(Object self) {
+ return find(self, Closure.IDENTITY);
}
/**
- * 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.
- *
- * 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" }
+ * Finds the first value matching the closure condition. Example:
+ * <pre class="groovyTestCase">def list = [1,2,3]
+ * assert 2 == list.find { it {@code >} 1 }
* </pre>
- * Alternatively, 'with' is an alias for 'with(false)', so the boolean parameter can be omitted instead.
*
- * @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 self a Collection
+ * @param closure a closure condition
+ * @return the first Object found, in the order of the collections iterator, or null if no element matches
+ * @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 <T> T find(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ for (T value : self) {
+ if (bcw.call(value)) {
+ return value;
+ }
+ }
+ return null;
}
/**
- * Allows the closure to be called for the object reference self (similar
- * to <code>with</code>) and always returns self.
+ * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth).
* <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'
- * }
+ * Example:
+ * <pre class="groovyTestCase">
+ * def items = [null, 0, 0.0, false, '', [], 42, 43]
+ * assert items.find() == 42
* </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 self a Collection
+ * @return the first Object found or null if none was found
+ * @since 1.8.1
+ * @see Closure#IDENTITY
*/
- @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> T find(Collection<T> self) {
+ return find(self, Closure.IDENTITY);
}
/**
- * Allows the subscript operator to be used to lookup dynamic property values.
- * <code>bean[somePropertyNameExpression]</code>. The normal property notation
- * of groovy is neater and more concise but only works with compile-time known
- * property names.
+ * Finds the first entry matching the closure condition.
+ * If the closure takes two parameters, the entry key and value are passed.
+ * If the closure takes one parameter, the Map.Entry object is passed.
+ * <pre class="groovyTestCase">assert [a:1, b:3].find { it.value == 3 }.key == "b"</pre>
*
- * @param self the object to act upon
- * @param property the property name of interest
- * @return the property value
+ * @param self a Map
+ * @param closure a 1 or 2 arg Closure condition
+ * @return the first Object found
* @since 1.0
*/
- public static Object getAt(Object self, String property) {
- return InvokerHelper.getProperty(self, property);
+ public static <K, V> Map.Entry<K, V> find(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<?> closure) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ if (bcw.callForMap(entry)) {
+ return entry;
+ }
+ }
+ return null;
}
+ //--------------------------------------------------------------------------
+ // findAll
+
/**
- * 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.
+ * Finds all entries matching the closure condition. If the
+ * closure takes one parameter then it will be passed the Map.Entry.
+ * Otherwise if the closure should take two parameters, which will be
+ * the key and the value.
+ * <p>
+ * If the <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.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">
+ * def result = [a:1, b:2, c:4, d:5].findAll { it.value % 2 == 0 }
+ * assert result.every { it instanceof Map.Entry }
+ * assert result*.key == ["b", "c"]
+ * assert result*.value == [2, 4]
+ * </pre>
*
- * @param self the object to act upon
- * @param property the name of the property to set
- * @param newValue the value to set
+ * @param self a Map
+ * @param closure a 1 or 2 arg Closure condition applying on the entries
+ * @return a new subMap
* @since 1.0
*/
- public static void putAt(Object self, String property, Object newValue) {
- InvokerHelper.setProperty(self, property, newValue);
- }
-
+ public static <K, V> Map<K, V> findAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure closure) {
+ Map<K, V> answer = createSimilarMap(self);
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ if (bcw.callForMap(entry)) {
+ answer.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return answer;
+ }
+
/**
- * Retrieves the list of {@link groovy.lang.MetaProperty} objects for 'self' and wraps it
- * in a list of {@link groovy.lang.PropertyValue} objects that additionally provide
- * the value for each property of 'self'.
+ * Finds all values matching the closure condition.
+ * <pre class="groovyTestCase">assert ([2,4] as Set) == ([1,2,3,4] as Set).findAll { it % 2 == 0 }</pre>
*
- * @param self the receiver object
- * @return list of {@link groovy.lang.PropertyValue} objects
- * @see groovy.util.Expando#getMetaPropertyValues()
- * @since 1.0
+ * @param self a Set
+ * @param closure a closure condition
+ * @return a Set of matching values
+ * @since 2.4.0
*/
- public static List<PropertyValue> getMetaPropertyValues(Object self) {
- MetaClass metaClass = InvokerHelper.getMetaClass(self);
- List<MetaProperty> mps = metaClass.getProperties();
- List<PropertyValue> props = new ArrayList<>(mps.size());
- for (MetaProperty mp : mps) {
- props.add(new PropertyValue(self, mp));
- }
- return props;
+ public static <T> Set<T> findAll(Set<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return (Set<T>) findAll((Collection<T>) self, closure);
}
/**
- * Convenience method that calls {@link #getMetaPropertyValues(java.lang.Object)}(self)
- * and provides the data in form of simple key/value pairs, i.e. without
- * type() information.
+ * Finds all values matching the closure condition.
+ * <pre class="groovyTestCase">assert [2,4] == [1,2,3,4].findAll { it % 2 == 0 }</pre>
*
- * @param self the receiver object
- * @return meta properties as Map of key/value pairs
- * @since 1.0
+ * @param self a List
+ * @param closure a closure condition
+ * @return a List of matching values
+ * @since 2.4.0
*/
- public static Map<String, Object> getProperties(Object self) {
- List<PropertyValue> metaProps = getMetaPropertyValues(self);
- Map<String, Object> props = new LinkedHashMap<>(metaProps.size());
-
- for (PropertyValue mp : metaProps) {
- try {
- props.put(mp.getName(), mp.getValue());
- } catch (Exception e) {
- java.util.logging.Logger.getLogger(DefaultGroovyMethods.class.getName())
- .throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e);
- }
- }
- return props;
+ public static <T> List<T> findAll(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return (List<T>) findAll((Collection<T>) self, closure);
}
/**
- * Scoped use method
+ * Finds all values matching the closure condition.
+ * <pre class="groovyTestCase">assert [2,4] == [1,2,3,4].findAll { it % 2 == 0 }</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 a Collection
+ * @param closure a closure condition
+ * @return a Collection of matching values
+ * @since 1.5.6
*/
- public static <T> T use(Object self, Class categoryClass, Closure<T> closure) {
- return GroovyCategorySupport.use(categoryClass, closure);
+ public static <T> Collection<T> findAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return findMany(createSimilarCollection(self), self.iterator(), closure);
}
/**
- * Extend object with category methods.
- * All methods for given class and all super classes will be added to the object.
+ * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth).
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null] as Set
+ * assert items.findAll() == [1, 2, true, 'foo', [4, 5]] as Set
+ * </pre>
*
- * @param self any Class
- * @param categoryClasses a category classes to use
- * @since 1.6.0
+ * @param self a Set
+ * @return a Set of the truthy values
+ * @since 2.4.0
+ * @see Closure#IDENTITY
*/
- public static void mixin(MetaClass self, List<Class> categoryClasses) {
- MixinInMetaClass.mixinClassesToMetaClass(self, categoryClasses);
+ public static <T> Set<T> findAll(Set<T> self) {
+ return findAll(self, Closure.IDENTITY);
}
/**
- * Extend class globally with category methods.
- * All methods for given class and all super classes will be added to the class.
+ * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth).
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
+ * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
+ * </pre>
*
- * @param self any Class
- * @param categoryClasses a category classes to use
- * @since 1.6.0
+ * @param self a List
+ * @return a List of the truthy values
+ * @since 2.4.0
+ * @see Closure#IDENTITY
*/
- public static void mixin(Class self, List<Class> categoryClasses) {
- mixin(getMetaClass(self), categoryClasses);
+ public static <T> List<T> findAll(List<T> self) {
+ return findAll(self, Closure.IDENTITY);
}
/**
- * Extend class globally with category methods.
+ * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth).
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
+ * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
+ * </pre>
*
- * @param self any Class
- * @param categoryClass a category class to use
- * @since 1.6.0
+ * @param self a Collection
+ * @return a Collection of the truthy values
+ * @since 1.8.1
+ * @see Closure#IDENTITY
*/
- public static void mixin(Class self, Class categoryClass) {
- mixin(getMetaClass(self), Collections.singletonList(categoryClass));
+ public static <T> Collection<T> findAll(Collection<T> self) {
+ return findAll(self, Closure.IDENTITY);
}
/**
- * Extend class globally with category methods.
+ * Finds all items matching the closure condition.
*
- * @param self any Class
- * @param categoryClass a category class to use
+ * @param self an Object with an Iterator returning its values
+ * @param closure a closure condition
+ * @return a List of the values found
* @since 1.6.0
*/
- public static void mixin(Class self, Class[] categoryClass) {
- mixin(getMetaClass(self), Arrays.asList(categoryClass));
+ @SuppressWarnings("unchecked")
+ public static List findAll(Object self, Closure closure) {
+ return findMany(new ArrayList(), InvokerHelper.asIterator(self), closure);
}
/**
- * Extend class globally with category methods.
+ * Finds all items matching the IDENTITY Closure (i.e. matching Groovy truth).
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
+ * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
+ * </pre>
*
- * @param self any Class
- * @param categoryClass a category class to use
- * @since 1.6.0
+ * @param self an Object with an Iterator returning its values
+ * @return a List of the truthy values
+ * @since 1.8.1
+ * @see Closure#IDENTITY
*/
- public static void mixin(MetaClass self, Class categoryClass) {
- mixin(self, Collections.singletonList(categoryClass));
+ public static List findAll(Object self) {
+ return findAll(self, Closure.IDENTITY);
+ }
+
+ static <T, C extends Collection<T>> C findMany(C collector, Iterator<? extends T> iter, Closure<?> closure) {
+ BooleanClosureWrapper test = new BooleanClosureWrapper(closure);
+ while (iter.hasNext()) {
+ T value = iter.next();
+ if (test.call(value)) {
+ collector.add(value);
+ }
+ }
+ return collector;
}
+ //--------------------------------------------------------------------------
+ // findIndexOf
+
/**
- * Extend class globally with category methods.
+ * Iterates over the elements of an aggregate of items and returns
+ * the index of the first item that matches the condition specified in the closure.
*
- * @param self any Class
- * @param categoryClass a category class to use
- * @since 1.6.0
+ * @param self the iteration object over which to iterate
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 1.0
*/
- public static void mixin(MetaClass self, Class[] categoryClass) {
- mixin(self, Arrays.asList(categoryClass));
+ public static int findIndexOf(Object self, Closure condition) {
+ return findIndexOf(self, 0, condition);
}
/**
- * Gets the url of the jar file/source file containing the specified class
+ * Iterates over the elements of an aggregate of items, starting from a
+ * specified startIndex, and returns the index of the first item that matches the
+ * condition specified in the closure.
+ * Example (aggregate is {@code ChronoUnit} enum values):
+ * <pre class="groovyTestCase">
+ * import java.time.temporal.ChronoUnit
+ * def nameStartsWithM = { it.name().startsWith('M') }
+ * def first = ChronoUnit.findIndexOf(nameStartsWithM)
+ * def second = ChronoUnit.findIndexOf(first + 1, nameStartsWithM)
+ * def third = ChronoUnit.findIndexOf(second + 1, nameStartsWithM)
+ * Set units = [first, second, third]
+ * assert !units.contains(-1) // should have found 3 of MICROS, MILLIS, MINUTES, MONTHS, ...
+ * assert units.size() == 3 // just check size so as not to rely on order
+ * </pre>
*
- * @param self the class
- * @return the url of the jar, {@code null} if the specified class is from JDK
- * @since 2.5.0
+ * @param self the iteration object over which to iterate
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 1.5.0
*/
- public static URL getLocation(Class self) {
- CodeSource codeSource = self.getProtectionDomain().getCodeSource();
-
- return null == codeSource ? null : codeSource.getLocation();
+ public static int findIndexOf(Object self, int startIndex, Closure condition) {
+ return findIndexOf(InvokerHelper.asIterator(self), startIndex, condition);
}
/**
- * Scoped use method with list of categories.
+ * Iterates over the elements of an Iterator and returns the index of the first item that satisfies the
+ * condition specified by the closure.
*
- * @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 an Iterator
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
*/
- public static <T> T use(Object self, List<Class> categoryClassList, Closure<T> closure) {
- return GroovyCategorySupport.use(categoryClassList, closure);
+ public static <T> int findIndexOf(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexOf(self, 0, condition);
}
/**
- * 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.
+ * Iterates over the elements of an Iterator, starting from a
+ * specified startIndex, and returns the index of the first item that satisfies the
+ * condition specified by the closure.
*
- * @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 self an Iterator
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.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);
+ public static <T> int findIndexOf(Iterator<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ int result = -1;
+ int i = 0;
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ while (self.hasNext()) {
+ Object value = self.next();
+ if (i++ < startIndex) {
+ continue;
+ }
+ if (bcw.call(value)) {
+ result = i - 1;
+ break;
}
- list.add(categoryClass);
- }
- return GroovyCategorySupport.use(list, closure);
- }
-
- private static Object getClosureOwner(Closure<?> cls) {
- Object owner = cls.getOwner();
- while (owner instanceof GeneratedClosure) {
- owner = ((Closure<?>) owner).getOwner();
}
- return owner;
+ return result;
}
/**
- * Inspects returns the String that matches what would be typed into a
- * terminal to create this object.
+ * Iterates over the elements of an Iterable and returns the index of the first item that satisfies the
+ * condition specified by the closure.
*
- * @param self any Object
- * @return a String that matches what would be typed into a terminal to
- * create this object. e.g. [1, 'hello'].inspect() {@code ->} [1, 'hello']
- * @since 1.0
+ * @param self an Iterable
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
*/
- public static String inspect(Object self) {
- return FormatHelper.inspect(self);
+ public static <T> int findIndexOf(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexOf(self, 0, condition);
}
/**
- * Provide a dynamic method invocation method which can be overloaded in
- * classes to implement dynamic proxies easily.
+ * Iterates over the elements of an Iterable, starting from a
+ * specified startIndex, and returns the index of the first item that satisfies the
+ * condition specified by the closure.
*
- * @param object any Object
- * @param method the name of the method to call
- * @param arguments the arguments to use
- * @return the result of the method call
- * @since 1.0
+ * @param self an Iterable
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
*/
- public static Object invokeMethod(Object object, String method, Object arguments) {
- return InvokerHelper.invokeMethod(object, method, arguments);
+ public static <T> int findIndexOf(Iterable<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexOf(self.iterator(), startIndex, condition);
}
//--------------------------------------------------------------------------
- // print/println/printf/sprintf
+ // findIndexValues
/**
- * Print a value formatted Groovy style to self if it
- * is a Writer, otherwise to the standard output stream.
+ * Iterates over the elements of an aggregate of items and returns
+ * the index values of the items that match the condition specified in the closure.
*
- * @param self any Object
- * @param value the value to print
- * @since 1.0
+ * @param self the iteration object over which to iterate
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 1.5.2
*/
- 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 List<Number> findIndexValues(Object self, Closure condition) {
+ return findIndexValues(self, 0, condition);
}
/**
- * Print a value formatted Groovy style to the print writer.
+ * Iterates over the elements of an aggregate of items, starting from
+ * a specified startIndex, and returns the index values of the items that match
+ * the condition specified in the closure.
*
- * @param self a PrintWriter
- * @param value the value to print
- * @since 1.0
+ * @param self the iteration object over which to iterate
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 1.5.2
*/
- public static void print(PrintWriter self, Object value) {
- self.print(FormatHelper.toString(value));
+ public static List<Number> findIndexValues(Object self, Number startIndex, Closure condition) {
+ return findIndexValues(InvokerHelper.asIterator(self), startIndex, condition);
}
/**
- * Print a value formatted Groovy style to the print stream.
+ * Iterates over the elements of an Iterator and returns
+ * the index values of the items that match the condition specified in the closure.
*
- * @param self a PrintStream
- * @param value the value to print
- * @since 1.6.0
+ * @param self an Iterator
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
*/
- public static void print(PrintStream self, Object value) {
- self.print(FormatHelper.toString(value));
+ public static <T> List<Number> findIndexValues(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexValues(self, 0, condition);
}
/**
- * Print a value to the standard output stream.
- * This method delegates to the owner to execute the method.
+ * Iterates over the elements of an Iterator, starting from
+ * a specified startIndex, and returns the index values of the items that match
+ * the condition specified in the closure.
*
- * @param self a generated closure
- * @param value the value to print
- * @since 1.0
+ * @param self an Iterator
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
*/
- public static void print(Closure self, Object value) {
- Object owner = getClosureOwner(self);
- InvokerHelper.invokeMethod(owner, "print", new Object[]{value});
+ public static <T> List<Number> findIndexValues(Iterator<T> self, Number startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ List<Number> result = new ArrayList<>();
+ long count = 0;
+ long startCount = startIndex.longValue();
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ while (self.hasNext()) {
+ Object value = self.next();
+ if (count++ < startCount) {
+ continue;
+ }
+ if (bcw.call(value)) {
+ result.add(count - 1);
+ }
+ }
+ return result;
}
/**
- * Print to a console in interactive format.
+ * Iterates over the elements of an Iterable and returns
+ * the index values of the items that match the condition specified in the closure.
*
- * @param self any Object
- * @param out the PrintWriter used for printing
- * @since 1.0
+ * @param self an Iterable
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
*/
- public static void print(Object self, PrintWriter out) {
- if (out == null) {
- out = new PrintWriter(System.out);
- }
- out.print(FormatHelper.toString(self));
+ public static <T> List<Number> findIndexValues(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexValues(self, 0, condition);
}
/**
- * Print a linebreak to the standard output stream.
+ * Iterates over the elements of an Iterable, starting from
+ * a specified startIndex, and returns the index values of the items that match
+ * the condition specified in the closure.
*
- * @param self any Object
- * @since 1.0
+ * @param self an Iterable
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
*/
- 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> List<Number> findIndexValues(Iterable<T> self, Number startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexValues(self.iterator(), startIndex, condition);
}
+ //--------------------------------------------------------------------------
+ // findLastIndexOf
+
/**
- * Print a linebreak to the standard output stream.
- * This method delegates to the owner to execute the method.
+ * Iterates over the elements of an aggregate of items and returns
+ * the index of the last item that matches the condition specified in the closure.
+ * Example (aggregate is {@code ChronoUnit} enum values):
+ * <pre class="groovyTestCase">
+ * import java.time.temporal.ChronoUnit
+ * def nameStartsWithM = { it.name().startsWith('M') }
+ * def first = ChronoUnit.findIndexOf(nameStartsWithM)
+ * def last = ChronoUnit.findLastIndexOf(nameStartsWithM)
+ * // should have found 2 unique index values for MICROS, MILLIS, MINUTES, MONTHS, ...
+ * assert first != -1 && last != -1 && first != last
+ * </pre>
*
- * @param self a closure
- * @since 1.0
+ * @param self the iteration object over which to iterate
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 1.5.2
*/
- public static void println(Closure self) {
- Object owner = getClosureOwner(self);
- InvokerHelper.invokeMethod(owner, "println", InvokerHelper.EMPTY_ARGS);
+ public static int findLastIndexOf(Object self, Closure condition) {
+ return findLastIndexOf(self, 0, condition);
}
/**
- * Print a value formatted Groovy style (followed by a newline) to self
- * if it is a Writer, otherwise to the standard output stream.
+ * Iterates over the elements of an aggregate of items, starting
+ * from a specified startIndex, and returns the index of the last item that
+ * matches the condition specified in the closure.
*
- * @param self any Object
- * @param value the value to print
- * @since 1.0
+ * @param self the iteration object over which to iterate
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 1.5.2
*/
- 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 int findLastIndexOf(Object self, int startIndex, Closure condition) {
+ return findLastIndexOf(InvokerHelper.asIterator(self), startIndex, condition);
}
/**
- * Print a value formatted Groovy style (followed by a newline) to the print writer.
+ * Iterates over the elements of an Iterator and returns
+ * the index of the last item that matches the condition specified in the closure.
*
- * @param self a PrintWriter
- * @param value the value to print
- * @since 1.0
+ * @param self an Iterator
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
*/
- public static void println(PrintWriter self, Object value) {
- self.println(FormatHelper.toString(value));
+ public static <T> int findLastIndexOf(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findLastIndexOf(self, 0, condition);
}
/**
- * Print a value formatted Groovy style (followed by a newline) to the print stream.
+ * Iterates over the elements of an Iterator, starting
+ * from a specified startIndex, and returns the index of the last item that
+ * matches the condition specified in the closure.
*
- * @param self any Object
- * @param value the value to print
- * @since 1.6.0
+ * @param self an Iterator
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
*/
- public static void println(PrintStream self, Object value) {
- self.println(FormatHelper.toString(value));
+ public static <T> int findLastIndexOf(Iterator<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ int result = -1;
+ int i = 0;
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ while (self.hasNext()) {
+ Object value = self.next();
+ if (i++ < startIndex) {
+ continue;
+ }
+ if (bcw.call(value)) {
+ result = i - 1;
+ }
+ }
+ return result;
}
/**
- * Print a value (followed by a newline) to the standard output stream.
- * This method delegates to the owner to execute the method.
+ * Iterates over the elements of an Iterable and returns
+ * the index of the last item that matches the condition specified in the closure.
*
- * @param self a closure
- * @param value the value to print
- * @since 1.0
+ * @param self an Iterable
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
*/
- public static void println(Closure self, Object value) {
- Object owner = getClosureOwner(self);
- InvokerHelper.invokeMethod(owner, "println", new Object[]{value});
+ public static <T> int findLastIndexOf(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findLastIndexOf(self.iterator(), 0, condition);
}
/**
- * Print to a console in interactive format.
+ * Iterates over the elements of an Iterable, starting
+ * from a specified startIndex, and returns the index of the last item that
+ * matches the condition specified in the closure.
*
- * @param self any Object
- * @param out the PrintWriter used for printing
- * @since 1.0
+ * @param self an Iterable
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.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> int findLastIndexOf(Iterable<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findLastIndexOf(self.iterator(), startIndex, condition);
}
+ //--------------------------------------------------------------------------
+ // findResult
+
/**
- * Printf to the standard output stream.
+ * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns null.
+ * <p>
+ * <pre class="groovyTestCase">
+ * int[] numbers = [1, 2, 3]
+ * assert numbers.findResult { if(it {@code >} 1) return it } == 2
+ * assert numbers.findResult { if(it {@code >} 4) return it } == null
+ * </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 self an Object with an iterator returning its values
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result of the closure
+ * @since 1.7.5
*/
- 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 Object findResult(Object self, Closure condition) {
+ for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
+ Object value = iter.next();
+ Object result = condition.call(value);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
}
/**
- * Printf 0 or more values to the standard output stream using a format string.
- * This method delegates to the owner to execute the method.
+ * Treats the object as iterable, iterating through the values it represents and returns the first non-null value, otherwise returns null.
+ * <p>
+ * <pre class="groovyTestCase">
+ * class Foo {
+ * List items
+ * Iterator iterator() {
+ * items.iterator()
+ * }
+ * }
+ * assert new Foo(items: [null, 2, 4]).findResult() == 2
+ * assert new Foo(items: [null, null]).findResult() == null
+ * </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 self an Object with an iterator returning its values
+ * @return the first non-null result of the closure
+ * @since 4.0.9
*/
- 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 Object findResult(Object self) {
+ return findResult(self, Closure.IDENTITY);
}
/**
- * Printf a value to the standard output stream using a format string.
- * This method delegates to the owner to execute the method.
+ * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns the defaultResult.
+ * <p>
+ * <pre class="groovyTestCase">
+ * int[] numbers = [1, 2, 3]
+ * assert numbers.findResult(5) { if(it {@code >} 1) return it } == 2
+ * assert numbers.findResult(5) { if(it {@code >} 4) return it } == 5
+ * </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 self an Object with an iterator returning its values
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result of the closure, otherwise the default value
+ * @since 1.7.5
*/
- 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 Object findResult(Object self, Object defaultResult, Closure condition) {
+ Object result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
}
/**
- * Prints a formatted string using the specified format string and arguments.
+ * Treats the object as iterable, iterating through the values it represents and returns the first non-null result, otherwise returns the defaultResult.
* <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 class="groovyTestCase">
+ * class Foo {
+ * List items
+ * Iterator iterator() {
+ * items.iterator()
+ * }
+ * }
+ * assert new Foo(items: [null, 2, 4]).findResult(5) == 2
+ * assert new Foo(items: [null, null]).findResult(5) == 5
* </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 self an Object with an iterator returning its values
+ * @param defaultResult an Object that should be returned if all elements are null
+ * @return the first non-null element, otherwise the default value
+ * @since 4.0.9
*/
- 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);
- }
+ public static Object findResult(Object self, Object defaultResult) {
+ Object result = findResult(self, Closure.IDENTITY);
+ if (result == null) return defaultResult;
+ return result;
}
/**
- * Sprintf to a string.
+ * Iterates through the Iterator calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
+ * <p>
+ * Examples:
+ * <pre class="groovyTestCase">
+ * def iter = [1,2,3].iterator()
+ * assert "Found 2" == iter.findResult("default") { it {@code >} 1 ? "Found $it" : null }
+ * assert "default" == iter.findResult("default") { it {@code >} 3 ? "Found $it" : null }
+ * </pre>
*
- * @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
+ * @param self an Iterator
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or the defaultValue
+ * @since 2.5.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 <S, T, U extends T, V extends T> T findResult(Iterator<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
+ T result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
}
/**
- * Returns a formatted string using the specified format string and
- * arguments.
+ * Iterates through the Iterator stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
+ * <p>
+ * Examples:
+ * <pre class="groovyTestCase">
+ * assert [null, 1, 2].iterator().findResult('default') == 1
+ * assert [null, null].findResult('default') == 'default'
+ * </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[].
- * @return the resulting printf'd string
- * @since 1.5.0
+ * @param self an Iterator
+ * @param defaultResult an Object that should be returned if all elements are null
+ * @return the first non-null result from the iterator, or the defaultValue
+ * @since 4.0.9
*/
- 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);
- }
+ public static <T, U extends T, V extends T> T findResult(Iterator<U> self, V defaultResult) {
+ T result = (T) findResult(self, Closure.IDENTITY);
+ return result == null ? defaultResult : result;
+ }
- 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;
+ /**
+ * Iterates through the Iterator calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
+ *
+ * @param self an Iterator
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or null
+ * @since 2.5.0
+ */
+ public static <T, U> T findResult(Iterator<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
+ while (self.hasNext()) {
+ U next = self.next();
+ T result = condition.call(next);
+ if (result != null) {
+ return result;
}
- 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);
+ return null;
}
- //-------------------------------------------------------------------------
- // isCase/isNotCase
-
/**
- * Method for overloading the behavior of the 'case' method in switch statements.
- * The default implementation handles arrays types but otherwise simply delegates
- * to Object#equals, but this may be overridden for other types. In this example:
- * <pre> switch( a ) {
- * case b: //some code
- * }</pre>
- * "some code" is called when <code>b.isCase( a )</code> returns
- * <code>true</code>.
+ * Iterates through the Iterator stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
*
- * @param caseValue the case value
- * @param switchValue the switch value
- * @return true if the switchValue is deemed to be equal to the caseValue
- * @since 1.0
+ * @param self an Iterator
+ * @return the first non-null result from the iterator, or null
+ * @since 4.0.9
*/
- public static boolean isCase(Object caseValue, Object switchValue) {
- if (caseValue.getClass().isArray()) {
- return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);
- }
- return caseValue.equals(switchValue);
+ public static <T> T findResult(Iterator<T> self) {
+ return (T) findResult(self, Closure.IDENTITY);
}
/**
- * Special 'Case' implementation for Class, which allows testing
- * whether some switch value is assignable from the given case class.
- *
- * If the switch value is an object, {@code isCase} will return true if the
- * switch value is assignment compatible with the class (case value),
- * i.e. is an {@code instanceof} the class, for example:
+ * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
+ * <p>
+ * Examples:
* <pre class="groovyTestCase">
- * def someList = []
- * switch (someList) {
- * case List:
- * assert true : 'is a list'
- * break
- * case Map:
- * assert false : 'is not a Map'
- * break
- * default:
- * assert false : 'should never get here'
- * break
- * }
+ * def list = [1,2,3]
+ * assert "Found 2" == list.findResult("default") { it {@code >} 1 ? "Found $it" : null }
+ * assert "default" == list.findResult("default") { it {@code >} 3 ? "Found $it" : null }
* </pre>
*
- * If the switch value is a class, {@code isCase} will return true if the
- * switch value is assignable from the given class (case value), i.e. the case class
- * is the same as, or a superclass, or a super-interface of the switch class, for example:
+ * @param self an Iterable
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or the defaultValue
+ * @since 2.5.0
+ */
+ public static <S, T, U extends T, V extends T> T findResult(Iterable<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
+ T result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
+ }
+
+ /**
+ * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
+ * <p>
+ * Examples:
* <pre class="groovyTestCase">
- * switch (ArrayList) {
- * case List:
- * assert true : 'is a list'
- * break
- * case Map:
- * assert false : 'is not a Map'
- * break
- * default:
- * assert false : 'should never get here'
- * break
- * }
+ * assert [null, 1, 2].findResult('default') == 1
+ * assert [null, null].findResult('default') == 'default'
* </pre>
*
- * @param caseValue the case value
- * @param switchValue the switch value
- * @return true if the switchValue is deemed to be assignable from the given class
- * @since 1.0
+ * @param self an Iterable
+ * @param defaultResult an Object that should be returned if all elements in the iterable are null
+ * @return the first non-null element from the iterable, or the defaultValue
+ * @since 4.0.9
*/
- @SuppressWarnings("unchecked")
- public static boolean isCase(Class caseValue, Object switchValue) {
- if (switchValue instanceof Class) {
- Class val = (Class) switchValue;
- return caseValue.isAssignableFrom(val);
- }
- return caseValue.isInstance(switchValue);
+ public static <T, U extends T, V extends T> T findResult(Iterable<U> self, V defaultResult) {
+ T result = (T) findResult(self, Closure.IDENTITY);
+ if (result == null) return defaultResult;
+ return result;
}
/**
- * 'Case' implementation for collections which tests if the 'switch'
- * operand is contained in any of the 'case' values.
- * For example:
- * <pre class="groovyTestCase">switch( 3 ) {
- * case [1,3,5]:
- * assert true
- * break
- * default:
- * assert false
- * }</pre>
+ * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
*
- * @param caseValue the case value
- * @param switchValue the switch value
- * @return true if the caseValue is deemed to contain the switchValue
- * @see java.util.Collection#contains(java.lang.Object)
- * @since 1.0
+ * @param self an Iterable
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or null
+ * @since 2.5.0
*/
- public static boolean isCase(Collection caseValue, Object switchValue) {
- return caseValue.contains(switchValue);
+ public static <T, U> T findResult(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
+ return findResult(self.iterator(), condition);
}
/**
- * 'Case' implementation for iterable types which tests if the 'switch'
- * operand is contained in any of the 'case' values.
- * For example:
- * <pre class="groovyTestCase">Iterable it = {[1,3,5].iterator()}
- * switch( 3 ) {
- * case it:
- * assert true
- * break
- * default:
- * assert false
- * }
- *
- * //GROOVY-7919
- * assert 1 in it
- * assert 2 !in it
- * </pre>
+ * Iterates through the Iterable stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
*
- * @param caseValue the case value
- * @param switchValue the switch value
- * @return true if the caseValue is deemed to contain the switchValue
- * @see #contains(Iterable,Object)
- * @since 5.0.0
+ * @param self an Iterable
+ * @return the first non-null element from the iterable, or null
+ * @since 4.0.9
*/
- public static boolean isCase(Iterable caseValue, Object switchValue) {
- return contains(caseValue, switchValue);
+ public static <T> T findResult(Iterable<T> self) {
+ return (T) findResult(self.iterator(), Closure.IDENTITY);
}
/**
- * 'Case' implementation for maps which tests the groovy truth
- * value obtained using the 'switch' operand as key.
- * For example:
- * <pre class="groovyTestCase">switch( 'foo' ) {
- * case [foo:true, bar:false]:
- * assert true
- * break
- * default:
- * assert false
- * }</pre>
+ * Returns the first non-null closure result found by passing each map entry to the closure, otherwise null is returned.
+ * If the closure takes two parameters, the entry key and value are passed.
+ * If the closure takes one parameter, the Map.Entry object is passed.
+ * <pre class="groovyTestCase">
+ * assert "Found b:3" == [a:1, b:3].findResult { if (it.value == 3) return "Found ${it.key}:${it.value}" }
+ * assert null == [a:1, b:3].findResult { if (it.value == 9) return "Found ${it.key}:${it.value}" }
+ * assert "Found a:1" == [a:1, b:3].findResult { k, v {@code ->} if (k.size() + v == 2) return "Found $k:$v" }
+ * </pre>
*
- * @param caseValue the case value
- * @param switchValue the switch value
- * @return the groovy truth value from caseValue corresponding to the switchValue key
- * @since 1.7.6
+ * @param self a Map
+ * @param condition a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
+ * @return the first non-null result collected by calling the closure, or null if no such result was found
+ * @since 1.7.5
*/
- public static boolean isCase(Map caseValue, Object switchValue) {
- return DefaultTypeTransformation.castToBoolean(caseValue.get(switchValue));
+ public static <T, K, V> T findResult(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> condition) {
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ T result = callClosureForMapEntry(condition, entry);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
}
/**
- * Special 'case' implementation for all numbers, which delegates to the
- * <code>compareTo()</code> method for comparing numbers of different
- * types.
+ * Returns the first non-null closure result found by passing each map entry to the closure, otherwise the defaultResult is returned.
+ * If the closure takes two parameters, the entry key and value are passed.
+ * If the closure takes one parameter, the Map.Entry object is passed.
+ * <pre class="groovyTestCase">
+ * assert "Found b:3" == [a:1, b:3].findResult("default") { if (it.value == 3) return "Found ${it.key}:${it.value}" }
+ * assert "default" == [a:1, b:3].findResult("default") { if (it.value == 9) return "Found ${it.key}:${it.value}" }
+ * assert "Found a:1" == [a:1, b:3].findResult("default") { k, v {@code ->} if (k.size() + v == 2) return "Found $k:$v" }
+ * </pre>
*
- * @param caseValue the case value
- * @param switchValue the switch value
- * @return true if the numbers are deemed equal
- * @since 1.5.0
+ * @param self a Map
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
+ * @return the first non-null result collected by calling the closure, or the defaultResult if no such result was found
+ * @since 1.7.5
*/
- public static boolean isCase(Number caseValue, Number switchValue) {
- return NumberMath.compareTo(caseValue, switchValue) == 0;
+ public static <T, U extends T, V extends T, A, B> T findResult(Map<A, B> self, U defaultResult, @ClosureParams(MapEntryOrKeyValue.class) Closure<V> condition) {
+ T result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
}
- /**
- * @since 4.0.0
- */
- public static boolean isNotCase(Number caseValue, Number switchValue) {
- return !isCase(caseValue, switchValue);
- }
+ //--------------------------------------------------------------------------
+ // findResults
/**
- * @since 4.0.0
+ * Iterates through the Iterable transforming items using the supplied closure
+ * and collecting any non-null results.
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * def list = [1,2,3]
+ * def result = list.findResults { it {@code >} 1 ? "Found $it" : null }
+ * assert result == ["Found 2", "Found 3"]
+ * </pre>
+ *
+ * @param self an Iterable
+ * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
+ * @return the list of non-null transformed values
+ * @since 2.2.0
*/
- public static boolean isNotCase(Object caseValue, Object switchValue) {
- return !isCase(caseValue, switchValue);
+ public static <T, U> Collection<T> findResults(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
+ return findResults(self.iterator(), filteringTransform);
}
/**
- * @since 4.0.0
+ * Iterates through the Iterable collecting any non-null results.
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * assert [1, null, 2, null, 3].findResults() == [1, 2, 3]
+ * </pre>
+ *
+ * @param self an Iterable
+ * @return the list of non-null values
+ * @since 4.0.9
*/
- public static boolean isNotCase(Class<?> caseValue, Object switchValue) {
- return !isCase(caseValue, switchValue);
+ public static <T> Collection<T> findResults(Iterable<T> self) {
+ return findResults(self.iterator(), Closure.IDENTITY);
}
/**
- * @since 4.0.0
+ * Iterates through the Iterator transforming items using the supplied closure
+ * and collecting any non-null results.
+ *
+ * @param self an Iterator
+ * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
+ * @return the list of non-null transformed values
+ * @since 2.5.0
*/
- public static boolean isNotCase(Closure<?> caseValue, Object switchValue) {
- return !caseValue.isCase(switchValue);
+ public static <T, U> Collection<T> findResults(Iterator<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
+ List<T> result = new ArrayList<>();
+ while (self.hasNext()) {
+ U value = self.next();
+ T transformed = filteringTransform.call(value);
+ if (transformed != null) {
+ result.add(transformed);
+ }
+ }
+ return result;
}
/**
- * @since 4.0.0
+ * Iterates through the Iterator collecting any non-null results.
+ *
+ * @param self an Iterator
+ * @return the list of non-null values
+ * @since 4.0.9
*/
- public static boolean isNotCase(Collection<?> caseValue, Object switchValue) {
- return !isCase(caseValue, switchValue);
+ public static <T> Collection<T> findResults(Iterator<T> self) {
+ return findResults(self, Closure.IDENTITY);
}
/**
- * @since 4.0.0
+ * Iterates through the map transforming items using the supplied closure
+ * and collecting any non-null results.
+ * If the closure takes two parameters, the entry key and value are passed.
+ * If the closure takes one parameter, the Map.Entry object is passed.
+ * <p>
+ * Example:
+ * <pre class="groovyTestCase">
+ * def map = [a:1, b:2, hi:2, cat:3, dog:2]
+ * def result = map.findResults { k, v {@code ->} k.size() == v ? "Found $k:$v" : null }
+ * assert result == ["Found a:1", "Found hi:2", "Found cat:3"]
+ * </pre>
+ *
+ * @param self a Map
+ * @param filteringTransform a 1 or 2 arg Closure that should return either a non-null transformed value or null for items which should be discarded
+ * @return the list of non-null transformed values
+ * @since 1.8.1
*/
- public static boolean isNotCase(Map<?, ?> caseValue, Object switchValue) {
- return !isCase(caseValue, switchValue);
+ public static <T, K, V> Collection<T> findResults(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> filteringTransform) {
+ List<T> result = new ArrayList<>();
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ T transformed = callClosureForMapEntry(filteringTransform, entry);
+ if (transformed != null) {
+ result.add(transformed);
+ }
+ }
+ return result;
}
//--------------------------------------------------------------------------
+ // first
/**
- * 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.
+ * Returns the first item from the List.
+ * <pre class="groovyTestCase">
+ * def list = [3, 4, 2]
+ * assert list.first() == 3
+ * // check original is unaltered
+ * assert list == [3, 4, 2]
+ * </pre>
*
- * @param self an Iterator
- * @return a new Iterator of the unique items from the original iterator
+ * @param self a List
+ * @return the first item from the List
+ * @throws NoSuchElementException if you try to access first() for an empty List
* @since 1.5.5
*/
- public static <T> Iterator<T> unique(Iterator<T> self) {
- return uniqueItems(new IteratorIterableAdapter<>(self)).listIterator();
+ public static <T> T first(List<T> self) {
+ if (self.isEmpty()) {
+ throw new NoSuchElementException("Cannot access first() element from an empty List");
+ }
+ return self.get(0);
}
/**
- * 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>
+ * Returns the first item from the Iterable.
+ * <pre class="groovyTestCase">
+ * def set = [3, 4, 2] as LinkedHashSet
+ * assert set.first() == 3
+ * // check original is unaltered
+ * assert set == [3, 4, 2] as Set
+ * </pre>
+ * The first element returned by the Iterable's iterator is returned.
+ * If the Iterable doesn't guarantee a defined order it may appear like
+ * a random element is returned.
*
- * @param self a collection
- * @return the now modified collection
- * @see #unique(Collection, boolean)
- * @since 1.0
+ * @param self an Iterable
+ * @return the first item from the Iterable
+ * @throws NoSuchElementException if you try to access first() for an empty Iterable
+ * @since 1.8.7
*/
- public static <T> Collection<T> unique(Collection<T> self) {
- return unique(self, true);
+ public static <T> T first(Iterable<T> self) {
+ Iterator<T> iterator = self.iterator();
+ if (!iterator.hasNext()) {
+ throw new NoSuchElementException("Cannot access first() element from an empty Iterable");
+ }
+ return iterator.next();
}
+ //--------------------------------------------------------------------------
+ // flatten
+
/**
- * 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>
+ * Flatten a Collection. This Collection and any nested arrays or
+ * collections have their contents (recursively) added to the new collection.
+ * <pre class="groovyTestCase">assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()</pre>
*
- * @param self a List
- * @return the now modified List
- * @see #unique(Collection, boolean)
- * @since 2.4.0
+ * @param self a Collection to flatten
+ * @return a flattened Collection
+ * @since 1.6.0
*/
- public static <T> List<T> unique(List<T> self) {
- return (List<T>) unique((Collection<T>) self, true);
+ public static Collection<?> flatten(Collection<?> self) {
+ return flatten(self, createSimilarCollection(self));
}
/**
- * 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>
- * <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>
+ * Flatten an Iterable. This Iterable and any nested arrays or
+ * collections have their contents (recursively) added to the new collection.
+ * <pre class="groovyTestCase">assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()</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 self an Iterable to flatten
+ * @return a flattened Collection
+ * @since 1.6.0
*/
- 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 Collection<?> flatten(Iterable<?> self) {
+ return flatten(self, createSimilarCollection(self));
}
- 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;
+ /**
+ * Flatten a List. This List and any nested arrays or
+ * collections have their contents (recursively) added to the new List.
+ * <pre class="groovyTestCase">assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()</pre>
+ *
+ * @param self a List to flatten
+ * @return a flattened List
+ * @since 2.4.0
+ */
+ public static List<?> flatten(List<?> self) {
+ return (List<?>) flatten((Collection<?>) self);
}
/**
- * 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>
+ * Flatten a Set. This Set and any nested arrays or
+ * collections have their contents (recursively) added to the new Set.
+ * <pre class="groovyTestCase">assert [1,2,3,4,5] as Set == ([1,[2,3],[[4]],[],5] as Set).flatten()</pre>
+ *
+ * @param self a Set to flatten
+ * @return a flattened Set
+ * @since 2.4.0
+ */
+ public static Set<?> flatten(Set<?> self) {
+ return (Set<?>) flatten((Collection<?>) self);
+ }
+
+ /**
+ * Flatten a SortedSet. This SortedSet and any nested arrays or
+ * collections have their contents (recursively) added to the new SortedSet.
* <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]
+ * Set nested = [[0,1],[2],3,[4],5]
+ * SortedSet sorted = new TreeSet({ a, b {@code ->} (a instanceof List ? a[0] : a) {@code <=>} (b instanceof List ? b[0] : b) } as Comparator)
+ * sorted.addAll(nested)
+ * assert [0,1,2,3,4,5] as SortedSet == sorted.flatten()
* </pre>
*
- * @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
+ * @param self a SortedSet to flatten
+ * @return a flattened SortedSet
* @since 2.4.0
*/
- public static <T> List<T> unique(List<T> self, boolean mutate) {
- return (List<T>) unique((Collection<T>) self, mutate);
+ public static SortedSet<?> flatten(SortedSet<?> self) {
+ return (SortedSet<?>) flatten((Collection<?>) self);
+ }
+
+ private static Collection flatten(Iterable elements, Collection addTo) {
+ for (Object element : elements) {
+ if (element instanceof Collection) {
+ flatten((Collection) element, addTo);
+ } else if (element != null && element.getClass().isArray()) {
+ flatten(DefaultTypeTransformation.arrayAsCollection(element), addTo);
+ } else {
+ // found a leaf
+ addTo.add(element);
+ }
+ }
+ return addTo;
}
/**
- * Provides a method that compares two comparables using Groovy's
- * default number aware comparator.
+ * Flatten an Iterable. This Iterable and any nested arrays or
+ * collections have their contents (recursively) added to the new collection.
+ * For any non-Array, non-Collection object which represents some sort
+ * of collective type, the supplied closure should yield the contained items;
+ * otherwise, the closure should just return any element which corresponds to a leaf.
*
- * @param self a Comparable
- * @param other another Comparable
- * @return a -ve number, 0 or a +ve number according to Groovy's compareTo contract
+ * @param self an Iterable
+ * @param flattenUsing a closure to determine how to flatten non-Array, non-Collection elements
+ * @return a flattened Collection
* @since 1.6.0
*/
- public static int numberAwareCompareTo(Comparable self, Comparable other) {
- return COMPARABLE_NUMBER_AWARE_COMPARATOR.compare(self, other);
+ public static <T> Collection<T> flatten(Iterable<T> self, Closure<? extends T> flattenUsing) {
+ return flatten(self, createSimilarCollection(self), flattenUsing);
+ }
+
+ private static <T> Collection<T> flatten(Iterable elements, Collection<T> addTo, Closure<? extends T> flattenUsing) {
+ for (Object element : elements) {
+ if (element instanceof Collection) {
+ flatten((Collection) element, addTo, flattenUsing);
+ } else if (element != null && element.getClass().isArray()) {
+ flatten(DefaultTypeTransformation.arrayAsCollection(element), addTo, flattenUsing);
+ } else {
+ T flattened = flattenUsing.call(new Object[]{element});
+ boolean returnedSelf = flattened == element;
+ if (!returnedSelf && flattened instanceof Collection) {
+ List<?> list = toList((Iterable<?>) flattened);
+ if (list.size() == 1 && list.get(0) == element) {
+ returnedSelf = true;
+ }
+ }
+ if (flattened instanceof Collection && !returnedSelf) {
+ flatten((Collection) flattened, addTo, flattenUsing);
+ } else {
+ addTo.add(flattened);
+ }
+ }
+ }
+ return addTo;
}
+ //--------------------------------------------------------------------------
+ // get
+
/**
- * 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).
+ * Looks up an item in a Map for the given key and returns the corresponding value.
+ * If there is no entry for the given key return instead the default value and
+ * also add the key and default value to the map.
+ * <pre class="groovyTestCase">
+ * def map=[:]
+ * map.get("a", []) << 5
+ * assert map == [a:[5]]
+ * </pre>
+ * For a method which doesn't mutate the map, consider instead using {@link Map#getOrDefault(Object, Object)}
+ * or consider using Groovy's {@link MapWithDefault} often instantiated using {@link #withDefault(Map, Closure)}
+ * or with more options {@link #withDefault(Map, boolean, boolean, Closure)}.
*
- * @param self an Iterator
- * @param condition a Closure used to determine unique items
- * @return the modified Iterator
- * @since 1.5.5
+ * @param map a Map
+ * @param key the key to look up the value
+ * @param defaultValue the value to return and add to the map for this key if
+ * there is no entry for the given key
+ * @return the value of the given key or the default value, added to the map if the
+ * key did not exist
+ * @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 <K, V> V get(Map<K, V> map, K key, V defaultValue) {
+ if (!map.containsKey(key)) {
+ map.put(key, defaultValue);
+ }
+ return map.get(key);
}
+ //--------------------------------------------------------------------------
+ // getAt
+
/**
- * 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>
+ * Allows the subscript operator to be used to lookup dynamic property values.
+ * <code>bean[somePropertyNameExpression]</code>. The normal property notation
+ * of groovy is neater and more concise but only works with compile-time known
+ * property names.
*
- * @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)
+ * @param self the object to act upon
+ * @param property the property name of interest
+ * @return the property value
* @since 1.0
*/
- 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 Object getAt(Object self, String property) {
+ return InvokerHelper.getProperty(self, property);
}
/**
- * 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>
+ * Support the range subscript operator for a List.
+ * <pre class="groovyTestCase">def list = [1, "a", 4.5, true]
+ * assert list[1..2] == ["a", 4.5]</pre>
*
- * @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 List
+ * @param range a Range indicating the items to get
+ * @return a new list instance based on range borders
+ *
+ * @since 1.0
*/
- 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 <T> List<T> getAt(List<T> self, Range range) {
+ RangeInfo info = subListBorders(self.size(), range);
+
+ List<T> subList = self.subList(info.from, info.to);
+ if (info.reverse) {
+ subList = reverse(subList);
+ }
+
+ // trying to guess the concrete list type and create a new instance from it
+ List<T> answer = createSimilarList(self, subList.size());
+ answer.addAll(subList);
+
+ return answer;
}
/**
- * 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>
+ * Select a List of items from an eager or lazy List using a Collection to
+ * identify the indices to be selected.
+ * <pre class="groovyTestCase">def list = [].withDefault { 42 }
+ * assert list[1,0,2] == [42, 42, 42]</pre>
*
- * @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 a ListWithDefault
+ * @param indices a Collection of indices
+ *
+ * @return a new eager or lazy list of the values at the given indices
*/
- 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));
+ @SuppressWarnings("unchecked")
+ public static <T> List<T> getAt(ListWithDefault<T> self, Collection indices) {
+ List<T> answer = ListWithDefault.newInstance(new ArrayList<>(indices.size()), self.isLazyDefaultValues(), self.getInitClosure());
+ for (Object value : indices) {
+ if (value instanceof Collection) {
+ answer.addAll((List<T>) InvokerHelper.invokeMethod(self, "getAt", value));
+ } else {
+ int idx = normaliseIndex(DefaultTypeTransformation.intUnbox(value), self.size());
+ answer.add(self.getAt(idx));
+ }
}
- return self;
+ return answer;
}
/**
- * 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>
+ * Support the range subscript operator for an eager or lazy List.
+ * <pre class="groovyTestCase">def list = [].withDefault { 42 }
+ * assert list[1..2] == [null, 42]</pre>
*
- * @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 <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);
- }
-
- /**
- * 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 self a ListWithDefault
+ * @param range a Range indicating the items to get
*
- * @param self an Iterator
- * @param comparator a Comparator
- * @return the modified Iterator
- * @since 1.5.5
+ * @return a new eager or lazy list instance based on range borders
*/
- 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;
+ public static <T> List<T> getAt(ListWithDefault<T> self, Range range) {
+ RangeInfo info = subListBorders(self.size(), range);
- private IteratorIterableAdapter(Iterator<T> self) {
- this.self = self;
+ // if a positive index is accessed not initialized so far
+ // initialization up to that index takes place
+ if (self.size() < info.to) {
+ self.get(info.to - 1);
}
- @Override
- public Iterator<T> iterator() {
- return self;
+ List<T> answer = self.subList(info.from, info.to);
+ if (info.reverse) {
+ answer = ListWithDefault.newInstance(reverse(answer), self.isLazyDefaultValues(), self.getInitClosure());
+ } else {
+ // instead of using the SubList backed by the parent list, a new ArrayList instance is used
+ answer = ListWithDefault.newInstance(new ArrayList<>(answer), self.isLazyDefaultValues(), self.getInitClosure());
}
+
+ return answer;
}
/**
- * 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>
+ * Support the range subscript operator for an eager or lazy List.
* <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)
- * }
- * }
+ * def list = [true, 1, 3.4].withDefault{ 42 }
+ * {@code assert list[0..<0] == []}
+ * </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")
+ * @param self a ListWithDefault
+ * @param range a Range indicating the items to get
*
- * def list = [a, b, c, d]
- * List list2 = list.unique(new PersonComparator())
- * assert( list2 == list {@code &&} list == [a, b, c] )
- * </pre>
+ * @return a new list instance based on range borders
*
- * @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
*/
- public static <T> Collection<T> unique(Collection<T> self, Comparator<? super T> comparator) {
- return unique(self, true, comparator) ;
+ public static <T> List<T> getAt(ListWithDefault<T> self, EmptyRange range) {
+ return ListWithDefault.newInstance(new ArrayList<>(), self.isLazyDefaultValues(), self.getInitClosure());
}
/**
- * 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>
+ * Support the range subscript operator for a List.
* <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)
- * }
- * }
+ * def list = [true, 1, 3.4]
+ * {@code assert list[0..<0] == []}
+ * </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")
+ * @param self a List
+ * @param range a Range indicating the items to get
+ * @return a new list instance based on range borders
*
- * def list = [a, b, c, d]
- * List list2 = list.unique(new PersonComparator())
- * assert( list2 == list {@code &&} list == [a, b, c] )
- * </pre>
+ * @since 1.0
+ */
+ public static <T> List<T> getAt(List<T> self, EmptyRange range) {
+ return createSimilarList(self, 0);
+ }
+
+ /**
+ * Select a List of items from a List using a Collection to
+ * identify the indices to be selected.
+ * <pre class="groovyTestCase">def list = [true, 1, 3.4, false]
+ * assert list[1,0,2] == [1, true, 3.4]</pre>
*
- * @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 List
+ * @param indices a Collection of indices
+ * @return a new list of the values at the given indices
+ * @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);
+ @SuppressWarnings("unchecked")
+ public static <T> List<T> getAt(List<T> self, Collection indices) {
+ List<T> answer = new ArrayList<>(indices.size());
+ for (Object value : indices) {
+ if (value instanceof Collection) {
+ answer.addAll(getAt(self, (Collection) value));
+ } else {
+ int idx = DefaultTypeTransformation.intUnbox(value);
+ answer.add(getAt(self, idx));
+ }
+ }
+ return answer;
}
/**
- * 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
- * }
- * }
+ * Support the subscript operator for a List.
+ * <pre class="groovyTestCase">def list = [2, "a", 5.3]
+ * assert list[1] == "a"</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)
- * }
- *
- * 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
+ * @param self a List
+ * @param idx an index
+ * @return the value at the given index
+ * @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;
- }
- }
- if (!duplicated)
- answer.add(t);
+ public static <T> T getAt(List<T> self, int idx) {
+ int size = self.size();
+ int i = normaliseIndex(idx, size);
+ if (i < size) {
+ return self.get(i);
+ } else {
+ return null;
}
- 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")
- *
- * 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 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
+ * Support subscript operator for list access.
*/
- 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 <T> T getAt(List<T> self, Number idx) {
+ return getAt(self, idx.intValue());
}
/**
- * 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).
+ * Support the subscript operator for an Iterator. The iterator
+ * will be partially exhausted up until the idx entry after returning
+ * if a +ve or 0 idx is used, or fully exhausted if a -ve idx is used
+ * or no corresponding entry was found. Typical usage:
* <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']
+ * def iter = [2, "a", 5.3].iterator()
+ * assert iter[1] == "a"
+ * </pre>
+ * A more elaborate example:
+ * <pre class="groovyTestCase">
+ * def items = [2, "a", 5.3]
+ * def iter = items.iterator()
+ * assert iter[-1] == 5.3
+ * // iter exhausted, so reset
+ * iter = items.iterator()
+ * assert iter[1] == "a"
+ * // iter partially exhausted so now idx starts after "a"
+ * assert iter[0] == 5.3
* </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 an Iterator
- * @param condition a Closure used to determine unique items
- * @return an Iterator with no duplicate items
- * @since 2.4.0
+ * @param idx an index value (-self.size() <= idx < self.size())
+ * @return the value at the given index (after normalisation) or null if no corresponding value was found
+ * @since 1.7.2
*/
- 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));
- }
-
- 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();
+ public static <T> T getAt(Iterator<T> self, int idx) {
+ if (idx < 0) {
+ // calculate whole list in this case
+ // recommend avoiding -ve's as this is not as efficient
+ List<T> list = toList(self);
+ int adjustedIndex = idx + list.size();
+ if (adjustedIndex < 0 || adjustedIndex >= list.size()) return null;
+ return list.get(adjustedIndex);
}
- private void advance() {
- boolean foundNext = false;
- while (!foundNext && !exhausted) {
- exhausted = !delegate.hasNext();
- if (!exhausted) {
- next = delegate.next();
- foundNext = seen.add(next);
- }
+ int count = 0;
+ while (self.hasNext()) {
+ if (count == idx) {
+ return self.next();
+ } else {
+ count++;
+ self.next();
}
}
+
+ return null;
}
/**
- * Returns an iterator equivalent to this iterator with all duplicated
- * items removed by using the supplied comparator.
+ * Support the subscript operator for an Iterable. Typical usage:
+ * <pre class="groovyTestCase">
+ * // custom Iterable example:
+ * class MyIterable implements Iterable {
+ * Iterator iterator() { [1, 2, 3].iterator() }
+ * }
+ * def myIterable = new MyIterable()
+ * assert myIterable[1] == 2
*
- * @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
+ * // Set example:
+ * def set = [1,2,3] as LinkedHashSet
+ * assert set[1] == 2
+ * </pre>
+ *
+ * @param self an Iterable
+ * @param idx an index value (-self.size() <= idx < self.size()) but using -ve index values will be inefficient
+ * @return the value at the given index (after normalisation) or null if no corresponding value was found
+ * @since 2.1.0
*/
- public static <T> Iterator<T> toUnique(Iterator<T> self, Comparator<? super T> comparator) {
- return new ToUniqueIterator<>(self, comparator);
+ public static <T> T getAt(Iterable<T> self, int idx) {
+ return getAt(self.iterator(), idx);
}
/**
- * Returns an iterator equivalent to this iterator with all duplicated
- * items removed by using the natural ordering of the items.
+ * Support the subscript operator for a Map.
+ * <pre class="groovyTestCase">def map = [1:10]
+ * assert map[1] == 10</pre>
*
- * @param self an Iterator
- * @return an Iterator with no duplicate items
- * @since 2.4.0
+ * @param self a Map
+ * @param key an Object as a key for the map
+ * @return the value corresponding to the given key
+ * @since 1.0
*/
- public static <T> Iterator<T> toUnique(Iterator<T> self) {
- return toUnique(self, (Comparator<T>) null);
+ public static <K,V> V getAt(Map<K,V> self, Object key) {
+ return self.get(key);
}
/**
- * 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")
+ * Support the subscript operator for a Bitset
*
- * 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 BitSet
+ * @param index index to retrieve
+ * @return value of the bit at the given index
+ * @see java.util.BitSet
+ * @since 1.5.0
+ */
+ public static boolean getAt(BitSet self, int index) {
+ int i = normaliseIndex(index, self.length());
+ return self.get(i);
+ }
+
+ /**
+ * Support retrieving a subset of a BitSet using a Range
*
- * @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 BitSet
+ * @param range a Range defining the desired subset
+ * @return a new BitSet that represents the requested subset
+ * @see java.util.BitSet
+ * @see groovy.lang.IntRange
+ * @since 1.5.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));
+ public static BitSet getAt(BitSet self, IntRange range) {
+ RangeInfo info = subListBorders(self.length(), range);
+ BitSet result = new BitSet();
+
+ int numberOfBits = info.to - info.from;
+ int adjuster = 1;
+ int offset = info.from;
+
+ if (info.reverse) {
+ adjuster = -1;
+ offset = info.to - 1;
+ }
+
+ for (int i = 0; i < numberOfBits; i++) {
+ result.set(i, self.get(offset + (adjuster * i)));
+ }
+
return result;
}
/**
- * 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>
+ * Support the subscript operator for Collection.
* <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]
+ * assert [String, Long, Integer] == ["a",5L,2]["class"]
* </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 coll a Collection
+ * @param property a String
+ * @return a List
+ * @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 List getAt(Collection coll, String property) {
+ List<Object> answer = new ArrayList<>(coll.size());
+ return getAtIterable(coll, property, answer);
+ }
+
+ private static List getAtIterable(Iterable coll, String property, List<Object> answer) {
+ for (Object item : coll) {
+ if (item == null) continue;
+ Object value;
+ try {
+ value = InvokerHelper.getProperty(item, property);
+ } catch (MissingPropertyExceptionNoStack mpe) {
+ String causeString = new MissingPropertyException(mpe.getProperty(), mpe.getType()).toString();
+ throw new MissingPropertyException("Exception evaluating property '" + property +
+ "' for " + coll.getClass().getName() + ", Reason: " + causeString);
+ }
+ answer.add(value);
+ }
+ return answer;
}
+ //--------------------------------------------------------------------------
+ // getGroovydoc
+
/**
- * 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>
+ * Gets runtime groovydoc.
*
- * @param self an Iterable
- * @return the Collection of non-duplicate items
- * @since 2.4.0
+ * @since 3.0.0
*/
- public static <T> Collection<T> toUnique(Iterable<T> self) {
- return toUnique(self, (Comparator<T>) null);
+ public static groovy.lang.groovydoc.Groovydoc getGroovydoc(AnnotatedElement holder) {
+ var groovydocAnnotation = holder.getAnnotation(groovy.lang.Groovydoc.class);
+ return groovydocAnnotation == null ? EMPTY_GROOVYDOC :
+ new groovy.lang.groovydoc.Groovydoc(groovydocAnnotation.value(), holder);
}
+ //--------------------------------------------------------------------------
+ // getIndices
+
/**
- * Returns a List containing the items from the List but with duplicates removed
- * using the natural ordering of the items to determine uniqueness.
+ * Returns indices of the collection.
* <p>
+ * Example:
* <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
+ * assert 0..2 == [5, 6, 7].indices
* </pre>
*
- * @param self a List
- * @return the List of non-duplicate items
+ * @param self a collection
+ * @return an index range
* @since 2.4.0
*/
- public static <T> List<T> toUnique(List<T> self) {
- return toUnique(self, (Comparator<T>) null);
+ public static IntRange getIndices(Collection self) {
+ return new IntRange(false, 0, self.size());
}
+ //--------------------------------------------------------------------------
+ // getLocation
+
/**
- * 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>
+ * Gets the url of the jar file/source file containing the specified class.
*
- * @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 the class
+ * @return the url of the jar, {@code null} if the specified class is from JDK
+ * @since 2.5.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 URL getLocation(Class self) {
+ CodeSource codeSource = self.getProtectionDomain().getCodeSource();
+ return (codeSource != null ? codeSource.getLocation() : null);
}
+ //--------------------------------------------------------------------------
+ // getMetaClass
+
/**
- * 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")
+ * Adds a "metaClass" property to all class objects so you can use the syntax
+ * <code>String.metaClass.myMethod = { println "foo" }</code>
*
- * 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 c The java.lang.Class instance
+ * @return An MetaClass instance
+ * @since 1.5.0
+ */
+ public static MetaClass getMetaClass(Class c) {
+ MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+ MetaClass mc = metaClassRegistry.getMetaClass(c);
+ if (mc instanceof ExpandoMetaClass
+ || mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass)
+ return mc;
+ else {
+ return new HandleMetaClass(mc);
+ }
+ }
+
+ /**
+ * Obtains a MetaClass for an object either from the registry or in the case of
+ * a GroovyObject from the object itself.
*
- * @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 obj The object in question
+ * @return The MetaClass
+ * @since 1.5.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 MetaClass getMetaClass(Object obj) {
+ MetaClass mc = InvokerHelper.getMetaClass(obj);
+ return new HandleMetaClass(mc, obj);
}
/**
- * Allows a Map to be iterated through in reverse order using a closure.
+ * Obtains a MetaClass for an object either from the registry or in the case of
+ * a GroovyObject from the object itself.
*
- * 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 obj The object in question
+ * @return The MetaClass
+ * @since 1.6.0
+ */
+ public static MetaClass getMetaClass(GroovyObject obj) {
+ // we need this method as trick to guarantee correct method selection
+ return getMetaClass((Object)obj);
+ }
+
+ //--------------------------------------------------------------------------
+ // getMetaProperties
+
+ /**
+ * Retrieves the list of {@link groovy.lang.MetaProperty} objects for 'self' and wraps it
+ * in a list of {@link groovy.lang.PropertyValue} objects that additionally provide
+ * the value for each property of 'self'.
*
- * @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 the receiver object
+ * @return list of {@link groovy.lang.PropertyValue} objects
+ * @see groovy.util.Expando#getMetaPropertyValues()
+ * @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());
+ public static List<PropertyValue> getMetaPropertyValues(Object self) {
+ MetaClass metaClass = InvokerHelper.getMetaClass(self);
+ List<MetaProperty> mps = metaClass.getProperties();
+ List<PropertyValue> props = new ArrayList<>(mps.size());
+ for (MetaProperty mp : mps) {
+ props.add(new PropertyValue(self, mp));
}
- return self;
+ return props;
}
+ //--------------------------------------------------------------------------
+ // getProperties
+
/**
- * 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>
+ * Convenience method that calls {@link #getMetaPropertyValues(java.lang.Object)}(self)
+ * and provides the data in form of simple key/value pairs, i.e. without
+ * type() information.
*
- * @param self a List
- * @param closure a closure to which each item is passed.
- * @return the original list
- * @since 1.5.0
+ * @param self the receiver object
+ * @return meta properties as Map of key/value pairs
+ * @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 Map<String, Object> getProperties(Object self) {
+ List<PropertyValue> metaProps = getMetaPropertyValues(self);
+ Map<String, Object> props = new LinkedHashMap<>(metaProps.size());
+
+ for (PropertyValue mp : metaProps) {
+ try {
+ props.put(mp.getName(), mp.getValue());
+ } catch (Exception e) {
+ java.util.logging.Logger.getLogger(DefaultGroovyMethods.class.getName())
+ .throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e);
+ }
+ }
+ return props;
}
+ //--------------------------------------------------------------------------
+ // getRootLoader
+
/**
- * Iterate over each element of the set in reverse order.
- * <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]
- * </pre>
+ * Iterates through the classloader parents until it finds a loader with a class
+ * named "org.codehaus.groovy.tools.RootLoader". If there is no such class
+ * <code>null</code> will be returned. The name is used for comparison because
+ * a direct comparison using == may fail as the class may be loaded through
+ * different classloaders.
*
- * @param self a NavigableSet
- * @param closure a closure to which each item is passed.
- * @return the original NavigableSet
- * @since 4.0.11
+ * @param self a ClassLoader
+ * @return the rootLoader for the ClassLoader
+ * @see org.codehaus.groovy.tools.RootLoader
+ * @since 1.5.0
*/
- public static <T> NavigableSet<T> reverseEach(NavigableSet<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- each(self.descendingIterator(), closure);
- return self;
+ public static ClassLoader getRootLoader(ClassLoader self) {
+ while (true) {
+ if (self == null) return null;
+ if (isRootLoaderClassOrSubClass(self)) return self;
+ self = self.getParent();
+ }
+ }
+
+ private static boolean isRootLoaderClassOrSubClass(ClassLoader self) {
+ Class current = self.getClass();
+ while(!current.getName().equals(Object.class.getName())) {
+ if(current.getName().equals(RootLoader.class.getName())) return true;
+ current = current.getSuperclass();
+ }
+
+ return false;
}
+ //--------------------------------------------------------------------------
+ // grep
+
/**
* Iterates over the collection of items which this Object represents and returns each item that matches
* the given filter - calling the <code>{@link #isCase(java.lang.Object, java.lang.Object)}</code>
@@ -6605,8123 +6426,8419 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
return grep(self, Closure.IDENTITY);
}
+ //--------------------------------------------------------------------------
+ // groupBy
+
/**
- * Convert an iterator to a List. The iterator will become
- * exhausted of elements after making this conversion.
+ * Sorts all Iterable members into groups determined by the supplied mapping closure.
+ * The closure should return the key that this item should be grouped by. The returned
+ * LinkedHashMap will have an entry for each distinct key returned from the closure,
+ * with each value being a list of items for that group.
+ * <p>
+ * Example usage:
+ * <pre class="groovyTestCase">
+ * assert [0:[2,4,6], 1:[1,3,5]] == [1,2,3,4,5,6].groupBy { it % 2 }
+ * </pre>
*
- * @param self an iterator
- * @return a List
- * @since 1.5.0
+ * @param self a collection to group
+ * @param closure a closure mapping entries on keys
+ * @return a new Map grouped by keys
+ * @since 2.2.0
*/
- public static <T> List<T> toList(Iterator<T> self) {
-
- List<T> answer = new ArrayList<>();
- while (self.hasNext()) {
- answer.add(self.next());
+ public static <K, T> Map<K, List<T>> groupBy(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<K> closure) {
+ Map<K, List<T>> answer = new LinkedHashMap<>();
+ for (T element : self) {
+ K value = closure.call(element);
+ groupAnswer(answer, element, value);
}
return answer;
}
/**
- * Convert an Iterable to a List. The Iterable's iterator will
- * become exhausted of elements after making this conversion.
- * <p>
+ * Sorts all Iterable members into (sub)groups determined by the supplied
+ * mapping closures. Each closure should return the key that this item
+ * should be grouped by. The returned LinkedHashMap will have an entry for each
+ * distinct 'key path' returned from the closures, with each value being a list
+ * of items for that 'group path'.
+ *
* Example usage:
- * <pre class="groovyTestCase">def x = [1,2,3] as HashSet
- * assert x.class == HashSet
- * assert x.toList() instanceof List</pre>
+ * <pre class="groovyTestCase">def result = [1,2,3,4,5,6].groupBy({ it % 2 }, { it {@code <} 4 })
+ * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]</pre>
*
- * @param self an Iterable
- * @return a List
- * @since 1.8.7
- */
- public static <T> List<T> toList(Iterable<T> self) {
- return toList(self.iterator());
- }
-
- /**
- * Convert an enumeration to a List.
+ * Another example:
+ * <pre>def sql = groovy.sql.Sql.newInstance(/* ... */)
+ * def data = sql.rows("SELECT * FROM a_table").groupBy({ it.column1 }, { it.column2 }, { it.column3 })
+ * if (data.val1.val2.val3) {
+ * // there exists a record where:
+ * // a_table.column1 == val1
+ * // a_table.column2 == val2, and
+ * // a_table.column3 == val3
+ * } else {
+ * // there is no such record
+ * }</pre>
+ * If an empty array of closures is supplied the IDENTITY Closure will be used.
*
- * @param self an enumeration
- * @return a List
- * @since 1.5.0
+ * @param self a collection to group
+ * @param closures an array of closures, each mapping entries on keys
+ * @return a new Map grouped by keys on each criterion
+ * @since 2.2.0
+ * @see Closure#IDENTITY
*/
- public static <T> List<T> toList(Enumeration<T> self) {
- List<T> answer = new ArrayList<>();
- while (self.hasMoreElements()) {
- answer.add(self.nextElement());
+ public static Map groupBy(Iterable self, Object... closures) {
+ final Closure head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0];
+
+ @SuppressWarnings("unchecked")
+ Map<Object, List> first = groupBy(self, head);
+ if (closures.length < 2)
+ return first;
+
+ final Object[] tail = new Object[closures.length - 1];
+ System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6
+
+ // inject([:]) { a,e {@code ->} a {@code <<} [(e.key): e.value.groupBy(tail)] }
+ Map<Object, Map> acc = new LinkedHashMap<>();
+ for (Map.Entry<Object, List> item : first.entrySet()) {
+ acc.put(item.getKey(), groupBy(item.getValue(), tail));
}
- return answer;
+
+ return acc;
}
/**
- * A variant of withCollectedValues for Iterators.
+ * Sorts all Iterable members into (sub)groups determined by the supplied
+ * mapping closures. Each closure should return the key that this item
+ * should be grouped by. The returned LinkedHashMap will have an entry for each
+ * distinct 'key path' returned from the closures, with each value being a list
+ * of items for that 'group path'.
*
- * @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
+ * Example usage:
+ * <pre class="groovyTestCase">
+ * def result = [1,2,3,4,5,6].groupBy([{ it % 2 }, { it {@code <} 4 }])
+ * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]
+ * </pre>
+ *
+ * Another example:
+ * <pre>
+ * def sql = groovy.sql.Sql.newInstance(/* ... */)
+ * def data = sql.rows("SELECT * FROM a_table").groupBy([{ it.column1 }, { it.column2 }, { it.column3 }])
+ * if (data.val1.val2.val3) {
+ * // there exists a record where:
+ * // a_table.column1 == val1
+ * // a_table.column2 == val2, and
+ * // a_table.column3 == val3
+ * } else {
+ * // there is no such record
+ * }
+ * </pre>
+ * If an empty list of closures is supplied the IDENTITY Closure will be used.
+ *
+ * @param self a collection to group
+ * @param closures a list of closures, each mapping entries on keys
+ * @return a new Map grouped by keys on each criterion
+ * @since 2.2.0
+ * @see Closure#IDENTITY
*/
- 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);
+ public static Map groupBy(Iterable self, List<Closure> closures) {
+ return groupBy(self, closures.toArray());
}
/**
- * A variant of withCollectedValues for Iterators.
+ * Groups the members of a map into sub maps determined by the
+ * supplied mapping closure. The closure will be passed a Map.Entry or
+ * key and value (depending on the number of parameters the closure accepts)
+ * and should return the key that each item should be grouped under. The
+ * resulting map will have an entry for each 'group' key returned by the
+ * closure, with values being the map members from the original map that
+ * belong to each group. (If instead of a map, you want a list of map entries
+ * use {code}groupEntriesBy{code}.)
+ * <p>
+ * If the <code>self</code> map is one of TreeMap, Hashtable or Properties,
+ * the returned Map will preserve that type, otherwise a LinkedHashMap will
+ * be returned.
+ * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy { it.value % 2 }
+ * assert result == [0:[b:2, d:4, f:6], 1:[a:1, c:3, e:5]]</pre>
*
- * @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
+ * @param self a map to group
+ * @param closure a closure mapping entries on keys
+ * @return a new Map grouped by keys
+ * @since 1.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 <G, K, V> Map<G, Map<K, V>> groupBy(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<G> closure) {
+ final Map<G, List<Map.Entry<K, V>>> initial = groupEntriesBy(self, closure);
+ final Map<G, Map<K, V>> answer = new LinkedHashMap<>();
+ for (Map.Entry<G, List<Map.Entry<K, V>>> outer : initial.entrySet()) {
+ G key = outer.getKey();
+ List<Map.Entry<K, V>> entries = outer.getValue();
+ Map<K, V> target = createSimilarMap(self);
+ putAll(target, entries);
+ answer.put(key, target);
+ }
+ return answer;
}
/**
- * 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}.
+ * Groups the members of a map into sub maps determined by the supplied
+ * mapping closures. Each closure will be passed a Map.Entry or key and
+ * value (depending on the number of parameters the closure accepts) and
+ * should return the key that each item should be grouped under. The
+ * resulting map will have an entry for each 'group path' returned by all
+ * closures, with values being the map members from the original map that
+ * belong to each such 'group path'.
*
- * @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
+ * If the <code>self</code> map is one of TreeMap, Hashtable, or Properties,
+ * the returned Map will preserve that type, otherwise a LinkedHashMap will
+ * be returned.
+ *
+ * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy({ it.value % 2 }, { it.key.next() })
+ * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]</pre>
+ * If an empty array of closures is supplied the IDENTITY Closure will be used.
+ *
+ * @param self a map to group
+ * @param closures an array of closures that map entries on keys
+ * @return a new map grouped by keys on each criterion
+ * @since 1.8.1
+ * @see Closure#IDENTITY
*/
- 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);
+ public static Map<Object, Map> groupBy(Map self, Object... closures) {
+ @SuppressWarnings("unchecked")
+ final Closure<Object> head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0];
+
+ @SuppressWarnings("unchecked")
+ Map<Object, Map> first = groupBy(self, head);
+ if (closures.length < 2)
+ return first;
+
+ final Object[] tail = new Object[closures.length - 1];
+ System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6
+
+ Map<Object, Map> acc = new LinkedHashMap<>();
+ for (Map.Entry<Object, Map> item: first.entrySet()) {
+ acc.put(item.getKey(), groupBy(item.getValue(), tail));
+ }
+
+ return acc;
}
/**
- * Transform Iterable elements into Map entries with keys unchanged
- * and values transformed using the supplied function.
- * <pre class="groovyTestCase">
- * 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}.
+ * Groups the members of a map into sub maps determined by the supplied
+ * mapping closures. Each closure will be passed a Map.Entry or key and
+ * value (depending on the number of parameters the closure accepts) and
+ * should return the key that each item should be grouped under. The
+ * resulting map will have an entry for each 'group path' returned by all
+ * closures, with values being the map members from the original map that
+ * belong to each such 'group path'.
*
- * @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
+ * If the <code>self</code> map is one of TreeMap, Hashtable, or Properties,
+ * the returned Map will preserve that type, otherwise a LinkedHashMap will
+ * be returned.
+ *
+ * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy([{ it.value % 2 }, { it.key.next() }])
+ * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]</pre>
+ * If an empty list of closures is supplied the IDENTITY Closure will be used.
+ *
+ * @param self a map to group
+ * @param closures a list of closures that map entries on keys
+ * @return a new map grouped by keys on each criterion
+ * @since 1.8.1
+ * @see Closure#IDENTITY
*/
- 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 Map<Object, Map> groupBy(Map self, List<Closure> closures) {
+ return groupBy(self, closures.toArray());
}
/**
- * A variant of withCollectedKeys for Iterators.
+ * Groups the current element according to the value
*
- * @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
+ * @param answer the map containing the results
+ * @param element the element to be placed
+ * @param value the value according to which the element will be placed
+ * @since 1.5.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());
+ protected static <K, T> void groupAnswer(final Map<K, List<T>> answer, T element, K value) {
+ List<T> groupedElements = answer.computeIfAbsent(value, k -> new ArrayList<>());
+
+ groupedElements.add(element);
}
+ //--------------------------------------------------------------------------
+ // groupEntriesBy
+
/**
- * A variant of withCollectedKeys for Iterators.
+ * Groups all map entries into groups determined by the
+ * supplied mapping closure. The closure will be passed a Map.Entry or
+ * key and value (depending on the number of parameters the closure accepts)
+ * and should return the key that each item should be grouped under. The
+ * resulting map will have an entry for each 'group' key returned by the
+ * closure, with values being the list of map entries that belong to each
+ * group. (If instead of a list of map entries, you want an actual map
+ * use {code}groupBy{code}.)
+ * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupEntriesBy { it.value % 2 }
+ * assert result[0]*.key == ["b", "d", "f"]
+ * assert result[1]*.value == [1, 3, 5]</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
+ * @param self a map to group
+ * @param closure a 1 or 2 arg Closure mapping entries on keys
+ * @return a new Map grouped by keys
+ * @since 1.5.2
*/
- public static <K, V> Map<K, V> withCollectedKeys(Iterator<V> values, Function<? super V, K> keyTransform) {
- return withCollectedKeys(values, new LinkedHashMap<>(), keyTransform);
+ public static <G, K, V> Map<G, List<Map.Entry<K, V>>> groupEntriesBy(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<G> closure) {
+ final Map<G, List<Map.Entry<K, V>>> answer = new LinkedHashMap<>();
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ G value = callClosureForMapEntry(closure, entry);
+ groupAnswer(answer, entry, value);
+ }
+ return answer;
}
+ //--------------------------------------------------------------------------
+
/**
- * 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}.
+ * Identity check. Since == is overridden in Groovy with the meaning of equality
+ * we need some fallback to check for object identity. Invoke using the
+ * 'is' method, like so: <code>def same = this.is(that)</code>
*
- * @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
+ * @param self an object
+ * @param other an object to compare identity with
+ * @return true if self and other are both references to the same
+ * instance, false otherwise
+ * @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());
+ public static boolean is(Object self, Object other) {
+ return self == other;
}
/**
- * 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}.
+ * Allows the closure to be called for the object reference self.
+ * Synonym for 'with()'.
*
- * @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 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, Closure)
+ * @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);
+ public static <T,U> T identity(
+ @DelegatesTo.Target("self") U self,
+ @DelegatesTo(value=DelegatesTo.Target.class,
+ target="self",
+ strategy=Closure.DELEGATE_FIRST)
+ @ClosureParams(FirstParam.class)
+ Closure<T> closure) {
+ return DefaultGroovyMethods.with(self, closure);
}
/**
- * Finds the first value matching the closure condition.
- *
+ * 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 numbers = [1, 2, 3]
- * def result = numbers.find { it {@code >} 1}
- * assert result == 2
+ * 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 an Object with an iterator returning its values
- * @param closure a closure condition
- * @return the first Object found or null if none was found
- * @since 1.0
+ * @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 Object find(Object self, Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
- Object value = iter.next();
- if (bcw.call(value)) {
- return value;
- }
- }
- return null;
+ @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);
}
/**
- * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth).
- * <p>
- * Example:
+ * 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 items = [null, 0, 0.0, false, '', [], 42, 43]
- * assert items.find() == 42
+ * 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.
*
- * @param self an Object with an Iterator returning its values
- * @return the first Object found or null if none was found
- * @since 1.8.1
- * @see Closure#IDENTITY
- */
- public static Object find(Object self) {
- return find(self, Closure.IDENTITY);
- }
-
- /**
- * Finds the first value matching the closure condition. Example:
- * <pre class="groovyTestCase">def list = [1,2,3]
- * assert 2 == list.find { it {@code >} 1 }
+ * 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 Collection
- * @param closure a closure condition
- * @return the first Object found, in the order of the collections iterator, or null if no element matches
- * @since 1.0
+ * @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 <T> T find(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (T value : self) {
- if (bcw.call(value)) {
- return value;
- }
- }
- return null;
+ 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;
}
/**
- * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth).
+ * Allows the closure to be called for the object reference self (similar
+ * to <code>with</code>) and always returns self.
* <p>
- * Example:
- * <pre class="groovyTestCase">
- * def items = [null, 0, 0.0, false, '', [], 42, 43]
- * assert items.find() == 42
+ * 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 a Collection
- * @return the first Object found or null if none was found
- * @since 1.8.1
- * @see Closure#IDENTITY
+ * @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> T find(Collection<T> self) {
- return find(self, Closure.IDENTITY);
+ @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);
}
/**
- * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns null.
- * <p>
- * <pre class="groovyTestCase">
- * int[] numbers = [1, 2, 3]
- * assert numbers.findResult { if(it {@code >} 1) return it } == 2
- * assert numbers.findResult { if(it {@code >} 4) return it } == null
- * </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 Object with an iterator returning its values
- * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
- * @return the first non-null result of the closure
- * @since 1.7.5
+ * @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 Object findResult(Object self, Closure condition) {
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
- Object value = iter.next();
- Object result = condition.call(value);
- if (result != null) {
- return result;
- }
- }
- return null;
+ public static void putAt(Object self, String property, Object newValue) {
+ InvokerHelper.setProperty(self, property, newValue);
}
/**
- * Treats the object as iterable, iterating through the values it represents and returns the first non-null value, otherwise returns null.
- * <p>
- * <pre class="groovyTestCase">
- * class Foo {
- * List items
- * Iterator iterator() {
- * items.iterator()
- * }
- * }
- * assert new Foo(items: [null, 2, 4]).findResult() == 2
- * assert new Foo(items: [null, null]).findResult() == null
- * </pre>
+ * Scoped use method
*
- * @param self an Object with an iterator returning its values
- * @return the first non-null result of the closure
- * @since 4.0.9
+ * @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
*/
- public static Object findResult(Object self) {
- return findResult(self, Closure.IDENTITY);
+ public static <T> T use(Object self, Class categoryClass, Closure<T> closure) {
+ return GroovyCategorySupport.use(categoryClass, closure);
}
/**
- * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns the defaultResult.
- * <p>
- * <pre class="groovyTestCase">
- * int[] numbers = [1, 2, 3]
- * assert numbers.findResult(5) { if(it {@code >} 1) return it } == 2
- * assert numbers.findResult(5) { if(it {@code >} 4) return it } == 5
- * </pre>
+ * Extend object with category methods.
+ * All methods for given class and all super classes will be added to the object.
*
- * @param self an Object with an iterator returning its values
- * @param defaultResult an Object that should be returned if all closure results are null
- * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
- * @return the first non-null result of the closure, otherwise the default value
- * @since 1.7.5
+ * @param self any Class
+ * @param categoryClasses a category classes to use
+ * @since 1.6.0
*/
- public static Object findResult(Object self, Object defaultResult, Closure condition) {
- Object result = findResult(self, condition);
- if (result == null) return defaultResult;
- return result;
+ public static void mixin(MetaClass self, List<Class> categoryClasses) {
+ MixinInMetaClass.mixinClassesToMetaClass(self, categoryClasses);
}
/**
- * Treats the object as iterable, iterating through the values it represents and returns the first non-null result, otherwise returns the defaultResult.
- * <p>
- * <pre class="groovyTestCase">
- * class Foo {
- * List items
- * Iterator iterator() {
- * items.iterator()
- * }
- * }
- * assert new Foo(items: [null, 2, 4]).findResult(5) == 2
- * assert new Foo(items: [null, null]).findResult(5) == 5
- * </pre>
+ * Extend class globally with category methods.
+ * All methods for given class and all super classes will be added to the class.
*
- * @param self an Object with an iterator returning its values
- * @param defaultResult an Object that should be returned if all elements are null
- * @return the first non-null element, otherwise the default value
- * @since 4.0.9
+ * @param self any Class
+ * @param categoryClasses a category classes to use
+ * @since 1.6.0
*/
- public static Object findResult(Object self, Object defaultResult) {
- Object result = findResult(self, Closure.IDENTITY);
- if (result == null) return defaultResult;
- return result;
+ public static void mixin(Class self, List<Class> categoryClasses) {
+ mixin(getMetaClass(self), categoryClasses);
}
/**
- * Iterates through the Iterator calling the given closure condition for each item but stopping once the first non-null
- * result is found and returning that result. If all are null, the defaultResult is returned.
- * <p>
- * Examples:
- * <pre class="groovyTestCase">
- * def iter = [1,2,3].iterator()
- * assert "Found 2" == iter.findResult("default") { it {@code >} 1 ? "Found $it" : null }
- * assert "default" == iter.findResult("default") { it {@code >} 3 ? "Found $it" : null }
- * </pre>
+ * Extend class globally with category methods.
*
- * @param self an Iterator
- * @param defaultResult an Object that should be returned if all closure results are null
- * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
- * @return the first non-null result from calling the closure, or the defaultValue
- * @since 2.5.0
+ * @param self any Class
+ * @param categoryClass a category class to use
+ * @since 1.6.0
*/
- public static <S, T, U extends T, V extends T> T findResult(Iterator<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
- T result = findResult(self, condition);
- if (result == null) return defaultResult;
- return result;
+ public static void mixin(Class self, Class categoryClass) {
+ mixin(getMetaClass(self), Collections.singletonList(categoryClass));
}
/**
- * Iterates through the Iterator stopping once the first non-null
- * result is found and returning that result. If all are null, the defaultResult is returned.
- * <p>
- * Examples:
- * <pre class="groovyTestCase">
- * assert [null, 1, 2].iterator().findResult('default') == 1
- * assert [null, null].findResult('default') == 'default'
- * </pre>
+ * Extend class globally with category methods.
*
- * @param self an Iterator
- * @param defaultResult an Object that should be returned if all elements are null
- * @return the first non-null result from the iterator, or the defaultValue
- * @since 4.0.9
+ * @param self any Class
+ * @param categoryClass a category class to use
+ * @since 1.6.0
*/
- public static <T, U extends T, V extends T> T findResult(Iterator<U> self, V defaultResult) {
- T result = (T) findResult(self, Closure.IDENTITY);
- return result == null ? defaultResult : result;
+ public static void mixin(Class self, Class[] categoryClass) {
+ mixin(getMetaClass(self), Arrays.asList(categoryClass));
}
/**
- * Iterates through the Iterator calling the given closure condition for each item but stopping once the first non-null
- * result is found and returning that result. If all results are null, null is returned.
+ * Extend class globally with category methods.
*
- * @param self an Iterator
- * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
- * @return the first non-null result from calling the closure, or null
- * @since 2.5.0
+ * @param self any Class
+ * @param categoryClass a category class to use
+ * @since 1.6.0
*/
- public static <T, U> T findResult(Iterator<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
- while (self.hasNext()) {
- U next = self.next();
- T result = condition.call(next);
- if (result != null) {
- return result;
- }
- }
- return null;
+ public static void mixin(MetaClass self, Class categoryClass) {
+ mixin(self, Collections.singletonList(categoryClass));
}
/**
- * Iterates through the Iterator stopping once the first non-null
- * result is found and returning that result. If all results are null, null is returned.
+ * Extend class globally with category methods.
*
- * @param self an Iterator
- * @return the first non-null result from the iterator, or null
- * @since 4.0.9
+ * @param self any Class
+ * @param categoryClass a category class to use
+ * @since 1.6.0
*/
- public static <T> T findResult(Iterator<T> self) {
- return (T) findResult(self, Closure.IDENTITY);
+ public static void mixin(MetaClass self, Class[] categoryClass) {
+ mixin(self, Arrays.asList(categoryClass));
}
/**
- * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
- * result is found and returning that result. If all are null, the defaultResult is returned.
- * <p>
- * Examples:
- * <pre class="groovyTestCase">
- * def list = [1,2,3]
- * assert "Found 2" == list.findResult("default") { it {@code >} 1 ? "Found $it" : null }
- * assert "default" == list.findResult("default") { it {@code >} 3 ? "Found $it" : null }
- * </pre>
+ * Scoped use method with list of categories.
*
- * @param self an Iterable
- * @param defaultResult an Object that should be returned if all closure results are null
- * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
- * @return the first non-null result from calling the closure, or the defaultValue
- * @since 2.5.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 <S, T, U extends T, V extends T> T findResult(Iterable<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
- T result = findResult(self, condition);
- if (result == null) return defaultResult;
- return result;
+ public static <T> T use(Object self, List<Class> categoryClassList, Closure<T> closure) {
+ return GroovyCategorySupport.use(categoryClassList, closure);
}
/**
- * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
- * result is found and returning that result. If all are null, the defaultResult is returned.
- * <p>
- * Examples:
- * <pre class="groovyTestCase">
- * assert [null, 1, 2].findResult('default') == 1
- * assert [null, null].findResult('default') == 'default'
- * </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 an Iterable
- * @param defaultResult an Object that should be returned if all elements in the iterable are null
- * @return the first non-null element from the iterable, or the defaultValue
- * @since 4.0.9
+ * @param self any Object
+ * @param array a list of category classes and a Closure
+ * @return the value returned from the closure
+ * @since 1.0
*/
- public static <T, U extends T, V extends T> T findResult(Iterable<U> self, V defaultResult) {
- T result = (T) findResult(self, Closure.IDENTITY);
- if (result == null) return defaultResult;
- return result;
+ @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);
}
/**
- * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
- * result is found and returning that result. If all results are null, null is returned.
+ * Inspects returns the String that matches what would be typed into a
+ * terminal to create this object.
*
- * @param self an Iterable
- * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
- * @return the first non-null result from calling the closure, or null
- * @since 2.5.0
+ * @param self any Object
+ * @return a String that matches what would be typed into a terminal to
+ * create this object. e.g. [1, 'hello'].inspect() {@code ->} [1, 'hello']
+ * @since 1.0
*/
- public static <T, U> T findResult(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
- return findResult(self.iterator(), condition);
+ public static String inspect(Object self) {
+ return FormatHelper.inspect(self);
}
/**
- * Iterates through the Iterable stopping once the first non-null
- * result is found and returning that result. If all results are null, null is returned.
+ * Provide a dynamic method invocation method which can be overloaded in
+ * classes to implement dynamic proxies easily.
*
- * @param self an Iterable
- * @return the first non-null element from the iterable, or null
- * @since 4.0.9
+ * @param object any Object
+ * @param method the name of the method to call
+ * @param arguments the arguments to use
+ * @return the result of the method call
+ * @since 1.0
*/
- public static <T> T findResult(Iterable<T> self) {
- return (T) findResult(self.iterator(), Closure.IDENTITY);
+ public static Object invokeMethod(Object object, String method, Object arguments) {
+ return InvokerHelper.invokeMethod(object, method, arguments);
}
+ //--------------------------------------------------------------------------
+ // print/println/printf/sprintf
+
/**
- * Returns the first non-null closure result found by passing each map entry to the closure, otherwise null is returned.
- * If the closure takes two parameters, the entry key and value are passed.
- * If the closure takes one parameter, the Map.Entry object is passed.
- * <pre class="groovyTestCase">
- * assert "Found b:3" == [a:1, b:3].findResult { if (it.value == 3) return "Found ${it.key}:${it.value}" }
- * assert null == [a:1, b:3].findResult { if (it.value == 9) return "Found ${it.key}:${it.value}" }
- * assert "Found a:1" == [a:1, b:3].findResult { k, v {@code ->} if (k.size() + v == 2) return "Found $k:$v" }
- * </pre>
+ * Print a value formatted Groovy style to self if it
+ * is a Writer, otherwise to the standard output stream.
*
- * @param self a Map
- * @param condition a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
- * @return the first non-null result collected by calling the closure, or null if no such result was found
- * @since 1.7.5
+ * @param self any Object
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T, K, V> T findResult(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> condition) {
- for (Map.Entry<K, V> entry : self.entrySet()) {
- T result = callClosureForMapEntry(condition, entry);
- if (result != null) {
- return result;
- }
- }
- return null;
- }
-
+ 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));
+ }
+ }
+
/**
- * Returns the first non-null closure result found by passing each map entry to the closure, otherwise the defaultResult is returned.
- * If the closure takes two parameters, the entry key and value are passed.
- * If the closure takes one parameter, the Map.Entry object is passed.
- * <pre class="groovyTestCase">
- * assert "Found b:3" == [a:1, b:3].findResult("default") { if (it.value == 3) return "Found ${it.key}:${it.value}" }
- * assert "default" == [a:1, b:3].findResult("default") { if (it.value == 9) return "Found ${it.key}:${it.value}" }
- * assert "Found a:1" == [a:1, b:3].findResult("default") { k, v {@code ->} if (k.size() + v == 2) return "Found $k:$v" }
- * </pre>
+ * Print a value formatted Groovy style to the print writer.
*
- * @param self a Map
- * @param defaultResult an Object that should be returned if all closure results are null
- * @param condition a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
- * @return the first non-null result collected by calling the closure, or the defaultResult if no such result was found
- * @since 1.7.5
+ * @param self a PrintWriter
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T, U extends T, V extends T, A, B> T findResult(Map<A, B> self, U defaultResult, @ClosureParams(MapEntryOrKeyValue.class) Closure<V> condition) {
- T result = findResult(self, condition);
- if (result == null) return defaultResult;
- return result;
+ public static void print(PrintWriter self, Object value) {
+ self.print(FormatHelper.toString(value));
}
/**
- * Iterates through the Iterable transforming items using the supplied closure
- * and collecting any non-null results.
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * def list = [1,2,3]
- * def result = list.findResults { it {@code >} 1 ? "Found $it" : null }
- * assert result == ["Found 2", "Found 3"]
- * </pre>
+ * Print a value formatted Groovy style to the print stream.
*
- * @param self an Iterable
- * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
- * @return the list of non-null transformed values
- * @since 2.2.0
+ * @param self a PrintStream
+ * @param value the value to print
+ * @since 1.6.0
*/
- public static <T, U> Collection<T> findResults(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
- return findResults(self.iterator(), filteringTransform);
+ public static void print(PrintStream self, Object value) {
+ self.print(FormatHelper.toString(value));
}
/**
- * Iterates through the Iterable collecting any non-null results.
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * assert [1, null, 2, null, 3].findResults() == [1, 2, 3]
- * </pre>
+ * Print a value to the standard output stream.
+ * This method delegates to the owner to execute the method.
*
- * @param self an Iterable
- * @return the list of non-null values
- * @since 4.0.9
+ * @param self a generated closure
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T> Collection<T> findResults(Iterable<T> self) {
- return findResults(self.iterator(), Closure.IDENTITY);
+ public static void print(Closure self, Object value) {
+ Object owner = getClosureOwner(self);
+ InvokerHelper.invokeMethod(owner, "print", new Object[]{value});
}
/**
- * Iterates through the Iterator transforming items using the supplied closure
- * and collecting any non-null results.
+ * Print to a console in interactive format.
*
- * @param self an Iterator
- * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
- * @return the list of non-null transformed values
- * @since 2.5.0
+ * @param self any Object
+ * @param out the PrintWriter used for printing
+ * @since 1.0
*/
- public static <T, U> Collection<T> findResults(Iterator<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
- List<T> result = new ArrayList<>();
- while (self.hasNext()) {
- U value = self.next();
- T transformed = filteringTransform.call(value);
- if (transformed != null) {
- result.add(transformed);
- }
+ public static void print(Object self, PrintWriter out) {
+ if (out == null) {
+ out = new PrintWriter(System.out);
}
- return result;
+ out.print(FormatHelper.toString(self));
}
/**
- * Iterates through the Iterator collecting any non-null results.
+ * Print a linebreak to the standard output stream.
*
- * @param self an Iterator
- * @return the list of non-null values
- * @since 4.0.9
+ * @param self any Object
+ * @since 1.0
*/
- public static <T> Collection<T> findResults(Iterator<T> self) {
- return findResults(self, Closure.IDENTITY);
+ 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();
+ }
}
/**
- * Iterates through the map transforming items using the supplied closure
- * and collecting any non-null results.
- * If the closure takes two parameters, the entry key and value are passed.
- * If the closure takes one parameter, the Map.Entry object is passed.
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * def map = [a:1, b:2, hi:2, cat:3, dog:2]
- * def result = map.findResults { k, v {@code ->} k.size() == v ? "Found $k:$v" : null }
- * assert result == ["Found a:1", "Found hi:2", "Found cat:3"]
- * </pre>
+ * Print a linebreak to the standard output stream.
+ * This method delegates to the owner to execute the method.
*
- * @param self a Map
- * @param filteringTransform a 1 or 2 arg Closure that should return either a non-null transformed value or null for items which should be discarded
- * @return the list of non-null transformed values
- * @since 1.8.1
+ * @param self a closure
+ * @since 1.0
*/
- public static <T, K, V> Collection<T> findResults(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> filteringTransform) {
- List<T> result = new ArrayList<>();
- for (Map.Entry<K, V> entry : self.entrySet()) {
- T transformed = callClosureForMapEntry(filteringTransform, entry);
- if (transformed != null) {
- result.add(transformed);
- }
- }
- return result;
+ public static void println(Closure self) {
+ Object owner = getClosureOwner(self);
+ InvokerHelper.invokeMethod(owner, "println", InvokerHelper.EMPTY_ARGS);
}
/**
- * Finds the first entry matching the closure condition.
- * If the closure takes two parameters, the entry key and value are passed.
- * If the closure takes one parameter, the Map.Entry object is passed.
- * <pre class="groovyTestCase">assert [a:1, b:3].find { it.value == 3 }.key == "b"</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 a Map
- * @param closure a 1 or 2 arg Closure condition
- * @return the first Object found
+ * @param self any Object
+ * @param value the value to print
* @since 1.0
*/
- public static <K, V> Map.Entry<K, V> find(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<?> closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Map.Entry<K, V> entry : self.entrySet()) {
- if (bcw.callForMap(entry)) {
- return entry;
- }
+ 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));
}
- return null;
}
/**
- * Finds all entries matching the closure condition. If the
- * closure takes one parameter then it will be passed the Map.Entry.
- * Otherwise if the closure should take two parameters, which will be
- * the key and the value.
- * <p>
- * If the <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.
- * <p>
- * Example usage:
- * <pre class="groovyTestCase">
- * def result = [a:1, b:2, c:4, d:5].findAll { it.value % 2 == 0 }
- * assert result.every { it instanceof Map.Entry }
- * assert result*.key == ["b", "c"]
- * assert result*.value == [2, 4]
- * </pre>
+ * Print a value formatted Groovy style (followed by a newline) to the print writer.
*
- * @param self a Map
- * @param closure a 1 or 2 arg Closure condition applying on the entries
- * @return a new subMap
+ * @param self a PrintWriter
+ * @param value the value to print
* @since 1.0
*/
- public static <K, V> Map<K, V> findAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure closure) {
- Map<K, V> answer = createSimilarMap(self);
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Map.Entry<K, V> entry : self.entrySet()) {
- if (bcw.callForMap(entry)) {
- answer.put(entry.getKey(), entry.getValue());
- }
- }
- return answer;
+ public static void println(PrintWriter self, Object value) {
+ self.println(FormatHelper.toString(value));
}
/**
- * Finds all values matching the closure condition.
- * <pre class="groovyTestCase">assert ([2,4] as Set) == ([1,2,3,4] as Set).findAll { it % 2 == 0 }</pre>
+ * Print a value formatted Groovy style (followed by a newline) to the print stream.
*
- * @param self a Set
- * @param closure a closure condition
- * @return a Set of matching values
- * @since 2.4.0
+ * @param self any Object
+ * @param value the value to print
+ * @since 1.6.0
*/
- public static <T> Set<T> findAll(Set<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return (Set<T>) findAll((Collection<T>) self, closure);
+ public static void println(PrintStream self, Object value) {
+ self.println(FormatHelper.toString(value));
}
/**
- * Finds all values matching the closure condition.
- * <pre class="groovyTestCase">assert [2,4] == [1,2,3,4].findAll { it % 2 == 0 }</pre>
+ * Print a value (followed by a newline) to the standard output stream.
+ * This method delegates to the owner to execute the method.
*
- * @param self a List
- * @param closure a closure condition
- * @return a List of matching values
- * @since 2.4.0
+ * @param self a closure
+ * @param value the value to print
+ * @since 1.0
*/
- public static <T> List<T> findAll(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return (List<T>) findAll((Collection<T>) self, closure);
+ public static void println(Closure self, Object value) {
+ Object owner = getClosureOwner(self);
+ InvokerHelper.invokeMethod(owner, "println", new Object[]{value});
}
/**
- * Finds all values matching the closure condition.
- * <pre class="groovyTestCase">assert [2,4] == [1,2,3,4].findAll { it % 2 == 0 }</pre>
+ * Print to a console in interactive format.
*
- * @param self a Collection
- * @param closure a closure condition
- * @return a Collection of matching values
- * @since 1.5.6
+ * @param self any Object
+ * @param out the PrintWriter used for printing
+ * @since 1.0
*/
- public static <T> Collection<T> findAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return findMany(createSimilarCollection(self), self.iterator(), closure);
+ public static void println(Object self, PrintWriter out) {
+ if (out == null) {
+ out = new PrintWriter(System.out);
+ }
+ out.println(FormatHelper.toString(self));
}
/**
- * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth).
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null] as Set
- * assert items.findAll() == [1, 2, true, 'foo', [4, 5]] as Set
- * </pre>
- *
- * @param self a Set
- * @return a Set of the truthy values
- * @since 2.4.0
- * @see Closure#IDENTITY
- */
- public static <T> Set<T> findAll(Set<T> self) {
- return findAll(self, Closure.IDENTITY);
- }
-
- /**
- * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth).
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
- * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
- * </pre>
+ * Printf to the standard output stream.
*
- * @param self a List
- * @return a List of the truthy values
- * @since 2.4.0
- * @see Closure#IDENTITY
+ * @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> List<T> findAll(List<T> self) {
- return findAll(self, Closure.IDENTITY);
+ 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);
}
/**
- * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth).
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
- * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
- * </pre>
+ * 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 a Collection
- * @return a Collection of the truthy values
- * @since 1.8.1
- * @see Closure#IDENTITY
+ * @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> Collection<T> findAll(Collection<T> self) {
- return findAll(self, Closure.IDENTITY);
+ 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);
}
/**
- * Finds all items matching the closure condition.
+ * 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 Object with an Iterator returning its values
- * @param closure a closure condition
- * @return a List of the values found
- * @since 1.6.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
*/
- @SuppressWarnings("unchecked")
- public static List findAll(Object self, Closure closure) {
- return findMany(new ArrayList(), InvokerHelper.asIterator(self), closure);
+ 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);
}
/**
- * Finds all items matching the IDENTITY Closure (i.e. matching Groovy truth).
+ * Prints a formatted string using the specified format string and arguments.
* <p>
- * Example:
- * <pre class="groovyTestCase">
- * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
- * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
+ * 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>
*
- * @param self an Object with an Iterator returning its values
- * @return a List of the truthy values
- * @since 1.8.1
- * @see Closure#IDENTITY
+ * @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 List findAll(Object self) {
- return findAll(self, Closure.IDENTITY);
+ 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);
}
- static <T, C extends Collection<T>> C findMany(C collector, Iterator<? extends T> iter, Closure<?> closure) {
- BooleanClosureWrapper test = new BooleanClosureWrapper(closure);
- while (iter.hasNext()) {
- T value = iter.next();
- if (test.call(value)) {
- collector.add(value);
- }
- }
- return collector;
+ private static void printf(PrintStream self, String format, Object arg) {
+ self.print(sprintf(self, format, arg));
}
- /**
- * 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.
- *
- * @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
- */
- @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);
+ 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);
+ }
}
/**
- * 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.
+ * Sprintf to a string.
*
- * @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 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
*/
- @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 String sprintf(Object self, String format, Object[] values) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(outputStream);
+ out.printf(format, values);
+ return outputStream.toString();
}
/**
- * 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.
+ * Returns a formatted string using the specified format string and
+ * arguments.
*
- * @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 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> 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;
- }
+ public static String sprintf(Object self, String format, Object arg) {
+ if (arg instanceof Object[]) {
+ return sprintf(self, format, (Object[]) arg);
}
- return result;
- }
-
- /**
- * 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.
- *
- * @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 <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;
- }
+ 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);
}
- return result;
- }
-
- /**
- * 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.
- *
- * @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> boolean removeAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
- return self.removeIf(new BooleanClosurePredicate<>(condition));
- }
-
- /**
- * 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>
- *
- * 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 <K, V> boolean removeAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
- return self.entrySet().removeIf(new BooleanClosureForMapPredicate<>(condition));
- }
-
- /**
- * 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 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
- */
- @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));
- }
-
- /**
- * 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 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> 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);
+ 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 + ")");
}
- answer.add(accept);
- answer.add(reject);
- return answer;
- }
-
- /**
- * 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 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
- */
- @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);
+ return sprintf(self, format, ans);
}
- /**
- * 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);
+ private static Object getClosureOwner(Closure<?> cls) {
+ Object owner = cls.getOwner();
+ while (owner instanceof GeneratedClosure) {
+ owner = ((Closure<?>) owner).getOwner();
+ }
+ return owner;
}
- /**
- * 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>
- *
- * @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);
- }
+ //-------------------------------------------------------------------------
+ // isCase/isNotCase
/**
- * 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>
+ * Method for overloading the behavior of the 'case' method in switch statements.
+ * The default implementation handles arrays types but otherwise simply delegates
+ * to Object#equals, but this may be overridden for other types. In this example:
+ * <pre> switch( a ) {
+ * case b: //some code
+ * }</pre>
+ * "some code" is called when <code>b.isCase( a )</code> returns
+ * <code>true</code>.
*
- * @param self the Iterable of items
- * @return the permutations from the list
- * @since 1.7.0
+ * @param caseValue the case value
+ * @param switchValue the switch value
+ * @return true if the switchValue is deemed to be equal to the caseValue
+ * @since 1.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 boolean isCase(Object caseValue, Object switchValue) {
+ if (caseValue.getClass().isArray()) {
+ return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);
}
- return ans;
+ return caseValue.equals(switchValue);
}
/**
- * 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>
+ * Special 'Case' implementation for Class, which allows testing
+ * whether some switch value is assignable from the given case class.
*
- * @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,V> List<V> permutations(Iterable<T> self, Closure<V> function) {
- return collect(permutations(self), function);
- }
-
- /**
- * 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>
+ * If the switch value is an object, {@code isCase} will return true if the
+ * switch value is assignment compatible with the class (case value),
+ * i.e. is an {@code instanceof} the class, for example:
* <pre class="groovyTestCase">
- * def result = [['a', 'b'], [1, 2], [3, 4]].transpose()
- * assert result == [['a', 1, 3], ['b', 2, 4]]
+ * def someList = []
+ * switch (someList) {
+ * case List:
+ * assert true : 'is a list'
+ * break
+ * case Map:
+ * assert false : 'is not a Map'
+ * break
+ * default:
+ * assert false : 'should never get here'
+ * break
+ * }
* </pre>
*
- * @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 List transpose(List self) {
- return GroovyCollections.transpose(self);
- }
-
- /**
- * Sorts all Iterable members into groups determined by the supplied mapping closure.
- * The closure should return the key that this item should be grouped by. The returned
- * LinkedHashMap will have an entry for each distinct key returned from the closure,
- * with each value being a list of items for that group.
- * <p>
- * Example usage:
+ * If the switch value is a class, {@code isCase} will return true if the
+ * switch value is assignable from the given class (case value), i.e. the case class
+ * is the same as, or a superclass, or a super-interface of the switch class, for example:
* <pre class="groovyTestCase">
- * assert [0:[2,4,6], 1:[1,3,5]] == [1,2,3,4,5,6].groupBy { it % 2 }
+ * switch (ArrayList) {
+ * case List:
+ * assert true : 'is a list'
+ * break
+ * case Map:
+ * assert false : 'is not a Map'
+ * break
+ * default:
+ * assert false : 'should never get here'
+ * break
+ * }
* </pre>
*
- * @param self a collection to group
- * @param closure a closure mapping entries on keys
- * @return a new Map grouped by keys
- * @since 2.2.0
+ * @param caseValue the case value
+ * @param switchValue the switch value
+ * @return true if the switchValue is deemed to be assignable from the given class
+ * @since 1.0
*/
- public static <K, T> Map<K, List<T>> groupBy(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<K> closure) {
- Map<K, List<T>> answer = new LinkedHashMap<>();
- for (T element : self) {
- K value = closure.call(element);
- groupAnswer(answer, element, value);
+ @SuppressWarnings("unchecked")
+ public static boolean isCase(Class caseValue, Object switchValue) {
+ if (switchValue instanceof Class) {
+ Class val = (Class) switchValue;
+ return caseValue.isAssignableFrom(val);
}
- return answer;
+ return caseValue.isInstance(switchValue);
}
/**
- * Sorts all Iterable members into (sub)groups determined by the supplied
- * mapping closures. Each closure should return the key that this item
- * should be grouped by. The returned LinkedHashMap will have an entry for each
- * distinct 'key path' returned from the closures, with each value being a list
- * of items for that 'group path'.
- *
- * Example usage:
- * <pre class="groovyTestCase">def result = [1,2,3,4,5,6].groupBy({ it % 2 }, { it {@code <} 4 })
- * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]</pre>
- *
- * Another example:
- * <pre>def sql = groovy.sql.Sql.newInstance(/* ... */)
- * def data = sql.rows("SELECT * FROM a_table").groupBy({ it.column1 }, { it.column2 }, { it.column3 })
- * if (data.val1.val2.val3) {
- * // there exists a record where:
- * // a_table.column1 == val1
- * // a_table.column2 == val2, and
- * // a_table.column3 == val3
- * } else {
- * // there is no such record
+ * 'Case' implementation for collections which tests if the 'switch'
+ * operand is contained in any of the 'case' values.
+ * For example:
+ * <pre class="groovyTestCase">switch( 3 ) {
+ * case [1,3,5]:
+ * assert true
+ * break
+ * default:
+ * assert false
* }</pre>
- * If an empty array of closures is supplied the IDENTITY Closure will be used.
*
- * @param self a collection to group
- * @param closures an array of closures, each mapping entries on keys
- * @return a new Map grouped by keys on each criterion
- * @since 2.2.0
- * @see Closure#IDENTITY
+ * @param caseValue the case value
+ * @param switchValue the switch value
+ * @return true if the caseValue is deemed to contain the switchValue
+ * @see java.util.Collection#contains(java.lang.Object)
+ * @since 1.0
*/
- public static Map groupBy(Iterable self, Object... closures) {
- final Closure head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0];
-
- @SuppressWarnings("unchecked")
- Map<Object, List> first = groupBy(self, head);
- if (closures.length < 2)
- return first;
-
- final Object[] tail = new Object[closures.length - 1];
- System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6
-
- // inject([:]) { a,e {@code ->} a {@code <<} [(e.key): e.value.groupBy(tail)] }
- Map<Object, Map> acc = new LinkedHashMap<>();
- for (Map.Entry<Object, List> item : first.entrySet()) {
- acc.put(item.getKey(), groupBy(item.getValue(), tail));
- }
-
- return acc;
+ public static boolean isCase(Collection caseValue, Object switchValue) {
+ return caseValue.contains(switchValue);
}
/**
- * Sorts all Iterable members into (sub)groups determined by the supplied
- * mapping closures. Each closure should return the key that this item
- * should be grouped by. The returned LinkedHashMap will have an entry for each
- * distinct 'key path' returned from the closures, with each value being a list
- * of items for that 'group path'.
- *
- * Example usage:
- * <pre class="groovyTestCase">
- * def result = [1,2,3,4,5,6].groupBy([{ it % 2 }, { it {@code <} 4 }])
- * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]
- * </pre>
- *
- * Another example:
- * <pre>
- * def sql = groovy.sql.Sql.newInstance(/* ... */)
- * def data = sql.rows("SELECT * FROM a_table").groupBy([{ it.column1 }, { it.column2 }, { it.column3 }])
- * if (data.val1.val2.val3) {
- * // there exists a record where:
- * // a_table.column1 == val1
- * // a_table.column2 == val2, and
- * // a_table.column3 == val3
- * } else {
- * // there is no such record
+ * 'Case' implementation for iterable types which tests if the 'switch'
+ * operand is contained in any of the 'case' values.
+ * For example:
+ * <pre class="groovyTestCase">Iterable it = {[1,3,5].iterator()}
+ * switch( 3 ) {
+ * case it:
+ * assert true
+ * break
+ * default:
+ * assert false
* }
+ *
+ * //GROOVY-7919
+ * assert 1 in it
+ * assert 2 !in it
* </pre>
- * If an empty list of closures is supplied the IDENTITY Closure will be used.
*
- * @param self a collection to group
- * @param closures a list of closures, each mapping entries on keys
- * @return a new Map grouped by keys on each criterion
- * @since 2.2.0
- * @see Closure#IDENTITY
+ * @param caseValue the case value
+ * @param switchValue the switch value
+ * @return true if the caseValue is deemed to contain the switchValue
+ * @see #contains(Iterable,Object)
+ * @since 5.0.0
*/
- public static Map groupBy(Iterable self, List<Closure> closures) {
- return groupBy(self, closures.toArray());
+ public static boolean isCase(Iterable caseValue, Object switchValue) {
+ return contains(caseValue, switchValue);
}
/**
- * Groups all map entries into groups determined by the
- * supplied mapping closure. The closure will be passed a Map.Entry or
- * key and value (depending on the number of parameters the closure accepts)
- * and should return the key that each item should be grouped under. The
- * resulting map will have an entry for each 'group' key returned by the
- * closure, with values being the list of map entries that belong to each
- * group. (If instead of a list of map entries, you want an actual map
- * use {code}groupBy{code}.)
- * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupEntriesBy { it.value % 2 }
- * assert result[0]*.key == ["b", "d", "f"]
- * assert result[1]*.value == [1, 3, 5]</pre>
+ * 'Case' implementation for maps which tests the groovy truth
+ * value obtained using the 'switch' operand as key.
+ * For example:
+ * <pre class="groovyTestCase">switch( 'foo' ) {
+ * case [foo:true, bar:false]:
+ * assert true
+ * break
+ * default:
+ * assert false
+ * }</pre>
*
- * @param self a map to group
- * @param closure a 1 or 2 arg Closure mapping entries on keys
- * @return a new Map grouped by keys
- * @since 1.5.2
+ * @param caseValue the case value
+ * @param switchValue the switch value
+ * @return the groovy truth value from caseValue corresponding to the switchValue key
+ * @since 1.7.6
*/
- public static <G, K, V> Map<G, List<Map.Entry<K, V>>> groupEntriesBy(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<G> closure) {
- final Map<G, List<Map.Entry<K, V>>> answer = new LinkedHashMap<>();
- for (Map.Entry<K, V> entry : self.entrySet()) {
- G value = callClosureForMapEntry(closure, entry);
- groupAnswer(answer, entry, value);
- }
- return answer;
+ public static boolean isCase(Map caseValue, Object switchValue) {
+ return DefaultTypeTransformation.castToBoolean(caseValue.get(switchValue));
}
/**
- * Groups the members of a map into sub maps determined by the
- * supplied mapping closure. The closure will be passed a Map.Entry or
- * key and value (depending on the number of parameters the closure accepts)
- * and should return the key that each item should be grouped under. The
- * resulting map will have an entry for each 'group' key returned by the
- * closure, with values being the map members from the original map that
- * belong to each group. (If instead of a map, you want a list of map entries
- * use {code}groupEntriesBy{code}.)
- * <p>
- * If the <code>self</code> map is one of TreeMap, Hashtable or Properties,
- * the returned Map will preserve that type, otherwise a LinkedHashMap will
- * be returned.
- * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy { it.value % 2 }
- * assert result == [0:[b:2, d:4, f:6], 1:[a:1, c:3, e:5]]</pre>
+ * Special 'case' implementation for all numbers, which delegates to the
+ * <code>compareTo()</code> method for comparing numbers of different
+ * types.
*
- * @param self a map to group
- * @param closure a closure mapping entries on keys
- * @return a new Map grouped by keys
- * @since 1.0
+ * @param caseValue the case value
+ * @param switchValue the switch value
+ * @return true if the numbers are deemed equal
+ * @since 1.5.0
*/
- public static <G, K, V> Map<G, Map<K, V>> groupBy(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<G> closure) {
- final Map<G, List<Map.Entry<K, V>>> initial = groupEntriesBy(self, closure);
- final Map<G, Map<K, V>> answer = new LinkedHashMap<>();
- for (Map.Entry<G, List<Map.Entry<K, V>>> outer : initial.entrySet()) {
- G key = outer.getKey();
- List<Map.Entry<K, V>> entries = outer.getValue();
- Map<K, V> target = createSimilarMap(self);
- putAll(target, entries);
- answer.put(key, target);
- }
- return answer;
+ public static boolean isCase(Number caseValue, Number switchValue) {
+ return NumberMath.compareTo(caseValue, switchValue) == 0;
}
/**
- * Groups the members of a map into sub maps determined by the supplied
- * mapping closures. Each closure will be passed a Map.Entry or key and
- * value (depending on the number of parameters the closure accepts) and
- * should return the key that each item should be grouped under. The
- * resulting map will have an entry for each 'group path' returned by all
- * closures, with values being the map members from the original map that
- * belong to each such 'group path'.
- *
- * If the <code>self</code> map is one of TreeMap, Hashtable, or Properties,
- * the returned Map will preserve that type, otherwise a LinkedHashMap will
- * be returned.
- *
- * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy({ it.value % 2 }, { it.key.next() })
- * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]</pre>
- * If an empty array of closures is supplied the IDENTITY Closure will be used.
- *
- * @param self a map to group
- * @param closures an array of closures that map entries on keys
- * @return a new map grouped by keys on each criterion
- * @since 1.8.1
- * @see Closure#IDENTITY
+ * @since 4.0.0
*/
- public static Map<Object, Map> groupBy(Map self, Object... closures) {
- @SuppressWarnings("unchecked")
- final Closure<Object> head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0];
+ public static boolean isNotCase(Number caseValue, Number switchValue) {
+ return !isCase(caseValue, switchValue);
+ }
- @SuppressWarnings("unchecked")
- Map<Object, Map> first = groupBy(self, head);
- if (closures.length < 2)
- return first;
+ /**
+ * @since 4.0.0
+ */
+ public static boolean isNotCase(Object caseValue, Object switchValue) {
+ return !isCase(caseValue, switchValue);
+ }
- final Object[] tail = new Object[closures.length - 1];
- System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6
+ /**
+ * @since 4.0.0
+ */
+ public static boolean isNotCase(Class<?> caseValue, Object switchValue) {
+ return !isCase(caseValue, switchValue);
+ }
- Map<Object, Map> acc = new LinkedHashMap<>();
- for (Map.Entry<Object, Map> item: first.entrySet()) {
- acc.put(item.getKey(), groupBy(item.getValue(), tail));
- }
+ /**
+ * @since 4.0.0
+ */
+ public static boolean isNotCase(Closure<?> caseValue, Object switchValue) {
+ return !caseValue.isCase(switchValue);
+ }
- return acc;
+ /**
+ * @since 4.0.0
+ */
+ public static boolean isNotCase(Collection<?> caseValue, Object switchValue) {
+ return !isCase(caseValue, switchValue);
}
/**
- * Groups the members of a map into sub maps determined by the supplied
- * mapping closures. Each closure will be passed a Map.Entry or key and
- * value (depending on the number of parameters the closure accepts) and
- * should return the key that each item should be grouped under. The
- * resulting map will have an entry for each 'group path' returned by all
- * closures, with values being the map members from the original map that
- * belong to each such 'group path'.
- *
- * If the <code>self</code> map is one of TreeMap, Hashtable, or Properties,
- * the returned Map will preserve that type, otherwise a LinkedHashMap will
- * be returned.
- *
- * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy([{ it.value % 2 }, { it.key.next() }])
- * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]</pre>
- * If an empty list of closures is supplied the IDENTITY Closure will be used.
- *
- * @param self a map to group
- * @param closures a list of closures that map entries on keys
- * @return a new map grouped by keys on each criterion
- * @since 1.8.1
- * @see Closure#IDENTITY
+ * @since 4.0.0
+ */
+ public static boolean isNotCase(Map<?, ?> caseValue, Object switchValue) {
+ return !isCase(caseValue, switchValue);
+ }
+
+ //--------------------------------------------------------------------------
+
+ /**
+ * 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
*/
- public static Map<Object, Map> groupBy(Map self, List<Closure> closures) {
- return groupBy(self, closures.toArray());
+ public static <T> Iterator<T> unique(Iterator<T> self) {
+ return uniqueItems(new IteratorIterableAdapter<>(self)).listIterator();
}
/**
- * Groups the current element according to the value
+ * 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 answer the map containing the results
- * @param element the element to be placed
- * @param value the value according to which the element will be placed
- * @since 1.5.0
+ * @param self a collection
+ * @return the now modified collection
+ * @see #unique(Collection, boolean)
+ * @since 1.0
*/
- protected static <K, T> void groupAnswer(final Map<K, List<T>> answer, T element, K value) {
- List<T> groupedElements = answer.computeIfAbsent(value, k -> new ArrayList<>());
-
- groupedElements.add(element);
+ public static <T> Collection<T> unique(Collection<T> self) {
+ return unique(self, true);
}
- // 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);
+ /**
+ * 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 a List
+ * @return the now modified List
+ * @see #unique(Collection, boolean)
+ * @since 2.4.0
+ */
+ public static <T> List<T> unique(List<T> self) {
+ return (List<T>) unique((Collection<T>) self, true);
}
- // 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);
+ /**
+ * 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>
+ * <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 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 <T> Collection<T> unique(Collection<T> self, boolean mutate) {
+ List<T> answer = uniqueItems(self);
+ if (mutate) {
+ self.clear();
+ self.addAll(answer);
}
- return closure.call(line);
+ return mutate ? self : answer ;
}
- // 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);
+ 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 closure.call(entry);
+ return answer;
}
/**
- * Performs the same function as the version of inject that takes an initial value, but
- * uses the head of the Collection as the initial value, and iterates over the tail.
+ * 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 * 2 * 3 * 4 == [ 1, 2, 3, 4 ].inject { acc, val {@code ->} acc * val }
- * assert ['b'] == [['a','b'], ['b','c'], ['d','b']].inject { acc, val {@code ->} acc.intersect( val ) }
- * LinkedHashSet set = [ 't', 'i', 'm' ]
- * assert 'tim' == set.inject { a, b {@code ->} a + b }
+ * 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 a Collection
- * @param closure a closure
- * @return the result of the last closure call
- * @throws NoSuchElementException if the collection is empty.
- * @see #inject(Collection, Object, Closure)
- * @since 1.8.7
+ * @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
*/
- @SuppressWarnings("unchecked")
- public static <T, V extends T> T inject(Collection<T> self, @ClosureParams(value=FromString.class,options="V,T") Closure<V> closure ) {
- if( self.isEmpty() ) {
- throw new NoSuchElementException( "Cannot call inject() on an empty collection without passing an initial value." ) ;
- }
- Iterator<T> iter = self.iterator();
- T head = iter.next();
- Collection<T> tail = tail(self);
- if (!tail.iterator().hasNext()) {
- return head;
- }
- // cast with explicit weaker generics for now to keep jdk6 happy, TODO: find better fix
- return (T) inject((Collection) tail, head, closure);
+ public static <T> List<T> unique(List<T> self, boolean mutate) {
+ return (List<T>) unique((Collection<T>) self, mutate);
}
/**
- * Iterates through the given Collection, passing in the initial value to
- * the 2-arg closure along with the first item. The result is passed back (injected) into
- * the closure along with the second item. The new result is injected back into
- * the closure along with the third item and so on until the entire collection
- * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional parlance.
- *
- * Examples:
- * <pre class="groovyTestCase">
- * assert 1*1*2*3*4 == [1,2,3,4].inject(1) { acc, val {@code ->} acc * val }
- *
- * assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val {@code ->} acc + val }
- *
- * assert 'The quick brown fox' ==
- * ['quick', 'brown', 'fox'].inject('The') { acc, val {@code ->} acc + ' ' + val }
- *
- * assert 'bat' ==
- * ['rat', 'bat', 'cat'].inject('zzz') { min, next {@code ->} next {@code <} min ? next : min }
- *
- * def max = { a, b {@code ->} [a, b].max() }
- * def animals = ['bat', 'rat', 'cat']
- * assert 'rat' == animals.inject('aaa', max)
- * </pre>
- * Visual representation of the last example above:
- * <pre>
- * initVal animals[0]
- * v v
- * max('aaa', 'bat') {@code =>} 'bat' animals[1]
- * v v
- * max('bat', 'rat') {@code =>} 'rat' animals[2]
- * v v
- * max('rat', 'cat') {@code =>} 'rat'
- * </pre>
+ * Provides a method that compares two comparables using Groovy's
+ * default number aware comparator.
*
- * @param self a Collection
- * @param initialValue some initial value
- * @param closure a closure
- * @return the result of the last closure call
- * @since 1.0
+ * @param self a Comparable
+ * @param other another Comparable
+ * @return a -ve number, 0 or a +ve number according to Groovy's compareTo contract
+ * @since 1.6.0
*/
- @SuppressWarnings("unchecked")
- public static <E, T, U extends T, V extends T> T inject(Collection<E> self, U initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> closure) {
- // cast with explicit weaker generics for now to keep jdk6 happy, TODO: find better fix
- return (T) inject((Iterator) self.iterator(), initialValue, closure);
+ public static int numberAwareCompareTo(Comparable self, Comparable other) {
+ return COMPARABLE_NUMBER_AWARE_COMPARATOR.compare(self, other);
}
/**
- * Iterates through the given Map, passing in the initial value to
- * the 2-arg Closure along with the first item (or 3-arg Closure along with the first key and value).
- * The result is passed back (injected) into
- * the closure along with the second item. The new result is injected back into
- * the closure along with the third item and so on until the entire collection
- * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional parlance.
+ * 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).
*
- * Examples:
- * <pre class="groovyTestCase">
- * def map = [a:1, b:2, c:3]
- * assert map.inject([]) { list, k, v {@code ->}
- * list + [k] * v
- * } == ['a', 'b', 'b', 'c', 'c', 'c']
- * </pre>
+ * @param self an Iterator
+ * @param condition a Closure used to determine unique items
+ * @return the modified Iterator
+ * @since 1.5.5
+ */
+ 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);
+ }
+
+ /**
+ * 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 a Map
- * @param initialValue some initial value
- * @param closure a 2 or 3 arg Closure
- * @return the result of the last closure call
- * @since 1.8.1
+ * @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 <K, V, T, U extends T, W extends T> T inject(Map<K, V> self, U initialValue, @ClosureParams(value=FromString.class,options={"U,Map.Entry<K,V>","U,K,V"}) Closure<W> closure) {
- T value = initialValue;
- for (Map.Entry<K, V> entry : self.entrySet()) {
- if (closure.getMaximumNumberOfParameters() == 3) {
- value = closure.call(value, entry.getKey(), entry.getValue());
- } else {
- value = closure.call(value, entry);
- }
- }
- return value;
+ public static <T> Collection<T> unique(Collection<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return unique(self, true, closure);
}
/**
- * Iterates through the given Iterator, passing in the initial value to
- * the closure along with the first item. The result is passed back (injected) into
- * the closure along with the second item. The new result is injected back into
- * the closure along with the third item and so on until the Iterator has been
- * expired of values. Also known as foldLeft in functional parlance.
+ * 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 initialValue some initial value
- * @param closure a closure
- * @return the result of the last closure call
- * @see #inject(Collection, Object, Closure)
- * @since 1.5.0
+ * @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 <E,T, U extends T, V extends T> T inject(Iterator<E> self, U initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> closure) {
- T value = initialValue;
- Object[] params = new Object[2];
- while (self.hasNext()) {
- Object item = self.next();
- params[0] = value;
- params[1] = item;
- value = closure.call(params);
- }
- return value;
+ 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);
}
/**
- * Iterates through the given Object, passing in the first value to
- * the closure along with the first item. The result is passed back (injected) into
- * the closure along with the second item. The new result is injected back into
- * the closure along with the third item and so on until further iteration of
- * the object is not possible. Also known as foldLeft in functional parlance.
+ * 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 Object
- * @param closure a closure
- * @return the result of the last closure call
- * @throws NoSuchElementException if the collection is empty.
- * @see #inject(Collection, Object, Closure)
- * @since 1.8.7
+ * @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
*/
- @SuppressWarnings("unchecked")
- public static <T, V extends T> T inject(Object self, Closure<V> closure) {
- Iterator iter = InvokerHelper.asIterator(self);
- if( !iter.hasNext() ) {
- throw new NoSuchElementException( "Cannot call inject() over an empty iterable without passing an initial value." ) ;
+ 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));
}
- Object initialValue = iter.next() ;
- return (T) inject(iter, initialValue, closure);
+ return self;
}
/**
- * Iterates through the given Object, passing in the initial value to
- * the closure along with the first item. The result is passed back (injected) into
- * the closure along with the second item. The new result is injected back into
- * the closure along with the third item and so on until further iteration of
- * the object is not possible. Also known as foldLeft in functional parlance.
+ * 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>
*
- * @param self an Object
- * @param initialValue some initial value
- * @param closure a closure
- * @return the result of the last closure call
- * @see #inject(Collection, Object, Closure)
- * @since 1.5.0
+ * @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
*/
- @SuppressWarnings("unchecked")
- public static <T, U extends T, V extends T> T inject(Object self, U initialValue, Closure<V> closure) {
- Iterator iter = InvokerHelper.asIterator(self);
- return (T) inject(iter, initialValue, closure);
+ 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);
}
/**
- * 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>
+ * 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 self Iterable of values to add together
- * @return The sum of all the items
- * @see #sum(Iterator)
- * @since 2.2.0
+ * @param self an Iterator
+ * @param comparator a Comparator
+ * @return the modified Iterator
+ * @since 1.5.5
*/
- public static Object sum(Iterable<?> self) {
- return sum(self.iterator(), null, true);
+ 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;
+ }
}
/**
- * 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.
+ * 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
+ * }
+ * }
*
- * @param self an Iterator for the values to add together
- * @return The sum of all the items
- * @since 1.5.5
+ * 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 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
*/
- public static Object sum(Iterator<Object> self) {
- return sum(self, null, true);
+ public static <T> Collection<T> unique(Collection<T> self, Comparator<? super T> comparator) {
+ return unique(self, true, comparator) ;
}
/**
- * Sums the items in an Iterable, adding the result to some initial value.
+ * 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">
- * assert 5+1+2+3+4 == [1,2,3,4].sum(5)
+ * 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 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
+ * @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
*/
- public static Object sum(Iterable<?> self, Object initialValue) {
- return sum(self.iterator(), initialValue, false);
+ public static <T> List<T> unique(List<T> self, Comparator<? super T> comparator) {
+ return (List<T>) unique((Collection<T>) self, true, comparator);
}
/**
- * 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.
+ * 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 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
+ * 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
*/
- public static Object sum(Iterator<?> self, Object initialValue) {
- return sum(self, initialValue, false);
+ 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;
}
- 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, 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;
+ }
}
- 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.
+ * 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
+ * }
+ * }
*
- * @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
+ * 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 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
*/
- 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, Comparator<? super T> comparator) {
+ return (List<T>) unique((Collection<T>) self, mutate, comparator);
}
/**
- * 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 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>
*
- * @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 an Iterator with no duplicate items
+ * @since 2.4.0
*/
- 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> 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));
+ }
+
+ 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);
+ }
+ }
+ }
}
/**
- * 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>
+ * Returns an iterator equivalent to this iterator with all duplicated
+ * items removed by using the supplied comparator.
*
- * @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 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 <T> Object sum(Iterable<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return sum(self.iterator(), initialValue, closure, false);
+ public static <T> Iterator<T> toUnique(Iterator<T> self, Comparator<? super T> comparator) {
+ return new ToUniqueIterator<>(self, comparator);
}
/**
- * 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.
+ * Returns an iterator equivalent to this iterator with all duplicated
+ * items removed by using the natural ordering of the items.
*
- * @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 an Iterator
+ * @return an Iterator with no duplicate items
+ * @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);
+ public static <T> Iterator<T> toUnique(Iterator<T> self) {
+ return toUnique(self, (Comparator<T>) null);
}
- 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);
- }
+ /**
+ * 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>
+ *
+ * @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> Collection<T> toUnique(Iterable<T> self, Comparator<? super T> comparator) {
+ Collection<T> result = createSimilarCollection(self);
+ addAll(result, toUnique(self.iterator(), comparator));
return result;
}
/**
- * Concatenates the <code>toString()</code> representation of each item from
- * the Iterator, with the given String as a separator between each item. The
- * iterator will become exhausted of elements after producing the resulting
- * conjoined value.
+ * 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>
+ * <pre class="groovyTestCase">
+ * class Person {
+ * def fname, lname
+ * String toString() {
+ * return fname + " " + lname
+ * }
+ * }
*
- * @param self an Iterator of objects
- * @param separator a String separator
- * @return the joined String
- * @since 1.5.5
+ * 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>
+ *
+ * @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 String join(final Iterator<?> self, final String separator) {
- StringJoiner joiner = new StringJoiner(separator != null ? separator : "");
- while (self.hasNext()) {
- Object obj = self.next();
- String str = FormatHelper.toString(obj);
- joiner.add(str);
- }
- return joiner.toString();
+ public static <T> List<T> toUnique(List<T> self, Comparator<? super T> comparator) {
+ return (List<T>) toUnique((Iterable<T>) self, comparator);
}
/**
- * Concatenates the <code>toString()</code> representation of each item from
- * the Iterable, with the given String as a separator between each item.
+ * 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 [1,2,3].join() == "123"</pre>
- * <pre class="groovyTestCase">assert [1,2,3].join(", ") == "1, 2, 3"</pre>
+ * <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>
*
- * @param self an Iterable of objects
- * @param separator a String separator
- * @return the joined String
- * @since 1.0
+ * @param self an Iterable
+ * @return the Collection of non-duplicate items
+ * @since 2.4.0
*/
- public static String join(final Iterable<?> self, final String separator) {
- return join(self.iterator(), separator);
+ public static <T> Collection<T> toUnique(Iterable<T> self) {
+ return toUnique(self, (Comparator<T>) null);
}
/**
- * Adds min() method to Collection objects.
- * <pre class="groovyTestCase">assert 2 == [4,2,5].min()</pre>
+ * 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>
*
- * @param self a Collection
- * @return the minimum value
- * @see #min(Iterator)
- * @since 1.0
+ * @param self a List
+ * @return the List of non-duplicate items
+ * @since 2.4.0
*/
- public static <T> T min(Iterable<T> self) {
- return min(self.iterator());
+ public static <T> List<T> toUnique(List<T> self) {
+ return toUnique(self, (Comparator<T>) null);
}
/**
- * Adds min() method to Iterator objects. The iterator will become
- * exhausted of elements after determining the minimum value.
+ * 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
+ * }
+ * }
*
- * @param self an Iterator
- * @return the minimum value
- * @see #min(Iterable)
- * @since 1.5.5
+ * 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 condition a Closure used to determine unique items
+ * @return a new Collection
+ * @see #toUnique(Iterable, Comparator)
+ * @since 2.4.0
*/
- public static <T> T min(Iterator<T> self) {
- T answer = null;
- while (self.hasNext()) {
- T value = self.next();
- if (value != null && (answer == null || ScriptBytecodeAdapter.compareLessThan(value, answer))) {
- answer = value;
- }
- }
- return answer;
+ 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);
}
/**
- * Selects the minimum value found in the Iterable using the given comparator.
- * <pre class="groovyTestCase">assert "hi" == ["hello","hi","hey"].min( { a, b {@code ->} a.length() {@code <=>} b.length() } as Comparator )</pre>
+ * 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
+ * }
+ * }
*
- * @param self an Iterable
- * @param comparator a Comparator
- * @return the minimum value or null for an empty Iterable
- * @see #min(Iterator, java.util.Comparator)
- * @since 2.2.0
+ * 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 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 <T> T min(Iterable<T> self, Comparator<? super T> comparator) {
- return min(self.iterator(), comparator);
+ 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);
}
/**
- * Selects the minimum value found from the Iterator using the given comparator.
+ * Allows a Map to be iterated through in reverse order using a closure.
*
- * @param self an Iterator
- * @param comparator a Comparator
- * @return the minimum value
- * @since 1.5.5
+ * 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 <T> T min(Iterator<T> self, Comparator<? super T> comparator) {
- Objects.requireNonNull(self);
- T answer = null;
- boolean first = true;
- while (self.hasNext()) {
- T value = self.next();
- if (first) {
- first = false;
- answer = value;
- } else if (comparator.compare(value, answer) < 0) {
- answer = value;
- }
+ @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 answer;
+ return self;
}
/**
- * Selects the item in the iterable which when passed as a parameter to the supplied closure returns the
- * minimum value. A null return value represents the least possible return value. If more than one item
- * has the minimum value, an arbitrary choice is made between the items having the minimum value.
- * <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" == ["hello","hi","hey"].min { it.length() }
- * </pre>
- * <pre class="groovyTestCase">
- * def lastDigit = { a, b {@code ->} a % 10 {@code <=>} b % 10 }
- * assert [19, 55, 91].min(lastDigit) == 91
- * </pre>
- * <pre class="groovyTestCase">
- * def pets = ['dog', 'cat', 'anaconda']
- * def shortestName = pets.min{ it.size() } // one of 'dog' or 'cat'
- * assert shortestName.size() == 3
- * </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 an Iterable
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return an item from the Iterable having the minimum value returned by calling the supplied closure with that item as parameter or null for an empty Iterable
- * @see #min(Iterator)
- * @since 1.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> T min(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return min(self.iterator(), closure);
+ public static <T> List<T> reverseEach(List<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ each(new ReverseListIterator<>(self), closure);
+ return self;
}
/**
- * Selects an entry in the map having the minimum
- * calculated value as determined by the supplied closure.
- * If more than one entry has the minimum value,
- * an arbitrary choice is made between the entries having the minimum value.
- * <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">
- * def zoo = [monkeys:6, lions:5, tigers:7]
- * def leastCommonEntry = zoo.min{ it.value }
- * assert leastCommonEntry.value == 5
- * def mostCommonEntry = zoo.min{ a, b {@code ->} b.value {@code <=>} a.value } // double negative!
- * assert mostCommonEntry.value == 7
- * </pre>
- * Edge case for multiple min values:
+ * Iterate over each element of the set in reverse order.
* <pre class="groovyTestCase">
- * def zoo = [monkeys:6, lions:5, tigers:7]
- * def lastCharOfName = { e {@code ->} e.key[-1] }
- * def ans = zoo.min(lastCharOfName) // some random entry
- * assert lastCharOfName(ans) == 's'
+ * 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 a Map
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return the Map.Entry having the minimum value as determined by the closure
- * @since 1.7.6
+ * @param self a NavigableSet
+ * @param closure a closure to which each item is passed.
+ * @return the original NavigableSet
+ * @since 4.0.11
*/
- public static <K, V> Map.Entry<K, V> min(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>", "Map.Entry<K,V>,Map.Entry<K,V>"}) Closure closure) {
- return min(self.entrySet(), closure);
+ public static <T> NavigableSet<T> reverseEach(NavigableSet<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ each(self.descendingIterator(), closure);
+ return self;
}
/**
- * Selects an entry in the map having the maximum
- * calculated value as determined by the supplied closure.
- * If more than one entry has the maximum value,
- * an arbitrary choice is made between the entries having the maximum value.
- * <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. An example:
- * <pre class="groovyTestCase">
- * def zoo = [monkeys:6, lions:5, tigers:7]
- * def mostCommonEntry = zoo.max{ it.value }
- * assert mostCommonEntry.value == 7
- * def leastCommonEntry = zoo.max{ a, b {@code ->} b.value {@code <=>} a.value } // double negative!
- * assert leastCommonEntry.value == 5
- * </pre>
- * Edge case for multiple max values:
- * <pre class="groovyTestCase">
- * def zoo = [monkeys:6, lions:5, tigers:7]
- * def lengthOfNamePlusNumber = { e {@code ->} e.key.size() + e.value }
- * def ans = zoo.max(lengthOfNamePlusNumber) // one of [monkeys:6, tigers:7]
- * assert lengthOfNamePlusNumber(ans) == 13
- * </pre>
+ * Convert an iterator to a List. The iterator will become
+ * exhausted of elements after making this conversion.
*
- * @param self a Map
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return the Map.Entry having the maximum value as determined by the closure
- * @since 1.7.6
+ * @param self an iterator
+ * @return a List
+ * @since 1.5.0
*/
- public static <K, V> Map.Entry<K, V> max(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>", "Map.Entry<K,V>,Map.Entry<K,V>"}) Closure closure) {
- return max(self.entrySet(), closure);
- }
+ public static <T> List<T> toList(Iterator<T> self) {
- /**
- * Selects the minimum value found from the Iterator
- * using the closure to determine the correct ordering.
- * The iterator will become
- * exhausted of elements after this operation.
- * <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 an Iterator
- * @param closure a Closure used to determine the correct ordering
- * @return the minimum value
- * @since 1.5.5
- */
- public static <T> T min(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- int params = closure.getMaximumNumberOfParameters();
- if (params != 1) {
- return min(self, new ClosureComparator<>(closure));
- }
- Objects.requireNonNull(self);
- boolean first = true;
- T answer = null;
- Object answerValue = null;
+ List<T> answer = new ArrayList<>();
while (self.hasNext()) {
- T item = self.next();
- Object value = closure.call(item);
- if (first) {
- first = false;
- answer = item;
- answerValue = value;
- } else if (ScriptBytecodeAdapter.compareLessThan(value, answerValue)) {
- answer = item;
- answerValue = value;
- }
+ answer.add(self.next());
}
return answer;
}
/**
- * Adds max() method to Iterable objects.
- * <pre class="groovyTestCase">
- * assert 5 == [2,3,1,5,4].max()
- * </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 an Iterable
- * @return the maximum value
- * @see #max(Iterator)
- * @since 2.2.0
+ * @return a List
+ * @since 1.8.7
*/
- public static <T> T max(Iterable<T> self) {
- return max(self.iterator());
+ public static <T> List<T> toList(Iterable<T> self) {
+ return toList(self.iterator());
}
/**
- * Adds max() method to Iterator objects. The iterator will become
- * exhausted of elements after determining the maximum value.
+ * Convert an enumeration to a List.
*
- * @param self an Iterator
- * @return the maximum value
- * @since 1.5.5
+ * @param self an enumeration
+ * @return a List
+ * @since 1.5.0
*/
- public static <T> T max(Iterator<T> self) {
- T answer = null;
- while (self.hasNext()) {
- T value = self.next();
- if (value != null && (answer == null || ScriptBytecodeAdapter.compareGreaterThan(value, answer))) {
- answer = value;
- }
+ public static <T> List<T> toList(Enumeration<T> self) {
+ List<T> answer = new ArrayList<>();
+ while (self.hasMoreElements()) {
+ answer.add(self.nextElement());
}
return answer;
}
/**
- * Selects the item in the iterable which when passed as a parameter to the supplied closure returns the
- * maximum value. A null return value represents the least possible return value, so any item for which
- * the supplied closure returns null, won't be selected (unless all items return null). If more than one item
- * has the maximum value, an arbitrary choice is made between the items having the maximum value.
- * <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 "hello" == ["hello","hi","hey"].max { it.length() }</pre>
- * <pre class="groovyTestCase">assert "hello" == ["hello","hi","hey"].max { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
- * <pre class="groovyTestCase">
- * def pets = ['dog', 'elephant', 'anaconda']
- * def longestName = pets.max{ it.size() } // one of 'elephant' or 'anaconda'
- * assert longestName.size() == 8
- * </pre>
+ * A variant of withCollectedValues for Iterators.
*
- * @param self an Iterable
- * @param closure a 1 or 2 arg Closure used to determine the correct ordering
- * @return an item from the Iterable having the maximum value returned by calling the supplied closure with that item as parameter or null for an empty Iterable
- * @since 2.2.0
+ * @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 <T> T max(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return max(self.iterator(), closure);
+ 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);
}
/**
- * Selects the maximum value found from the Iterator
- * using the closure to determine the correct ordering.
- * The iterator will become exhausted of elements after this operation.
- * <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 withCollectedValues for Iterators.
*
- * @param self an Iterator
- * @param closure a Closure used to determine the correct ordering
- * @return the maximum value
- * @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> T max(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- int params = closure.getMaximumNumberOfParameters();
- if (params != 1) {
- return max(self, new ClosureComparator<>(closure));
- }
- boolean first = true;
- T answer = null;
- Object answerValue = null;
- while (self.hasNext()) {
- T item = self.next();
- Object value = closure.call(item);
- if (first) {
- first = false;
- answer = item;
- answerValue = value;
- } else if (ScriptBytecodeAdapter.compareLessThan(answerValue, value)) {
- answer = item;
- answerValue = value;
- }
- }
- return answer;
+ public static <K, V> Map<K, V> withCollectedValues(Iterator<K> keys, Function<? super K, V> valueTransform) {
+ return withCollectedValues(keys, new LinkedHashMap<>(), valueTransform);
}
/**
- * Selects the maximum value found in the Iterable using the given comparator.
+ * 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">
- * assert "hello" == ["hello","hi","hey"].max( { a, b {@code ->} a.length() {@code <=>} b.length() } as Comparator )
+ * 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 an Iterable
- * @param comparator a Comparator
- * @return the maximum value or null for an empty Iterable
- * @see #max(Iterator, Comparator)
- * @since 2.2.0
+ * @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> T max(Iterable<T> self, Comparator<? super T> comparator) {
- return max(self.iterator(), comparator);
+ 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);
}
/**
- * Selects the maximum value found from the Iterator using the given comparator.
+ * Transform Iterable elements into Map entries with keys unchanged
+ * and values transformed using the supplied function.
+ * <pre class="groovyTestCase">
+ * 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 an Iterator
- * @param comparator a Comparator
- * @return the maximum value
- * @since 1.5.5
+ * @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> T max(Iterator<T> self, Comparator<? super T> comparator) {
- T answer = null;
- while (self.hasNext()) {
- T value = self.next();
- if (value != null && (answer == null || comparator.compare(value, answer) > 0)) {
- answer = value;
- }
- }
- return answer;
+ public static <K, V> Map<K, V> withCollectedValues(Iterable<K> keys, Function<? super K, V> valueTransform) {
+ return withCollectedValues(keys.iterator(), new LinkedHashMap<>(), valueTransform);
}
/**
- * Returns indices of the collection.
- * <p>
- * Example:
- * <pre class="groovyTestCase">
- * assert 0..2 == [5, 6, 7].indices
- * </pre>
+ * A variant of withCollectedKeys for Iterators.
*
- * @param self a collection
- * @return an index range
- * @since 2.4.0
+ * @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 IntRange getIndices(Collection self) {
- return new IntRange(false, 0, self.size());
+ 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());
}
/**
- * 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 variant of withCollectedKeys for Iterators.
*
- * @param self an Iterator
- * @return the length of the Iterator
- * @since 1.5.5
+ * @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 int size(Iterator self) {
- int count = 0;
- while (self.hasNext()) {
- self.next();
- count++;
- }
- return count;
+ public static <K, V> Map<K, V> withCollectedKeys(Iterator<V> values, Function<? super V, K> keyTransform) {
+ return withCollectedKeys(values, new LinkedHashMap<>(), keyTransform);
}
/**
- * Provide the standard Groovy <code>size()</code> method for <code>Iterable</code>.
+ * 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 items = [1, 2, 3]
- * def iterable = { [ hasNext:{ !items.isEmpty() }, next:{ items.pop() } ] as Iterator } as Iterable
- * assert iterable.size() == 3
+ * 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 an Iterable
- * @return the length of the Iterable
- * @since 2.3.8
+ * @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 int size(Iterable self) {
- return size(self.iterator());
+ 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());
}
/**
- * Check whether an <code>Iterable</code> has elements
+ * Transform Iterable elements into Map entries with values unchanged
+ * and keys transformed using the supplied function.
* <pre class="groovyTestCase">
- * def items = [1]
- * def iterable = { [ hasNext:{ !items.isEmpty() }, next:{ items.pop() } ] as Iterator } as Iterable
- * assert !iterable.isEmpty()
- * iterable.iterator().next()
- * assert iterable.isEmpty()
+ * 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 Iterable
- * @return true if the iterable has no elements, false otherwise
- * @since 2.5.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 boolean isEmpty(Iterable self) {
- return !self.iterator().hasNext();
+ public static <K, V> Map<K, V> withCollectedKeys(Iterable<V> values, Function<? super V, K> keyTransform) {
+ return withCollectedKeys(values.iterator(), new LinkedHashMap<>(), keyTransform);
+ }
+
+ /**
+ * 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.
+ *
+ * @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
+ */
+ @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);
}
/**
- * Support the range subscript operator for a List.
- * <pre class="groovyTestCase">def list = [1, "a", 4.5, true]
- * assert list[1..2] == ["a", 4.5]</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.
*
- * @param self a List
- * @param range a Range indicating the items to get
- * @return a new list instance based on range borders
+ * 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.
*
- * @since 1.0
+ * @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
*/
- public static <T> List<T> getAt(List<T> self, Range range) {
- RangeInfo info = subListBorders(self.size(), range);
-
- List<T> subList = self.subList(info.from, info.to);
- if (info.reverse) {
- subList = reverse(subList);
- }
-
- // trying to guess the concrete list type and create a new instance from it
- List<T> answer = createSimilarList(self, subList.size());
- answer.addAll(subList);
-
- return answer;
+ @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);
}
/**
- * Select a List of items from an eager or lazy List using a Collection to
- * identify the indices to be selected.
- * <pre class="groovyTestCase">def list = [].withDefault { 42 }
- * assert list[1,0,2] == [42, 42, 42]</pre>
+ * 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 a ListWithDefault
- * @param indices a Collection of indices
+ * <pre class="groovyTestCase">def list = ['a', 'b']
+ * list.retainAll { it == 'b' }
+ * assert list == ['b']</pre>
*
- * @return a new eager or lazy list of the values at the given indices
+ * 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
*/
- @SuppressWarnings("unchecked")
- public static <T> List<T> getAt(ListWithDefault<T> self, Collection indices) {
- List<T> answer = ListWithDefault.newInstance(new ArrayList<>(indices.size()), self.isLazyDefaultValues(), self.getInitClosure());
- for (Object value : indices) {
- if (value instanceof Collection) {
- answer.addAll((List<T>) InvokerHelper.invokeMethod(self, "getAt", value));
- } else {
- int idx = normaliseIndex(DefaultTypeTransformation.intUnbox(value), self.size());
- answer.add(self.getAt(idx));
+ 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 answer;
+ return result;
}
/**
- * Support the range subscript operator for an eager or lazy List.
- * <pre class="groovyTestCase">def list = [].withDefault { 42 }
- * assert list[1..2] == [null, 42]</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 a ListWithDefault
- * @param range a Range indicating the items to get
+ * <pre class="groovyTestCase">def map = [a:1, b:2]
+ * map.retainAll { k,v {@code ->} k == 'b' }
+ * assert map == [b:2]</pre>
*
- * @return a new eager or lazy list instance based on range borders
+ * 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> getAt(ListWithDefault<T> self, Range range) {
- RangeInfo info = subListBorders(self.size(), range);
-
- // if a positive index is accessed not initialized so far
- // initialization up to that index takes place
- if (self.size() < info.to) {
- self.get(info.to - 1);
- }
-
- List<T> answer = self.subList(info.from, info.to);
- if (info.reverse) {
- answer = ListWithDefault.newInstance(reverse(answer), self.isLazyDefaultValues(), self.getInitClosure());
- } else {
- // instead of using the SubList backed by the parent list, a new ArrayList instance is used
- answer = ListWithDefault.newInstance(new ArrayList<>(answer), self.isLazyDefaultValues(), self.getInitClosure());
+ 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 answer;
+ return result;
}
/**
- * Support the range subscript operator for an eager or lazy List.
- * <pre class="groovyTestCase">
- * def list = [true, 1, 3.4].withDefault{ 42 }
- * {@code assert list[0..<0] == []}
- * </pre>
+ * Modifies this collection by removing the elements that are matched according
+ * to the specified closure condition.
*
- * @param self a ListWithDefault
- * @param range a Range indicating the items to get
+ * <pre class="groovyTestCase">def list = ['a', 'b']
+ * list.removeAll { it == 'b' }
+ * assert list == ['a']</pre>
*
- * @return a new list instance based on range borders
+ * 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> getAt(ListWithDefault<T> self, EmptyRange range) {
- return ListWithDefault.newInstance(new ArrayList<>(), self.isLazyDefaultValues(), self.getInitClosure());
+ public static <T> boolean removeAll(Collection<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return self.removeIf(new BooleanClosurePredicate<>(condition));
}
/**
- * Support the range subscript operator for a List.
- * <pre class="groovyTestCase">
- * def list = [true, 1, 3.4]
- * {@code assert list[0..<0] == []}
- * </pre>
+ * 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 a List
- * @param range a Range indicating the items to get
- * @return a new list instance based on range borders
+ * <pre class="groovyTestCase">def map = [a:1, b:2]
+ * map.removeAll { k,v {@code ->} k == 'b' }
+ * assert map == [a:1]</pre>
*
- * @since 1.0
+ * 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> getAt(List<T> self, EmptyRange range) {
- return createSimilarList(self, 0);
+ public static <K, V> boolean removeAll(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure condition) {
+ return self.entrySet().removeIf(new BooleanClosureForMapPredicate<>(condition));
}
/**
- * Select a List of items from a List using a Collection to
- * identify the indices to be selected.
- * <pre class="groovyTestCase">def list = [true, 1, 3.4, false]
- * assert list[1,0,2] == [1, true, 3.4]</pre>
+ * 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 a List
- * @param indices a Collection of indices
- * @return a new list of the values at the given indices
- * @since 1.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
*/
@SuppressWarnings("unchecked")
- public static <T> List<T> getAt(List<T> self, Collection indices) {
- List<T> answer = new ArrayList<>(indices.size());
- for (Object value : indices) {
- if (value instanceof Collection) {
- answer.addAll(getAt(self, (Collection) value));
- } else {
- int idx = DefaultTypeTransformation.intUnbox(value);
- answer.add(getAt(self, idx));
- }
- }
- return answer;
+ public static Collection split(Object self, Closure closure) {
+ List accept = new ArrayList();
+ List reject = new ArrayList();
+ return split(closure, accept, reject, InvokerHelper.asIterator(self));
}
/**
- * 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>
+ * 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 map a Map
- * @param keys a Collection of keys
- * @return a new Map containing the given keys
- * @since 1.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 <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));
+ 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;
}
/**
- * 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>
+ * 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 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
+ */
+ @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);
+ }
+
+ /**
+ * 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 map a Map
- * @param keys an array of keys
- * @return a new Map containing the given keys
- * @since 2.1.0
+ * @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> 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;
+ @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);
}
/**
- * Looks up an item in a Map for the given key and returns the corresponding value.
- * If there is no entry for the given key return instead the default value and
- * also add the key and default value to the map.
- * <pre class="groovyTestCase">
- * def map=[:]
- * map.get("a", []) << 5
- * assert map == [a:[5]]
- * </pre>
- * For a method which doesn't mutate the map, consider instead using {@link Map#getOrDefault(Object, Object)}
- * or consider using Groovy's {@link MapWithDefault} often instantiated using {@link #withDefault(Map, Closure)}
- * or with more options {@link #withDefault(Map, boolean, boolean, Closure)}.
+ * 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>
*
- * @param map a Map
- * @param key the key to look up the value
- * @param defaultValue the value to return and add to the map for this key if
- * there is no entry for the given key
- * @return the value of the given key or the default value, added to the map if the
- * key did not exist
- * @since 1.0
+ * @param self the List of items
+ * @return the subsequences from the list
+ * @since 1.7.0
*/
- public static <K, V> V get(Map<K, V> map, K key, V defaultValue) {
- if (!map.containsKey(key)) {
- map.put(key, defaultValue);
- }
- return map.get(key);
+ public static <T> Set<List<T>> subsequences(List<T> self) {
+ return GroovyCollections.subsequences(self);
}
/**
- * Support the subscript operator for a List.
- * <pre class="groovyTestCase">def list = [2, "a", 5.3]
- * assert list[1] == "a"</pre>
+ * 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 a List
- * @param idx an index
- * @return the value at the given index
- * @since 1.0
+ * @param self the Iterable of items
+ * @return the permutations from the list
+ * @since 1.7.0
*/
- public static <T> T getAt(List<T> self, int idx) {
- int size = self.size();
- int i = normaliseIndex(idx, size);
- if (i < size) {
- return self.get(i);
- } else {
- return null;
+ 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;
}
/**
- * Support subscript operator for list access.
+ * 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 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 getAt(List<T> self, Number idx) {
- return getAt(self, idx.intValue());
+ public static <T,V> List<V> permutations(Iterable<T> self, Closure<V> function) {
+ return collect(permutations(self), function);
}
/**
- * Support the subscript operator for an Iterator. The iterator
- * will be partially exhausted up until the idx entry after returning
- * if a +ve or 0 idx is used, or fully exhausted if a -ve idx is used
- * or no corresponding entry was found. Typical usage:
+ * 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 iter = [2, "a", 5.3].iterator()
- * assert iter[1] == "a"
+ * def result = [['a', 'b'], [1, 2]].transpose()
+ * assert result == [['a', 1], ['b', 2]]
* </pre>
- * A more elaborate example:
* <pre class="groovyTestCase">
- * def items = [2, "a", 5.3]
- * def iter = items.iterator()
- * assert iter[-1] == 5.3
- * // iter exhausted, so reset
- * iter = items.iterator()
- * assert iter[1] == "a"
- * // iter partially exhausted so now idx starts after "a"
- * assert iter[0] == 5.3
+ * def result = [['a', 'b'], [1, 2], [3, 4]].transpose()
+ * assert result == [['a', 1, 3], ['b', 2, 4]]
* </pre>
*
- * @param self an Iterator
- * @param idx an index value (-self.size() <= idx < self.size())
- * @return the value at the given index (after normalisation) or null if no corresponding value was found
- * @since 1.7.2
+ * @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 <T> T getAt(Iterator<T> self, int idx) {
- if (idx < 0) {
- // calculate whole list in this case
- // recommend avoiding -ve's as this is not as efficient
- List<T> list = toList(self);
- int adjustedIndex = idx + list.size();
- if (adjustedIndex < 0 || adjustedIndex >= list.size()) return null;
- return list.get(adjustedIndex);
+ 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);
+ }
- int count = 0;
- while (self.hasNext()) {
- if (count == idx) {
- return self.next();
- } else {
- count++;
- self.next();
- }
+ // 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);
+ }
- return null;
+ // 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);
}
/**
- * Support the subscript operator for an Iterable. Typical usage:
+ * Performs the same function as the version of inject that takes an initial value, but
+ * uses the head of the Collection as the initial value, and iterates over the tail.
* <pre class="groovyTestCase">
- * // custom Iterable example:
- * class MyIterable implements Iterable {
- * Iterator iterator() { [1, 2, 3].iterator() }
- * }
- * def myIterable = new MyIterable()
- * assert myIterable[1] == 2
- *
- * // Set example:
- * def set = [1,2,3] as LinkedHashSet
- * assert set[1] == 2
+ * assert 1 * 2 * 3 * 4 == [ 1, 2, 3, 4 ].inject { acc, val {@code ->} acc * val }
+ * assert ['b'] == [['a','b'], ['b','c'], ['d','b']].inject { acc, val {@code ->} acc.intersect( val ) }
+ * LinkedHashSet set = [ 't', 'i', 'm' ]
+ * assert 'tim' == set.inject { a, b {@code ->} a + b }
* </pre>
*
- * @param self an Iterable
- * @param idx an index value (-self.size() <= idx < self.size()) but using -ve index values will be inefficient
- * @return the value at the given index (after normalisation) or null if no corresponding value was found
- * @since 2.1.0
- */
- public static <T> T getAt(Iterable<T> self, int idx) {
- return getAt(self.iterator(), idx);
- }
-
- /**
- * 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
+ * @param self a Collection
+ * @param closure a closure
+ * @return the result of the last closure call
+ * @throws NoSuchElementException if the collection is empty.
+ * @see #inject(Collection, Object, Closure)
+ * @since 1.8.7
*/
- 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);
+ @SuppressWarnings("unchecked")
+ public static <T, V extends T> T inject(Collection<T> self, @ClosureParams(value=FromString.class,options="V,T") Closure<V> closure ) {
+ if( self.isEmpty() ) {
+ throw new NoSuchElementException( "Cannot call inject() on an empty collection without passing an initial value." ) ;
+ }
+ Iterator<T> iter = self.iterator();
+ T head = iter.next();
+ Collection<T> tail = tail(self);
+ if (!tail.iterator().hasNext()) {
+ return head;
}
+ // cast with explicit weaker generics for now to keep jdk6 happy, TODO: find better fix
+ return (T) inject((Collection) tail, head, closure);
}
/**
- * Support subscript operator for list modification.
- */
- public static <T> void putAt(List<T> self, Number idx, T value) {
- putAt(self, idx.intValue(), value);
- }
-
- /**
- * A helper method to allow lists to work with subscript operators.
+ * Iterates through the given Collection, passing in the initial value to
+ * the 2-arg closure along with the first item. The result is passed back (injected) into
+ * the closure along with the second item. The new result is injected back into
+ * the closure along with the third item and so on until the entire collection
+ * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional parlance.
+ *
+ * Examples:
* <pre class="groovyTestCase">
- * def list = ["a", true]
- * {@code list[1..<1] = 5}
- * assert list == ["a", 5, true]
+ * assert 1*1*2*3*4 == [1,2,3,4].inject(1) { acc, val {@code ->} acc * val }
+ *
+ * assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val {@code ->} acc + val }
+ *
+ * assert 'The quick brown fox' ==
+ * ['quick', 'brown', 'fox'].inject('The') { acc, val {@code ->} acc + ' ' + val }
+ *
+ * assert 'bat' ==
+ * ['rat', 'bat', 'cat'].inject('zzz') { min, next {@code ->} next {@code <} min ? next : min }
+ *
+ * def max = { a, b {@code ->} [a, b].max() }
+ * def animals = ['bat', 'rat', 'cat']
+ * assert 'rat' == animals.inject('aaa', max)
+ * </pre>
+ * Visual representation of the last example above:
+ * <pre>
+ * initVal animals[0]
+ * v v
+ * max('aaa', 'bat') {@code =>} 'bat' animals[1]
+ * v v
+ * max('bat', 'rat') {@code =>} 'rat' animals[2]
+ * v v
+ * max('rat', 'cat') {@code =>} 'rat'
* </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 initialValue some initial value
+ * @param closure a closure
+ * @return the result of the last closure call
* @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 <E, T, U extends T, V extends T> T inject(Collection<E> self, U initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> closure) {
+ // cast with explicit weaker generics for now to keep jdk6 happy, TODO: find better fix
+ return (T) inject((Iterator) self.iterator(), initialValue, closure);
}
/**
- * A helper method to allow lists to work with subscript operators.
+ * Iterates through the given Map, passing in the initial value to
+ * the 2-arg Closure along with the first item (or 3-arg Closure along with the first key and value).
+ * The result is passed back (injected) into
+ * the closure along with the second item. The new result is injected back into
+ * the closure along with the third item and so on until the entire collection
+ * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional parlance.
+ *
+ * Examples:
* <pre class="groovyTestCase">
- * def list = ["a", true]
- * {@code list[1..<1] = [4, 3, 2]}
- * assert list == ["a", 4, 3, 2, true]
+ * def map = [a:1, b:2, c:3]
+ * assert map.inject([]) { list, k, v {@code ->}
+ * list + [k] * v
+ * } == ['a', 'b', 'b', 'c', 'c', 'c']
* </pre>
*
- * @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)
+ * @param self a Map
+ * @param initialValue some initial value
+ * @param closure a 2 or 3 arg Closure
+ * @return the result of the last closure call
+ * @since 1.8.1
*/
- 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);
+ public static <K, V, T, U extends T, W extends T> T inject(Map<K, V> self, U initialValue, @ClosureParams(value=FromString.class,options={"U,Map.Entry<K,V>","U,K,V"}) Closure<W> closure) {
+ T value = initialValue;
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ if (closure.getMaximumNumberOfParameters() == 3) {
+ value = closure.call(value, entry.getKey(), entry.getValue());
+ } else {
+ value = closure.call(value, entry);
}
}
- List<T> sublist = self.subList(info.from, info.to);
- sublist.clear();
- return sublist;
+ return value;
}
/**
- * 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>
- *
- * Items in the given
- * range are replaced with items from the collection.
+ * Iterates through the given Iterator, passing in the initial value to
+ * the closure along with the first item. The result is passed back (injected) into
+ * the closure along with the second item. The new result is injected back into
+ * the closure along with the third item and so on until the Iterator has been
+ * expired of values. Also known as foldLeft in functional parlance.
*
- * @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
+ * @param self an Iterator
+ * @param initialValue some initial value
+ * @param closure a closure
+ * @return the result of the last closure call
+ * @see #inject(Collection, Object, Closure)
* @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>
- *
- * 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
- */
- @SuppressWarnings("unchecked")
- public static void putAt(List self, IntRange range, Object value) {
- List sublist = resizeListWithRangeAndGetSublist(self, range);
- sublist.add(value);
+ public static <E,T, U extends T, V extends T> T inject(Iterator<E> self, U initialValue, @ClosureParams(value=FromString.class,options="U,E") Closure<V> closure) {
+ T value = initialValue;
+ Object[] params = new Object[2];
+ while (self.hasNext()) {
+ Object item = self.next();
+ params[0] = value;
+ params[1] = item;
+ value = closure.call(params);
+ }
+ return value;
}
/**
- * 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>
+ * Iterates through the given Object, passing in the first value to
+ * the closure along with the first item. The result is passed back (injected) into
+ * the closure along with the second item. The new result is injected back into
+ * the closure along with the third item and so on until further iteration of
+ * the object is not possible. Also known as foldLeft in functional parlance.
*
- * @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
+ * @param self an Object
+ * @param closure a closure
+ * @return the result of the last closure call
+ * @throws NoSuchElementException if the collection is empty.
+ * @see #inject(Collection, Object, Closure)
+ * @since 1.8.7
*/
@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, V extends T> T inject(Object self, Closure<V> closure) {
+ Iterator iter = InvokerHelper.asIterator(self);
+ if( !iter.hasNext() ) {
+ throw new NoSuchElementException( "Cannot call inject() over an empty iterable without passing an initial value." ) ;
}
+ Object initialValue = iter.next() ;
+ return (T) inject(iter, initialValue, closure);
}
/**
- * @deprecated use #putAt(List, List, Collection), retained for binary compatibility
+ * Iterates through the given Object, passing in the initial value to
+ * the closure along with the first item. The result is passed back (injected) into
+ * the closure along with the second item. The new result is injected back into
+ * the closure along with the third item and so on until further iteration of
+ * the object is not possible. Also known as foldLeft in functional parlance.
+ *
+ * @param self an Object
+ * @param initialValue some initial value
+ * @param closure a closure
+ * @return the result of the last closure call
+ * @see #inject(Collection, Object, Closure)
+ * @since 1.5.0
*/
- @Deprecated
@SuppressWarnings("unchecked")
- public static void putAt(List self, List splice, List values) {
- putAt(self, splice, (Collection) values);
+ public static <T, U extends T, V extends T> T inject(Object self, U initialValue, Closure<V> closure) {
+ Iterator iter = InvokerHelper.asIterator(self);
+ return (T) inject(iter, initialValue, closure);
}
/**
- * 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>
+ * 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 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);
- }
- } else {
- throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName());
- }
+ * @param self Iterable of values to add together
+ * @return The sum of all the items
+ * @see #sum(Iterator)
+ * @since 2.2.0
+ */
+ public static Object sum(Iterable<?> self) {
+ return sum(self.iterator(), null, true);
}
/**
- * Support the subscript operator for a Map.
- * <pre class="groovyTestCase">def map = [1:10]
- * assert map[1] == 10</pre>
+ * 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 a Map
- * @param key an Object as a key for the map
- * @return the value corresponding to the given key
- * @since 1.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> V getAt(Map<K,V> self, Object key) {
- return self.get(key);
+ public static Object sum(Iterator<Object> self) {
+ return sum(self, null, true);
}
/**
- * 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.
+ * Sums the items in an Iterable, adding the result to some initial value.
* <pre class="groovyTestCase">
- * assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]
+ * assert 5+1+2+3+4 == [1,2,3,4].sum(5)
* </pre>
*
- * @param left a Map
- * @param right a Map
- * @return a new Map containing all entries from left and right
- * @since 1.5.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 <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;
+ public static Object sum(Iterable<?> self, Object initialValue) {
+ return sum(self.iterator(), initialValue, false);
}
/**
- * A helper method to allow maps to work with subscript operators
+ * 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 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
+ * @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 <K,V> V putAt(Map<K,V> self, K key, V value) {
- self.put(key, value);
- return value;
+ 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 result;
}
/**
- * Support the subscript operator for Collection.
- * <pre class="groovyTestCase">
- * assert [String, Long, Integer] == ["a",5L,2]["class"]
- * </pre>
+ * 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 coll a Collection
- * @param property a String
- * @return a List
- * @since 1.0
+ * @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 List getAt(Collection coll, String property) {
- List<Object> answer = new ArrayList<>(coll.size());
- return getAtIterable(coll, property, answer);
+ public static <T> Object sum(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self.iterator(), null, closure, true);
}
- private static List getAtIterable(Iterable coll, String property, List<Object> answer) {
- for (Object item : coll) {
- if (item == null) continue;
- Object value;
- try {
- value = InvokerHelper.getProperty(item, property);
- } catch (MissingPropertyExceptionNoStack mpe) {
- String causeString = new MissingPropertyException(mpe.getProperty(), mpe.getType()).toString();
- throw new MissingPropertyException("Exception evaluating property '" + property +
- "' for " + coll.getClass().getName() + ", Reason: " + causeString);
- }
- answer.add(value);
- }
- return answer;
+ /**
+ * 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 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 <T> Object sum(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self, null, closure, true);
}
/**
- * Synonym for {@link #toSpreadMap(java.util.Map)}.
+ * 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 map
- * @return a newly created SpreadMap
- * @since 1.0
+ * @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 SpreadMap spread(Map self) {
- return toSpreadMap(self);
+ public static <T> Object sum(Iterable<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self.iterator(), initialValue, closure, false);
}
/**
- * 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 }
- *
- * 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.
+ * 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 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
+ * @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 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);
+ 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;
}
/**
- * 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
+ * Concatenates the <code>toString()</code> representation of each item from
+ * the Iterator, with the given String as a separator between each item. The
+ * iterator will become exhausted of elements after producing the resulting
+ * conjoined value.
+ *
+ * @param self an Iterator of objects
+ * @param separator a String separator
+ * @return the joined String
+ * @since 1.5.5
*/
- 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);
+ public static String join(final Iterator<?> self, final String separator) {
+ StringJoiner joiner = new StringJoiner(separator != null ? separator : "");
+ while (self.hasNext()) {
+ Object obj = self.next();
+ String str = FormatHelper.toString(obj);
+ joiner.add(str);
+ }
+ return joiner.toString();
}
/**
- * Creates a spreadable map from this iterable.
+ * Concatenates the <code>toString()</code> representation of each item from
+ * the Iterable, with the given String as a separator between each item.
* <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));
+ * <pre class="groovyTestCase">assert [1,2,3].join() == "123"</pre>
+ * <pre class="groovyTestCase">assert [1,2,3].join(", ") == "1, 2, 3"</pre>
+ *
+ * @param self an Iterable of objects
+ * @param separator a String separator
+ * @return the joined String
+ * @since 1.0
+ */
+ public static String join(final Iterable<?> self, final String separator) {
+ return join(self.iterator(), separator);
}
/**
- * 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 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>
+ * Adds min() method to Collection objects.
+ * <pre class="groovyTestCase">assert 2 == [4,2,5].min()</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 a Collection
+ * @return the minimum value
+ * @see #min(Iterator)
+ * @since 1.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> T min(Iterable<T> self) {
+ return min(self.iterator());
}
/**
- * 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>
+ * Adds min() method to Iterator objects. The iterator will become
+ * exhausted of elements after determining the minimum value.
*
- * @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
+ * @return the minimum value
+ * @see #min(Iterable)
+ * @since 1.5.5
*/
- 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> T min(Iterator<T> self) {
+ T answer = null;
+ while (self.hasNext()) {
+ T value = self.next();
+ if (value != null && (answer == null || ScriptBytecodeAdapter.compareLessThan(value, answer))) {
+ answer = value;
+ }
+ }
+ return answer;
}
/**
- * An alias for <code>withLazyDefault</code> which decorates a list allowing
- * it to grow when called with index values outside the normal list bounds.
+ * Selects the minimum value found in the Iterable using the given comparator.
+ * <pre class="groovyTestCase">assert "hi" == ["hello","hi","hey"].min( { a, b {@code ->} a.length() {@code <=>} b.length() } as Comparator )</pre>
*
- * @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 Iterable
+ * @param comparator a Comparator
+ * @return the minimum value or null for an empty Iterable
+ * @see #min(Iterator, java.util.Comparator)
+ * @since 2.2.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> T min(Iterable<T> self, Comparator<? super T> comparator) {
+ return min(self.iterator(), comparator);
}
/**
- * 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 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>
+ * Selects the minimum value found from the Iterator using the given comparator.
*
- * @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 Iterator
+ * @param comparator a Comparator
+ * @return the minimum value
+ * @since 1.5.5
*/
- 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> T min(Iterator<T> self, Comparator<? super T> comparator) {
+ Objects.requireNonNull(self);
+ T answer = null;
+ boolean first = true;
+ while (self.hasNext()) {
+ T value = self.next();
+ if (first) {
+ first = false;
+ answer = value;
+ } else if (comparator.compare(value, answer) < 0) {
+ answer = value;
+ }
+ }
+ return answer;
}
/**
- * 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>.
+ * Selects the item in the iterable which when passed as a parameter to the supplied closure returns the
+ * minimum value. A null return value represents the least possible return value. If more than one item
+ * has the minimum value, an arbitrary choice is made between the items having the minimum value.
* <p>
- * Example usage:
+ * 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">
- * 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
- *
- * // 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]
+ * assert "hi" == ["hello","hi","hey"].min { it.length() }
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def lastDigit = { a, b {@code ->} a % 10 {@code <=>} b % 10 }
+ * assert [19, 55, 91].min(lastDigit) == 91
+ * </pre>
+ * <pre class="groovyTestCase">
+ * def pets = ['dog', 'cat', 'anaconda']
+ * def shortestName = pets.min{ it.size() } // one of 'dog' or 'cat'
+ * assert shortestName.size() == 3
* </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 an Iterable
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return an item from the Iterable having the minimum value returned by calling the supplied closure with that item as parameter or null for an empty Iterable
+ * @see #min(Iterator)
+ * @since 1.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> T min(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return min(self.iterator(), closure);
}
/**
- * Zips an Iterable with indices in (value, index) order.
- * <p/>
- * Example usage:
+ * Selects an entry in the map having the minimum
+ * calculated value as determined by the supplied closure.
+ * If more than one entry has the minimum value,
+ * an arbitrary choice is made between the entries having the minimum value.
+ * <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">
+ * def zoo = [monkeys:6, lions:5, tigers:7]
+ * def leastCommonEntry = zoo.min{ it.value }
+ * assert leastCommonEntry.value == 5
+ * def mostCommonEntry = zoo.min{ a, b {@code ->} b.value {@code <=>} a.value } // double negative!
+ * assert mostCommonEntry.value == 7
+ * </pre>
+ * Edge case for multiple min values:
* <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" }
+ * def zoo = [monkeys:6, lions:5, tigers:7]
+ * def lastCharOfName = { e {@code ->} e.key[-1] }
+ * def ans = zoo.min(lastCharOfName) // some random entry
+ * assert lastCharOfName(ans) == 's'
* </pre>
*
- * @param self an Iterable
- * @return a zipped list with indices
- * @see #indexed(Iterable)
- * @since 2.4.0
+ * @param self a Map
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return the Map.Entry having the minimum value as determined by the closure
+ * @since 1.7.6
*/
- public static <E> List<Tuple2<E, Integer>> withIndex(Iterable<E> self) {
- return withIndex(self, 0);
+ public static <K, V> Map.Entry<K, V> min(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>", "Map.Entry<K,V>,Map.Entry<K,V>"}) Closure closure) {
+ return min(self.entrySet(), closure);
}
/**
- * Zips an Iterable with indices in (index, value) order.
- * <p/>
- * Example usage:
+ * Selects an entry in the map having the maximum
+ * calculated value as determined by the supplied closure.
+ * If more than one entry has the maximum value,
+ * an arbitrary choice is made between the entries having the maximum value.
+ * <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. An example:
* <pre class="groovyTestCase">
- * assert [0: "a", 1: "b"] == ["a", "b"].indexed()
- * assert ["0: a", "1: b"] == ["a", "b"].indexed().collect { idx, str {@code ->} "$idx: $str" }
+ * def zoo = [monkeys:6, lions:5, tigers:7]
+ * def mostCommonEntry = zoo.max{ it.value }
+ * assert mostCommonEntry.value == 7
+ * def leastCommonEntry = zoo.max{ a, b {@code ->} b.value {@code <=>} a.value } // double negative!
+ * assert leastCommonEntry.value == 5
+ * </pre>
+ * Edge case for multiple max values:
+ * <pre class="groovyTestCase">
+ * def zoo = [monkeys:6, lions:5, tigers:7]
+ * def lengthOfNamePlusNumber = { e {@code ->} e.key.size() + e.value }
+ * def ans = zoo.max(lengthOfNamePlusNumber) // one of [monkeys:6, tigers:7]
+ * assert lengthOfNamePlusNumber(ans) == 13
* </pre>
*
- * @param self an Iterable
- * @return a zipped map with indices
- * @see #withIndex(Iterable)
- * @since 2.4.0
+ * @param self a Map
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return the Map.Entry having the maximum value as determined by the closure
+ * @since 1.7.6
*/
- public static <E> Map<Integer, E> indexed(Iterable<E> self) {
- return indexed(self, 0);
+ public static <K, V> Map.Entry<K, V> max(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>", "Map.Entry<K,V>,Map.Entry<K,V>"}) Closure closure) {
+ return max(self.entrySet(), closure);
}
/**
- * Zips an Iterable with indices in (value, index) order.
- * <p/>
- * Example usage:
- * <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" }
- * </pre>
+ * Selects the minimum value found from the Iterator
+ * using the closure to determine the correct ordering.
+ * The iterator will become
+ * exhausted of elements after this operation.
+ * <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 an Iterable
- * @param offset an index to start from
- * @return a zipped list with indices
- * @see #indexed(Iterable, int)
- * @since 2.4.0
+ * @param self an Iterator
+ * @param closure a Closure used to determine the correct ordering
+ * @return the minimum value
+ * @since 1.5.5
*/
- public static <E> List<Tuple2<E, Integer>> withIndex(Iterable<E> self, int offset) {
- return toList(withIndex(self.iterator(), offset));
+ public static <T> T min(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ int params = closure.getMaximumNumberOfParameters();
+ if (params != 1) {
+ return min(self, new ClosureComparator<>(closure));
+ }
+ Objects.requireNonNull(self);
+ boolean first = true;
+ T answer = null;
+ Object answerValue = null;
+ while (self.hasNext()) {
+ T item = self.next();
+ Object value = closure.call(item);
+ if (first) {
+ first = false;
+ answer = item;
+ answerValue = value;
+ } else if (ScriptBytecodeAdapter.compareLessThan(value, answerValue)) {
+ answer = item;
+ answerValue = value;
+ }
+ }
+ return answer;
}
/**
- * Zips an Iterable with indices in (index, value) order.
- * <p/>
- * Example usage:
+ * Adds max() method to Iterable objects.
* <pre class="groovyTestCase">
- * assert [5: "a", 6: "b"] == ["a", "b"].indexed(5)
- * assert ["1: a", "2: b"] == ["a", "b"].indexed(1).collect { idx, str {@code ->} "$idx: $str" }
+ * assert 5 == [2,3,1,5,4].max()
* </pre>
*
- * @param self an Iterable
- * @param offset an index to start from
- * @return a Map (since the keys/indices are unique) containing the elements from the iterable zipped with indices
- * @see #withIndex(Iterable, int)
- * @since 2.4.0
+ * @param self an Iterable
+ * @return the maximum value
+ * @see #max(Iterator)
+ * @since 2.2.0
*/
- public static <E> Map<Integer, E> indexed(Iterable<E> self, int offset) {
- Objects.requireNonNull(self);
- Map<Integer, E> result = new LinkedHashMap<>();
- Iterator<Tuple2<Integer, E>> indexed = indexed(self.iterator(), offset);
- while (indexed.hasNext()) {
- Tuple2<Integer, E> next = indexed.next();
- result.put(next.getV1(), next.getV2());
- }
- return result;
+ public static <T> T max(Iterable<T> self) {
+ return max(self.iterator());
}
/**
- * Zips an iterator with indices in (value, index) order.
- * <p/>
- * Example usage:
- * <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()
- * </pre>
+ * Adds max() method to Iterator objects. The iterator will become
+ * exhausted of elements after determining the maximum value.
*
- * @param self an iterator
- * @return a zipped iterator with indices
- * @see #indexed(Iterator)
- * @since 2.4.0
+ * @param self an Iterator
+ * @return the maximum value
+ * @since 1.5.5
*/
- public static <E> Iterator<Tuple2<E, Integer>> withIndex(Iterator<E> self) {
- return withIndex(self, 0);
+ public static <T> T max(Iterator<T> self) {
+ T answer = null;
+ while (self.hasNext()) {
+ T value = self.next();
+ if (value != null && (answer == null || ScriptBytecodeAdapter.compareGreaterThan(value, answer))) {
+ answer = value;
+ }
+ }
+ return answer;
}
/**
- * Zips an iterator with indices in (index, value) order.
- * <p/>
- * Example usage:
+ * Selects the item in the iterable which when passed as a parameter to the supplied closure returns the
+ * maximum value. A null return value represents the least possible return value, so any item for which
+ * the supplied closure returns null, won't be selected (unless all items return null). If more than one item
+ * has the maximum value, an arbitrary choice is made between the items having the maximum value.
+ * <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 "hello" == ["hello","hi","hey"].max { it.length() }</pre>
+ * <pre class="groovyTestCase">assert "hello" == ["hello","hi","hey"].max { a, b {@code ->} a.length() {@code <=>} b.length() }</pre>
* <pre class="groovyTestCase">
- * assert [[0, "a"], [1, "b"]] == ["a", "b"].iterator().indexed().collect{ tuple {@code ->} [tuple.first, tuple.second] }
- * assert ["0: a", "1: b"] == ["a", "b"].iterator().indexed().collect { idx, str {@code ->} "$idx: $str" }.toList()
+ * def pets = ['dog', 'elephant', 'anaconda']
+ * def longestName = pets.max{ it.size() } // one of 'elephant' or 'anaconda'
+ * assert longestName.size() == 8
* </pre>
*
- * @param self an iterator
- * @return a zipped iterator with indices
- * @see #withIndex(Iterator)
- * @since 2.4.0
+ * @param self an Iterable
+ * @param closure a 1 or 2 arg Closure used to determine the correct ordering
+ * @return an item from the Iterable having the maximum value returned by calling the supplied closure with that item as parameter or null for an empty Iterable
+ * @since 2.2.0
*/
- public static <E> Iterator<Tuple2<Integer, E>> indexed(Iterator<E> self) {
- return indexed(self, 0);
+ public static <T> T max(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ return max(self.iterator(), closure);
}
/**
- * 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>
+ * Selects the maximum value found from the Iterator
+ * using the closure to determine the correct ordering.
+ * The iterator will become exhausted of elements after this operation.
+ * <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 an iterator
- * @param offset an index to start from
- * @return a zipped iterator with indices
- * @see #indexed(Iterator, int)
- * @since 2.4.0
+ * @param self an Iterator
+ * @param closure a Closure used to determine the correct ordering
+ * @return the maximum value
+ * @since 1.5.5
*/
- public static <E> Iterator<Tuple2<E, Integer>> withIndex(Iterator<E> self, int offset) {
- return new ZipPostIterator<>(self, offset);
+ public static <T> T max(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+ int params = closure.getMaximumNumberOfParameters();
+ if (params != 1) {
+ return max(self, new ClosureComparator<>(closure));
+ }
+ boolean first = true;
+ T answer = null;
+ Object answerValue = null;
+ while (self.hasNext()) {
+ T item = self.next();
+ Object value = closure.call(item);
+ if (first) {
+ first = false;
+ answer = item;
+ answerValue = value;
+ } else if (ScriptBytecodeAdapter.compareLessThan(answerValue, value)) {
+ answer = item;
+ answerValue = value;
+ }
+ }
+ return answer;
}
/**
- * Zips an iterator with indices in (index, value) order.
- * <p/>
- * Example usage:
+ * Selects the maximum value found in the Iterable using the given comparator.
* <pre class="groovyTestCase">
- * assert [[5, "a"], [6, "b"]] == ["a", "b"].iterator().indexed(5).toList()
- * assert ["a: 1", "b: 2"] == ["a", "b"].iterator().indexed(1).collect { idx, str {@code ->} "$str: $idx" }.toList()
+ * assert "hello" == ["hello","hi","hey"].max( { a, b {@code ->} a.length() {@code <=>} b.length() } as Comparator )
* </pre>
*
- * @param self an iterator
- * @param offset an index to start from
- * @return a zipped iterator with indices
- * @see #withIndex(Iterator, int)
- * @since 2.4.0
+ * @param self an Iterable
+ * @param comparator a Comparator
+ * @return the maximum value or null for an empty Iterable
+ * @see #max(Iterator, Comparator)
+ * @since 2.2.0
*/
- public static <E> Iterator<Tuple2<Integer, E>> indexed(Iterator<E> self, int offset) {
- return new ZipPreIterator<>(self, offset);
+ public static <T> T max(Iterable<T> self, Comparator<? super T> comparator) {
+ return max(self.iterator(), comparator);
}
- 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();
+ /**
+ * Selects the maximum value found from the Iterator using the given comparator.
+ *
+ * @param self an Iterator
+ * @param comparator a Comparator
+ * @return the maximum value
+ * @since 1.5.5
+ */
+ public static <T> T max(Iterator<T> self, Comparator<? super T> comparator) {
+ T answer = null;
+ while (self.hasNext()) {
+ T value = self.next();
+ if (value != null && (answer == null || comparator.compare(value, answer) > 0)) {
+ answer = value;
+ }
}
+ return answer;
}
- private static final class ZipPreIterator<E> implements Iterator<Tuple2<Integer, E>> {
- private final Iterator<E> delegate;
- private int index;
-
- private ZipPreIterator(Iterator<E> delegate, int offset) {
- this.delegate = delegate;
- this.index = offset;
- }
-
- @Override
- public boolean hasNext() {
- return delegate.hasNext();
- }
-
- @Override
- public Tuple2<Integer, E> next() {
- if (!hasNext()) throw new NoSuchElementException();
- return new Tuple2<>(index++, delegate.next());
- }
-
- @Override
- public void remove() {
- delegate.remove();
+ /**
+ * 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 Iterator
+ * @return the length of the Iterator
+ * @since 1.5.5
+ */
+ public static int size(Iterator self) {
+ int count = 0;
+ while (self.hasNext()) {
+ self.next();
+ count++;
}
+ return count;
}
/**
- * 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>
+ * Provide the standard Groovy <code>size()</code> method for <code>Iterable</code>.
+ * <pre class="groovyTestCase">
+ * def items = [1, 2, 3]
+ * def iterable = { [ hasNext:{ !items.isEmpty() }, next:{ items.pop() } ] as Iterator } as Iterable
+ * assert iterable.size() == 3
+ * </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 an Iterable
+ * @return the length of the Iterable
+ * @since 2.3.8
*/
- public static <T> List<T> sort(Iterable<T> self) {
- return sort(self, true);
+ public static int size(Iterable self) {
+ return size(self.iterator());
}
/**
- * 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>
+ * Check whether an <code>Iterable</code> has elements
* <pre class="groovyTestCase">
- * def orig = [1, 3, 2]
- * def sorted = orig.sort(false)
- * assert orig == [1, 3, 2]
- * assert sorted == [1, 2, 3]
+ * def items = [1]
+ * def iterable = { [ hasNext:{ !items.isEmpty() }, next:{ items.pop() } ] as Iterator } as Iterable
+ * assert !iterable.isEmpty()
+ * iterable.iterator().next()
+ * assert iterable.isEmpty()
* </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 an Iterable
+ * @return true if the iterable has no elements, false otherwise
+ * @since 2.5.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;
+ public static boolean isEmpty(Iterable self) {
+ return !self.iterator().hasNext();
}
/**
- * 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>
+ * 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 unsorted map
- * @param closure a Closure used as a comparator
- * @return the sorted map
- * @since 1.6.0
+ * @param map a Map
+ * @param keys a Collection of keys
+ * @return a new Map containing the given keys
+ * @since 1.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 <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 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>
+ * 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 self the original unsorted map
- * @param comparator a Comparator
- * @return the sorted map
- * @since 1.7.2
+ * @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> sort(Map<K, V> self, Comparator<? super K> comparator) {
- Map<K, V> result = new TreeMap<>(comparator);
- result.putAll(self);
- return result;
+ 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;
}
/**
- * 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>
+ * 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 the original unsorted map
- * @return the sorted map
- * @since 1.7.2
+ * @param self a List
+ * @param idx an index
+ * @param value the value to put at the given index
+ * @since 1.0
*/
- public static <K, V> Map<K, V> sort(Map<K, V> self) {
- return new TreeMap<>(self);
+ 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);
+ }
}
/**
- * 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
+ * Support subscript operator for list modification.
*/
- public static <T> Iterator<T> sort(Iterator<T> self) {
- return sort((Iterable<T>) toList(self)).listIterator();
+ public static <T> void putAt(List<T> self, Number idx, T value) {
+ putAt(self, idx.intValue(), value);
}
/**
- * 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.
+ * A helper method to allow lists to work with subscript operators.
+ * <pre class="groovyTestCase">
+ * def list = ["a", true]
+ * {@code list[1..<1] = 5}
+ * assert list == ["a", 5, true]
+ * </pre>
*
- * @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 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 <T> Iterator<T> sort(Iterator<T> self, Comparator<? super T> comparator) {
- return sort(toList(self), true, comparator).listIterator();
+ @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);
+ }
}
/**
- * 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>
+ * A helper method to allow lists to work with subscript operators.
* <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 list = ["a", true]
+ * {@code list[1..<1] = [4, 3, 2]}
+ * assert list == ["a", 4, 3, 2, true]
* </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
- * @param comparator a Comparator used for the comparison
- * @return a sorted List
- * @since 2.2.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 <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 void putAt(List self, EmptyRange range, Collection value) {
+ putAt(self, range, (Object)value);
}
- /**
- * 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 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 <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();
+ 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;
}
/**
- * 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>
+ * 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 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
+ * 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 <T> List<T> sort(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
- return sort(self, true, closure);
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, IntRange range, Collection col) {
+ List sublist = resizeListWithRangeAndGetSublist(self, range);
+ if (col.isEmpty()) return;
+ sublist.addAll(col);
}
/**
- * 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 orig = ["hello","hi","Hey"]
- * def sorted = orig.sort(false) { it.toUpperCase() }
- * assert orig == ["hello","hi","Hey"]
- * assert sorted == ["hello","Hey","hi"]
- * </pre>
+ * 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 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
+ * 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 <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;
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, IntRange range, Object value) {
+ List sublist = resizeListWithRangeAndGetSublist(self, range);
+ sublist.add(value);
}
/**
- * Avoids doing unnecessary work when sorting an already sorted set (i.e. an identity function for an already sorted set).
+ * 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 self an already sorted set
- * @return the set
+ * @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 <T> SortedSet<T> sort(SortedSet<T> self) {
- return self;
+ @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());
+ }
}
/**
- * Avoids doing unnecessary work when sorting an already sorted map (i.e. an identity function for an already sorted map).
- *
- * @param self an already sorted map
- * @return the map
- * @since 1.8.1
+ * @deprecated use #putAt(List, List, Collection), retained for binary compatibility
*/
- public static <K, V> SortedMap<K, V> sort(SortedMap<K, V> self) {
- return self;
+ @Deprecated
+ @SuppressWarnings("unchecked")
+ public static void putAt(List self, List splice, List values) {
+ putAt(self, splice, (Collection) values);
}
/**
- * 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>
+ * 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 the Iterable to be sorted
- * @return the sorted iterable as a List
- * @see #toSorted(Iterable, Comparator)
- * @since 2.4.0
+ * @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
*/
- public static <T> List<T> toSorted(Iterable<T> self) {
- return toSorted(self, new NumberAwareComparator<>());
+ @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);
+ }
+ } else {
+ throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName());
+ }
}
/**
- * 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.
+ * 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">
- * def orig = ["hello","hi","Hey"]
- * def sorted = orig.toSorted(String.CASE_INSENSITIVE_ORDER)
- * assert orig == ["hello","hi","Hey"]
- * assert sorted == ["hello","Hey","hi"]
+ * assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]
* </pre>
*
- * @param self the Iterable to be sorted
- * @param comparator a Comparator used for the comparison
- * @return a sorted List
- * @since 2.4.0
+ * @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 <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> Map<K, V> plus(Map<K, V> left, Map<K, V> right) {
+ Map<K, V> map = cloneSimilarMap(left);
+ map.putAll(right);
+ return map;
}
/**
- * 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>
+ * A helper method to allow maps to work with subscript operators
*
- * @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
+ * @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 <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 <K,V> V putAt(Map<K,V> self, K key, V value) {
+ self.put(key, value);
+ return value;
}
/**
- * 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.
+ * Synonym for {@link #toSpreadMap(java.util.Map)}.
*
- * @param self the Iterator to be sorted
- * @return the sorted items as an Iterator
- * @see #toSorted(Iterator, Comparator)
- * @since 2.4.0
+ * @param self a map
+ * @return a newly created SpreadMap
+ * @since 1.0
*/
- public static <T> Iterator<T> toSorted(Iterator<T> self) {
- return toSorted(self, new NumberAwareComparator<>());
+ public static SpreadMap spread(Map self) {
+ return toSpreadMap(self);
}
/**
- * 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.
+ * 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 }
*
- * @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
+ * 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 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> Iterator<T> toSorted(Iterator<T> self, Comparator<? super T> comparator) {
- return toSorted(toList(self), comparator).listIterator();
+ 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);
}
/**
- * 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.
+ * Creates a spreadable map from this list.
* <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 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 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 <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 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);
}
/**
- * 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 self the original unsorted map
- * @return the sorted map
+ * 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
*/
- public static <K, V> Map<K, V> toSorted(Map<K, V> self) {
- return toSorted(self, new NumberAwareValueComparator<>());
+ @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));
}
/**
- * 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.
+ * 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 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]'
+ * 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 original unsorted map
- * @param comparator a Comparator used for the comparison
- * @return the sorted map
- * @since 2.4.0
+ * @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 <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;
+ 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);
}
/**
- * 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.
+ * 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 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]'
+ * // 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>
*
- * @param self the original unsorted map
- * @param condition a Closure used as a comparator
- * @return the sorted map
- * @since 2.4.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);
- }
-
- /**
- * Avoids doing unnecessary work when sorting an already sorted set
- *
- * @param self an already sorted set
- * @return an ordered copy of the sorted set
- * @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> Set<T> toSorted(SortedSet<T> self) {
- return new LinkedHashSet<>(self);
+ 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);
}
/**
- * Avoids doing unnecessary work when sorting an already sorted map
+ * 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 an already sorted map
- * @return an ordered copy of the map
- * @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 <K, V> Map<K, V> toSorted(SortedMap<K, V> self) {
- return new LinkedHashMap<>(self);
+ public static <T> ListWithDefault<T> withDefault(List<T> self, @ClosureParams(value=SimpleType.class, options = "int") Closure<T> init) {
+ return withLazyDefault(self, init);
}
/**
- * Removes the initial item from the 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. 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
... 7652 lines suppressed ...