Skip to content

Commit c45c74e

Browse files
committed
GROOVY-11891: Groovy could provide DGM/ADM isSorted variants
1 parent 5b65cd7 commit c45c74e

2 files changed

Lines changed: 230 additions & 0 deletions

File tree

src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5122,6 +5122,66 @@ public static <E, T, U extends T, V extends T> List<T> injectAll(E[] self, U ini
51225122
return result;
51235123
}
51245124

5125+
//--------------------------------------------------------------------------
5126+
// isSorted
5127+
5128+
/**
5129+
* Determines if the array is sorted in natural order using a {@link NumberAwareComparator}.
5130+
* <pre class="groovyTestCase">
5131+
* Integer[] nums = [1, 2, 3]
5132+
* assert nums.isSorted()
5133+
* Integer[] unsorted = [3, 1, 2]
5134+
* assert !unsorted.isSorted()
5135+
* </pre>
5136+
*
5137+
* @param self the array to check
5138+
* @return true if the array elements are sorted in non-descending order
5139+
* @see #isSorted(Object[], Comparator)
5140+
* @since 6.0.0
5141+
*/
5142+
public static <T> boolean isSorted(T[] self) {
5143+
return isSorted(self, new NumberAwareComparator<>());
5144+
}
5145+
5146+
/**
5147+
* Determines if the array is sorted according to the given Comparator.
5148+
* This is efficient — it checks adjacent pairs in a single pass and
5149+
* short-circuits on the first out-of-order pair.
5150+
* <pre class="groovyTestCase">
5151+
* String[] words = ["hello","Hey","hi"]
5152+
* assert words.isSorted(String.CASE_INSENSITIVE_ORDER)
5153+
* </pre>
5154+
*
5155+
* @param self the array to check
5156+
* @param comparator a Comparator used for the comparison
5157+
* @return true if the array elements are sorted according to the comparator
5158+
* @since 6.0.0
5159+
*/
5160+
public static <T> boolean isSorted(T[] self, Comparator<? super T> comparator) {
5161+
for (int i = 1; i < self.length; i++) {
5162+
if (comparator.compare(self[i - 1], self[i]) > 0) return false;
5163+
}
5164+
return true;
5165+
}
5166+
5167+
/**
5168+
* Determines if the array is sorted using the given Closure to determine order.
5169+
* <p>
5170+
* If the Closure has two parameters it is used like a traditional Comparator.
5171+
* Otherwise, the Closure is assumed to take a single parameter and return a
5172+
* Comparable (typically an Integer) which is then used for further comparison.
5173+
*
5174+
* @param self the array to check
5175+
* @param closure a 1 or 2 arg Closure used to determine the ordering
5176+
* @return true if the array elements are sorted according to the closure
5177+
* @see #isSorted(Object[], Comparator)
5178+
* @since 6.0.0
5179+
*/
5180+
public static <T> boolean isSorted(T[] self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure<?> closure) {
5181+
Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(closure);
5182+
return isSorted(self, comparator);
5183+
}
5184+
51255185
//--------------------------------------------------------------------------
51265186
// iterator
51275187

src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9657,6 +9657,176 @@ public static boolean isNotCase(Map<?, ?> caseValue, Object switchValue) {
96579657
return !isCase(caseValue, switchValue);
96589658
}
96599659

