[jOOQ/jOOQ#12341] Add a Records::intoHierarchy Collector to turn Result into tree representations

This commit is contained in:
Lukas Eder 2023-03-24 15:36:23 +01:00
parent d7ffb56ad6
commit d6dcaa3762

View File

@ -42,12 +42,16 @@ import static java.util.stream.Collectors.toCollection;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@ -573,6 +577,109 @@ public final class Records {
);
}
/**
* Create a collector that can collect {@link Record} resulting from a
* {@link ResultQuery} into a hierarchy of custom data types.
* <p>
* For example:
* <p>
*
* <pre>
* <code>
* record File(String name, List&lt;File&gt; contents) {}
*
* List&lt;File&gt; files =
* ctx.select(FILE.ID, FILE.PARENT_ID, FILE.NAME)
* .from(FILE)
* .collect(intoHierarchy(
* r -&gt; r.value1(),
* r -&gt; r.value2(),
* (r, l) -&gt; new File(r.value3(), l)
* ));
* </code>
* </pre>
*
* @param <K> The key type (e.g. an <code>ID</code>)
* @param <E> The value type (e.g. a POJO)
* @param <R> The record type
* @param keyMapper A function that extract a key from a record
* @param parentKeyMapper A function that extracts the parent key from a
* record.
* @param recordMapper A function that maps a record and a list of child
* values to a new value type.
*/
public static final <K, E, R extends Record> Collector<R, ?, List<E>> intoHierarchy(
Function<? super R, ? extends K> keyMapper,
Function<? super R, ? extends K> parentKeyMapper,
BiFunction<? super R, ? super List<E>, ? extends E> recordMapper
) {
return intoHierarchy(keyMapper, parentKeyMapper, recordMapper, ArrayList::new);
}
/**
* Create a collector that can collect {@link Record} resulting from a
* {@link ResultQuery} into a hierarchy of custom data types.
* <p>
* For example:
* <p>
*
* <pre>
* <code>
* record File(String name, List&lt;File&gt; contents) {}
*
* List&lt;File&gt; files =
* ctx.select(FILE.ID, FILE.PARENT_ID, FILE.NAME)
* .from(FILE)
* .collect(intoHierarchy(
* r -&gt; r.value1(),
* r -&gt; r.value2(),
* (r, l) -&gt; new File(r.value3(), l),
* ArrayList::new
* ));
* </code>
* </pre>
*
* @param <K> The key type (e.g. an <code>ID</code>)
* @param <E> The value type (e.g. a POJO)
* @param <R> The record type
* @param keyMapper A function that extract a key from a record
* @param parentKeyMapper A function that extracts the parent key from a
* record.
* @param collectionFactory A supplier for new child value collections.
* @param recordMapper A function that maps a record and a list of child
* values to a new value type.
*/
public static final <K, E, C extends Collection<E>, R extends Record> Collector<R, ?, List<E>> intoHierarchy(
Function<? super R, ? extends K> keyMapper,
Function<? super R, ? extends K> parentKeyMapper,
BiFunction<? super R, ? super C, ? extends E> recordMapper,
Supplier<? extends C> collectionFactory
) {
return collectingAndThen(
intoMap(keyMapper, r -> {
C e = collectionFactory.get();
return new Tuple3<R, C, E>(r, e, recordMapper.apply(r, e));
}),
m -> {
List<E> r = new ArrayList<>();
m.forEach((k, v) -> {
K parent = parentKeyMapper.apply(v.t1());
E child = v.t3();
if (m.containsKey(parent))
m.get(parent).t2().add(child);
else
r.add(child);
});
return r;
}
);
}
private static final record Tuple3<T1, T2, T3>(T1 t1, T2 t2, T3 t3) {}
/**