This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new da291866a9 GROOVY-11808: create a DGM#groupByMany extension method
da291866a9 is described below

commit da291866a90e044866deafb1e2e292f02808e7c8
Author: Paul King <[email protected]>
AuthorDate: Wed Nov 26 21:48:02 2025 +1000

    GROOVY-11808: create a DGM#groupByMany extension method
---
 .../groovy/runtime/DefaultGroovyMethods.java       | 141 +++++++++++++++++++++
 1 file changed, 141 insertions(+)

diff --git 
a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java 
b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index ac078870e7..f6e1b6113f 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -126,6 +126,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Deque;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -7957,6 +7958,146 @@ public class DefaultGroovyMethods extends 
DefaultGroovyMethodsSupport {
         groupedElements.add(element);
     }
 
+    
//--------------------------------------------------------------------------
+    // groupByMany
+
+    /**
+     * Sorts all Iterable members into (sub)groups determined by the supplied
+     * mapping closure. Each closure should return a list of keys. The item
+     * should be grouped by each of these keys. 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>
+     * def people = [
+     *     [name: 'Alice', cities: ['NY', 'LA']],
+     *     [name: 'Bob',   cities: ['NY']],
+     *     [name: 'Cara',  cities: ['LA', 'CHI']]
+     * ]
+     *
+     * def grouped = people*.name.groupByMany { people.find{ p -> it == p.name 
}.cities }
+     *
+     * assert grouped == [
+     *     NY  : ['Alice', 'Bob'],
+     *     LA  : ['Alice', 'Cara'],
+     *     CHI : ['Cara']
+     * ]
+     * </pre>
+     *
+     * @param self  an iterable to group
+     * @param keyFn a closure returning an Iterable of keys for each element
+     * @return a new Map from keys to lists of elements
+     * @since 6.0.0
+     */
+    public static <T, K> Map<K, List<T>> groupByMany(Iterable<T> self, 
Closure<? extends Iterable<? extends K>> keyFn) {
+        Map<K, List<T>> result = new HashMap<>();
+
+        for (T item : self) {
+            Iterable<? extends K> keys = keyFn.call(item);
+            if (keys == null) continue;
+
+            for (K key : keys) {
+                result.computeIfAbsent(key, k -> new ArrayList<>())
+                    .add(item);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Sorts all Iterable members into (sub)groups determined by the supplied
+     * mapping closure. Each closure should return a list of keys. The item
+     * should be grouped by each of these keys. 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>
+     * record Person(String name, List&lt;String> cities) { }
+     *
+     * def people = [
+     *     new Person('Alice', ['NY', 'LA']),
+     *     new Person('Bob', ['NY']),
+     *     new Person('Cara', ['LA', 'CHI'])
+     * ]
+     *
+     * def grouped = people.groupByMany(Person::name, Person::cities)
+     *
+     * assert grouped == [
+     *     NY  : ['Alice', 'Bob'],
+     *     LA  : ['Alice', 'Cara'],
+     *     CHI : ['Cara']
+     * ]
+     * </pre>
+     *
+     * @param self  an iterable to group
+     * @param valueFn a closure which can transform each element before 
collecting
+     * @param keyFn a closure returning an Iterable of keys for each element
+     * @return a new Map from keys to lists of elements
+     * @since 6.0.0
+     */
+    public static <T, K> Map<K, List<T>> groupByMany(Iterable<T> self, 
Closure<? extends T> valueFn, Closure<? extends Iterable<? extends K>> keyFn) {
+        Map<K, List<T>> result = new HashMap<>();
+
+        for (T item : self) {
+            Iterable<? extends K> keys = keyFn.call(item);
+            if (keys == null) continue;
+
+            for (K key : keys) {
+                result.computeIfAbsent(key, k -> new ArrayList<>())
+                    .add(valueFn.call(item));
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Sorts all Iterable members into (sub)groups determined by the supplied
+     * mapping closure. Each closure should return a list of keys. The item
+     * should be grouped by each of these keys. 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>
+     * def citiesLived = [
+     *     Alice: ['NY', 'LA'],
+     *     Bob: ['NY'],
+     *     Cara: ['LA', 'CHI']
+     * ]
+     *
+     * def grouped = citiesLived.groupByMany()
+     *
+     * assert grouped == [
+     *     NY  : ['Alice', 'Bob'],
+     *     LA  : ['Alice', 'Cara'],
+     *     CHI : ['Cara']
+     * ]
+     * </pre>
+     *
+     * @param self  a Map to group
+     * @return a new Map from keys to lists of elements
+     * @since 6.0.0
+     */
+    public static <T, K> Map<K, List<T>> groupByMany(Map<T, List<K>> self) {
+        Map<K, List<T>> result = new HashMap<>();
+
+        for (Map.Entry<T, List<K>> e : self.entrySet()) {
+            Iterable<? extends K> keys = e.getValue();
+            if (keys == null) continue;
+
+            for (K key : keys) {
+                result.computeIfAbsent(key, k -> new ArrayList<>())
+                    .add(e.getKey());
+            }
+        }
+
+        return result;
+    }
+
     
//--------------------------------------------------------------------------
     // groupEntriesBy
 

Reply via email to