9660+
//--------------------------------------------------------------------------
9661+
// isSorted
9662+
9663+
/**
9664+
* Determines if the Iterable is sorted. Assumes the elements are
9665+
* comparable and uses a {@link NumberAwareComparator} to determine the order.
9666+
* <pre class="groovyTestCase">
9667+
* assert [1, 2, 3].isSorted()
9668+
* assert !([3, 1, 2].isSorted())
9669+
* assert [].isSorted()
9670+
* </pre>
9671+
*
9672+
* @param self the Iterable to check
9673+
* @return true if the elements are sorted in non-descending order
9674+
* @see #isSorted(Iterable, Comparator)
9675+
* @since 6.0.0
9676+
*/
9677+
public static <T> boolean isSorted(Iterable<T> self) {
9678+
return isSorted(self, new NumberAwareComparator<>());
9679+
}
9680+
9681+
/**
9682+
* Determines if the Iterable is sorted according to the given Comparator.
9683+
* This is efficient — it checks adjacent pairs in a single pass and
9684+
* short-circuits on the first out-of-order pair.
9685+
* {@code SortedSet} instances always return {@code true}.
9686+
* <pre class="groovyTestCase">
9687+
* assert ["hello","Hey","hi"].isSorted(String.CASE_INSENSITIVE_ORDER)
9688+
* assert !(["hi","Hey","hello"].isSorted(String.CASE_INSENSITIVE_ORDER))
9689+
* assert (new TreeSet(["c","a","b"])).isSorted()
9690+
* </pre>
9691+
*
9692+
* @param self the Iterable to check
9693+
* @param comparator a Comparator used for the comparison
9694+
* @return true if the elements are sorted according to the comparator
9695+
* @since 6.0.0
9696+
*/
9697+
public static <T> boolean isSorted(Iterable<T> self, Comparator<? super T> comparator) {
9698+
if (self instanceof SortedSet) return true;
9699+
return isSorted(self.iterator(), comparator);
9700+
}
9701+
9702+
/**
9703+
* Determines if the Iterable is sorted using the given Closure to determine order.
9704+
* <p>
9705+
* If the Closure has two parameters it is used like a traditional Comparator.
9706+
* Otherwise, the Closure is assumed to take a single parameter and return a
9707+
* Comparable (typically an Integer) which is then used for further comparison.
9708+
* <pre class="groovyTestCase">
9709+
* assert ["hi","hey","hello"].isSorted { it.length() }
9710+
* assert !["hello","hi","hey"].isSorted { it.length() }
9711+
* assert ["hi","hey","hello"].isSorted { a, b {@code ->} a.length() {@code <=>} b.length() }
9712+
* </pre>
9713+
*
9714+
* @param self the Iterable to check
9715+
* @param closure a 1 or 2 arg Closure used to determine the ordering
9716+
* @return true if the elements are sorted according to the closure
9717+
* @see #isSorted(Iterable, Comparator)
9718+
* @since 6.0.0
9719+
*/
9720+
public static <T> boolean isSorted(Iterable<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
9721+
Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(closure);
9722+
return isSorted(self, comparator);
9723+
}
9724+
9725+
/**
9726+
* Determines if the Iterator elements are sorted. Assumes the elements are
9727+
* comparable and uses a {@link NumberAwareComparator} to determine the order.
9728+
* <p>
9729+
* The iterator will be exhausted of elements after determining the sorted status.
9730+
*
9731+
* @param self the Iterator to check
9732+
* @return true if the elements are sorted in non-descending order
9733+
* @see #isSorted(Iterator, Comparator)
9734+
* @since 6.0.0
9735+
*/
9736+
public static <T> boolean isSorted(Iterator<T> self) {
9737+
return isSorted(self, new NumberAwareComparator<>());
9738+
}
9739+
9740+
/**
9741+
* Determines if the Iterator elements are sorted according to the given Comparator.
9742+
* This is efficient — it checks adjacent pairs and short-circuits on the first
9743+
* out-of-order pair. The iterator will be exhausted only if the elements are sorted.
9744+
*
9745+
* @param self the Iterator to check
9746+
* @param comparator a Comparator used for the comparison
9747+
* @return true if the elements are sorted according to the comparator
9748+
* @since 6.0.0
9749+
*/
9750+
public static <T> boolean isSorted(Iterator<T> self, Comparator<? super T> comparator) {
9751+
if (!self.hasNext()) return true;
9752+
T prev = self.next();
9753+
while (self.hasNext()) {
9754+
T curr = self.next();
9755+
if (comparator.compare(prev, curr) > 0) return false;
9756+
prev = curr;
9757+
}
9758+
return true;
9759+
}
9760+
9761+
/**
9762+
* Determines if the Iterator elements are sorted using the given Closure to determine order.
9763+
* <p>
9764+
* If the Closure has two parameters it is used like a traditional Comparator.
9765+
* Otherwise, the Closure is assumed to take a single parameter and return a
9766+
* Comparable which is then used for further comparison.
9767+
* <p>
9768+
* The iterator will be exhausted of elements after determining the sorted status.
9769+
*
9770+
* @param self the Iterator to check
9771+
* @param closure a 1 or 2 arg Closure used to determine the ordering
9772+
* @return true if the elements are sorted according to the closure
9773+
* @see #isSorted(Iterator, Comparator)
9774+
* @since 6.0.0
9775+
*/
9776+
public static <T> boolean isSorted(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
9777+
Comparator<T> comparator = (closure.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(closure) : new ClosureComparator<>(closure);
9778+
return isSorted(self, comparator);
9779+
}
9780+
9781+
/**
9782+
* Determines if the Map entries are sorted.
9783+
* Assumes the entries are comparable and uses a {@link NumberAwareValueComparator}
9784+
* to determine the order. The map's iteration order is checked.
9785+
* <pre class="groovyTestCase">
9786+
* def map = new LinkedHashMap([b:3, d:4, a:5, c:6])
9787+
* assert map.isSorted()
9788+
* </pre>
9789+
*
9790+
* @param self the Map to check
9791+
* @return true if the map entries are sorted in non-descending order
9792+
* @since 6.0.0
9793+
*/
9794+
public static <K, V> boolean isSorted(Map<K, V> self) {
9795+
return isSorted(self, new NumberAwareValueComparator<>());
9796+
}
9797+
9798+
/**
9799+
* Determines if the Map entries are sorted according to the given Comparator.
9800+
* The map's iteration order is checked.
9801+
* {@code SortedMap} instances always return {@code true}.
9802+
*
9803+
* @param self the Map to check
9804+
* @param comparator a Comparator used to compare Map.Entry instances
9805+
* @return true if the map entries are sorted according to the comparator
9806+
* @since 6.0.0
9807+
*/
9808+
public static <K, V> boolean isSorted(Map<K, V> self, Comparator<Map.Entry<K, V>> comparator) {
9809+
if (self instanceof SortedMap) return true;
9810+
return isSorted(self.entrySet(), comparator);
9811+
}
9812+
9813+
/**
9814+
* Determines if the Map entries are sorted using the given Closure to determine order.
9815+
* <p>
9816+
* If the Closure has two parameters it is used like a traditional Comparator on entries.
9817+
* Otherwise, the Closure is assumed to take a single entry parameter and return a
9818+
* Comparable which is then used for further comparison.
9819+
*
9820+
* @param self the Map to check
9821+
* @param condition a Closure used as a comparator
9822+
* @return true if the map entries are sorted according to the closure
9823+
* @since 6.0.0
9824+
*/
9825+
public static <K, V> boolean isSorted(Map<K, V> self, @ClosureParams(value=FromString.class, options={"Map.Entry<K,V>","Map.Entry<K,V>,Map.Entry<K,V>"}) Closure condition) {
9826+
Comparator<Map.Entry<K,V>> comparator = (condition.getMaximumNumberOfParameters() == 1) ? new OrderBy<>(condition) : new ClosureComparator<>(condition);
9827+
return isSorted(self, comparator);
9828+
}
9829+
96609830
//--------------------------------------------------------------------------
96619831
// isUpperCase
96629832

0 commit comments

Comments
 (0)