Ver Fonte

#98
new IteratorUtils#deepFlatIf
new IteratorUtils#treeFlatIf

Ranides Atterwim há 3 anos atrás
pai
commit
286bbf89e5

+ 66 - 1
assira.core/src/main/java/net/ranides/assira/collection/iterators/IteratorUtils.java

@@ -352,6 +352,22 @@ public final class IteratorUtils {
         return new FlatIterator<>(iterator);
     }
 
+    public static <T> Iterator<T> flat(Iterator<? extends T> iterator, Function<? super T, Iterator<T>> splitter) {
+        return new FlatMapIterator<>(FlatMode.SHALLOW, iterator, item -> true, splitter);
+    }
+
+    public static <T> Iterator<T> flatIf(Iterator<? extends T> iterator, Predicate<? super T> split, Function<? super T, Iterator<T>> splitter) {
+        return new FlatMapIterator<>(FlatMode.SHALLOW, iterator, split, splitter);
+    }
+
+    public static <T> Iterator<T> deepFlatIf(Iterator<? extends T> iterator, Predicate<? super T> split, Function<? super T, Iterator<T>> splitter) {
+        return new FlatMapIterator<>(FlatMode.DEEP, iterator, split, splitter);
+    }
+
+    public static <T> Iterator<T> treeFlatIf(Iterator<? extends T> iterator, Predicate<? super T> split, Function<? super T, Iterator<T>> splitter) {
+        return new FlatMapIterator<>(FlatMode.TREE, iterator, split, splitter);
+    }
+
     /**
      * Returns view which lazily expands all provided Iterators and merges into one iterator.
      * Flat iterators move forward only as much as necessary (which means one element at time).
@@ -1247,7 +1263,7 @@ public final class IteratorUtils {
         }
 
     }
-    
+
     private static final class FlatIterator<T> implements Iterator<T> {
 
         private final Iterator<? extends Iterator<? extends T>> li;
@@ -1287,6 +1303,55 @@ public final class IteratorUtils {
 
     }
 
+    private enum FlatMode {
+        SHALLOW,
+        DEEP,
+        TREE
+    }
+
+    private static class FlatMapIterator<T> extends ForwardIterator<T> {
+
+        private final Deque<Iterator<? extends T>> stack = new ArrayDeque<>(1);
+
+        private final FlatMode mode;
+        private final Predicate<? super T> split;
+        private final Function<? super T, Iterator<T>> splitter;
+
+        public FlatMapIterator(FlatMode mode, Iterator<? extends T> iterator, Predicate<? super T> split, Function<? super T, Iterator<T>> splitter) {
+            this.mode = mode;
+            this.split = split;
+            this.splitter = splitter;
+            this.stack.push(iterator);
+        }
+
+        @Override
+        protected boolean next(Consumer<? super T> action) {
+            if(stack.isEmpty()) {
+                return false;
+            }
+
+            if(!stack.peek().hasNext()) {
+                stack.pop();
+                return next(action);
+            }
+
+            T item = stack.peek().next();
+            if((stack.size() == 1 || mode != FlatMode.SHALLOW) && split.test(item)) {
+                stack.push(splitter.apply(item));
+                if(mode == FlatMode.TREE)  {
+                    action.accept(item);
+                    return true;
+                } else {
+                    return next(action);
+                }
+            } else {
+                action.accept(item);
+                return true;
+            }
+        }
+
+    }
+
     private static class SingleIterator<T> implements Iterator<T> {
 
         private final T value;

+ 66 - 0
assira.core/src/main/java/net/ranides/assira/collection/query/CQuery.java

@@ -602,6 +602,72 @@ public interface CQuery<T> extends Iterable<T> {
      */
     CQuery<T> flatArrayIf(Predicate<? super T> p, Function<? super T, T[]> f);
 
+    /**
+     * Expands conditionally every item into Query and concatenates generated results into one query.
+     * It checks if element should be expanded or returned without change.
+     * Please note: supports partial evaluation of outer query and inner queries.
+     *
+     * @param p p
+     * @param f f
+     * @return query
+     */
+    CQuery<T> deepFlatIf(Predicate<? super T> p, Function<? super T, CQuery<T>> f);
+
+    /**
+     * Expands conditionally every item into Iterator and concatenates generated results into one query.
+     * It checks if element should be expanded or returned without change.
+     * Please note: supports partial evaluation of outer query and inner iterators.
+     *
+     * @param p p
+     * @param f f
+     * @return query
+     */
+    CQuery<T> deepFlatIterableIf(Predicate<? super T> p, Function<? super T, Iterable<T>> f);
+
+    /**
+     * Expands conditionally every item into array and concatenates generated results into one query.
+     * It checks if element should be expanded or returned without change.
+     * Please note: supports partial evaluation of outer query.
+     *
+     * @param p p
+     * @param f f
+     * @return query
+     */
+    CQuery<T> deepFlatArrayIf(Predicate<? super T> p, Function<? super T, T[]> f);
+
+    /**
+     * Expands conditionally every item into Query and concatenates generated results into one query.
+     * It checks if element should be expanded or returned without change.
+     * Please note: supports partial evaluation of outer query and inner queries.
+     *
+     * @param p p
+     * @param f f
+     * @return query
+     */
+    CQuery<T> treeFlatIf(Predicate<? super T> p, Function<? super T, CQuery<T>> f);
+
+    /**
+     * Expands conditionally every item into Iterator and concatenates generated results into one query.
+     * It checks if element should be expanded or returned without change.
+     * Please note: supports partial evaluation of outer query and inner iterators.
+     *
+     * @param p p
+     * @param f f
+     * @return query
+     */
+    CQuery<T> treeFlatIterableIf(Predicate<? super T> p, Function<? super T, Iterable<T>> f);
+
+    /**
+     * Expands conditionally every item into array and concatenates generated results into one query.
+     * It checks if element should be expanded or returned without change.
+     * Please note: supports partial evaluation of outer query.
+     *
+     * @param p p
+     * @param f f
+     * @return query
+     */
+    CQuery<T> treeFlatArrayIf(Predicate<? super T> p, Function<? super T, T[]> f);
+
     /**
      * Terminal operation.
      * Computes final result by applying accumulator on every item, combining it with

+ 30 - 0
assira.core/src/main/java/net/ranides/assira/collection/query/CQueryAbstract.java

@@ -402,6 +402,36 @@ public abstract class CQueryAbstract<T> implements CQuery<T>, CQueryFeatures {
         return BaseFlat.flatArrayIf(this, p, f);
     }
 
+    @Override
+    public CQuery<T> deepFlatIf(Predicate<? super T> p, Function<? super T, CQuery<T>> f) {
+        return BaseFlat.deepFlatIf(this, p, f);
+    }
+
+    @Override
+    public CQuery<T> deepFlatIterableIf(Predicate<? super T> p, Function<? super T, Iterable<T>> f) {
+        return BaseFlat.deepFlatIterableIf(this, p, f);
+    }
+
+    @Override
+    public CQuery<T> deepFlatArrayIf(Predicate<? super T> p, Function<? super T, T[]> f) {
+        return BaseFlat.deepFlatArrayIf(this, p, f);
+    }
+
+    @Override
+    public CQuery<T> treeFlatIf(Predicate<? super T> p, Function<? super T, CQuery<T>> f) {
+        return BaseFlat.treeFlatIf(this, p, f);
+    }
+
+    @Override
+    public CQuery<T> treeFlatIterableIf(Predicate<? super T> p, Function<? super T, Iterable<T>> f) {
+        return BaseFlat.treeFlatIterableIf(this, p, f);
+    }
+
+    @Override
+    public CQuery<T> treeFlatArrayIf(Predicate<? super T> p, Function<? super T, T[]> f) {
+        return BaseFlat.treeFlatArrayIf(this, p, f);
+    }
+
     @Override
     public T reduce(T identity, BinaryOperator<T> accumulator) {
         if(!hasFastStream()) {

+ 26 - 6
assira.core/src/main/java/net/ranides/assira/collection/query/support/BaseFlat.java

@@ -237,9 +237,7 @@ public class BaseFlat {
 
             @Override
             public Iterator<T> iterator() {
-                return IteratorUtils.flat(IteratorUtils.map(source.iterator(), s -> {
-                    return p.test(s) ? f.apply(s).iterator() : IteratorUtils.singletonIterator(s);
-                }));
+                return IteratorUtils.flatIf(source.iterator(), p, item -> f.apply(item).iterator());
             }
 
             @Override
@@ -291,9 +289,7 @@ public class BaseFlat {
 
             @Override
             public Iterator<T> iterator() {
-                return IteratorUtils.flat(IteratorUtils.map(source.iterator(), s -> {
-                    return p.test(s) ? f.apply(s).iterator() : IteratorUtils.singletonIterator(s);
-                }));
+                return IteratorUtils.flatIf(source.iterator(), p, item -> f.apply(item).iterator());
             }
 
             @Override
@@ -364,6 +360,30 @@ public class BaseFlat {
         return new CQFlatArrayIf(that);
     }
 
+    public static <T> CQuery<T> deepFlatIf(CQuery<T> that, Predicate<? super T> p, Function<? super T, CQuery<T>> f) {
+        return CQuery.from().iterable(() -> IteratorUtils.deepFlatIf(that.iterator(), p, item -> f.apply(item).iterator()));
+    }
+
+    public static <T> CQuery<T> deepFlatIterableIf(CQuery<T> that, Predicate<? super T> p, Function<? super T, Iterable<T>> f) {
+        return CQuery.from().iterable(() -> IteratorUtils.deepFlatIf(that.iterator(), p, item -> f.apply(item).iterator()));
+    }
+
+    public static <T> CQuery<T> deepFlatArrayIf(CQuery<T> that, Predicate<? super T> p, Function<? super T, T[]> f) {
+        return CQuery.from().iterable(() -> IteratorUtils.deepFlatIf(that.iterator(), p, item -> ArrayUtils.iterator(f.apply(item))));
+    }
+
+    public static <T> CQuery<T> treeFlatIf(CQuery<T> that, Predicate<? super T> p, Function<? super T, CQuery<T>> f) {
+        return CQuery.from().iterable(() -> IteratorUtils.treeFlatIf(that.iterator(), p, item -> f.apply(item).iterator()));
+    }
+
+    public static <T> CQuery<T> treeFlatIterableIf(CQuery<T> that, Predicate<? super T> p, Function<? super T, Iterable<T>> f) {
+        return CQuery.from().iterable(() -> IteratorUtils.treeFlatIf(that.iterator(), p, item -> f.apply(item).iterator()));
+    }
+
+    public static <T> CQuery<T> treeFlatArrayIf(CQuery<T> that, Predicate<? super T> p, Function<? super T, T[]> f) {
+        return CQuery.from().iterable(() -> IteratorUtils.treeFlatIf(that.iterator(), p, item -> ArrayUtils.iterator(f.apply(item))));
+    }
+
     private static abstract class CQAbstractSplit<T,R> extends CQAbstractFilter<T,R> {
 
         public CQAbstractSplit(CQuery<T> source) {

+ 120 - 0
assira.core/src/test/java/net/ranides/assira/collection/iterators/FlatMapIteratorTest.java

@@ -0,0 +1,120 @@
+package net.ranides.assira.collection.iterators;
+
+import org.junit.Test;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+public class FlatMapIteratorTest {
+
+    @Test
+    public void shallow() {
+        Iterator<Integer> i = IteratorUtils.flatIf(
+            Arrays.asList(100, 200, 300, 400, 500).iterator(),
+            item -> item > 10,
+            item -> {
+                int v = item / 10;
+                return Arrays.asList(v + 0, v + 1, v + 2, v + 3, v + 4).iterator();
+            }
+        );
+
+        ArrayList<Integer> target = IteratorUtils.collect(i, new ArrayList<>());
+        List<Integer> expected = Arrays.asList(
+            10, 11, 12, 13, 14,
+            20, 21, 22, 23, 24,
+            30, 31, 32, 33, 34,
+            40, 41, 42, 43, 44,
+            50, 51, 52, 53, 54
+        );
+        assertEquals(expected, target);
+    }
+
+    @Test
+    public void deep() {
+        Iterator<Integer> i = IteratorUtils.deepFlatIf(
+            Arrays.asList(100, 200, 300, 400, 500).iterator(),
+            item -> item > 10,
+            item -> {
+                int v = item / 10;
+                return Arrays.asList(v + 0, v + 1, v + 2, v + 3, v + 4).iterator();
+            }
+        );
+
+        ArrayList<Integer> target = IteratorUtils.collect(i, new ArrayList<>());
+        List<Integer> expected = Arrays.asList(
+            10,
+            1, 2, 3, 4, 5,
+            1, 2, 3, 4, 5,
+            1, 2, 3, 4, 5,
+            1, 2, 3, 4, 5,
+            2, 3, 4, 5, 6,
+            2, 3, 4, 5, 6,
+            2, 3, 4, 5, 6,
+            2, 3, 4, 5, 6,
+            2, 3, 4, 5, 6,
+            3, 4, 5, 6, 7,
+            3, 4, 5, 6, 7,
+            3, 4, 5, 6, 7,
+            3, 4, 5, 6, 7,
+            3, 4, 5, 6, 7,
+            4, 5, 6, 7, 8,
+            4, 5, 6, 7, 8,
+            4, 5, 6, 7, 8,
+            4, 5, 6, 7, 8,
+            4, 5, 6, 7, 8,
+            5, 6, 7, 8, 9,
+            5, 6, 7, 8, 9,
+            5, 6, 7, 8, 9,
+            5, 6, 7, 8, 9,
+            5, 6, 7, 8, 9
+        );
+        assertEquals(expected, target);
+    }
+
+    @Test
+    public void tree() {
+        Iterator<Integer> i = IteratorUtils.treeFlatIf(
+            Arrays.asList(100, 200, 300, 400, 500).iterator(),
+            item -> item > 10,
+            item -> {
+                int v = item / 10;
+                return Arrays.asList(v + 0, v + 1, v + 2, v + 3, v + 4).iterator();
+            }
+        );
+
+        ArrayList<Integer> target = IteratorUtils.collect(i, new ArrayList<>());
+        List<Integer> expected = Arrays.asList(
+            100, 10,
+            11, 1, 2, 3, 4, 5,
+            12, 1, 2, 3, 4, 5,
+            13, 1, 2, 3, 4, 5,
+            14, 1, 2, 3, 4, 5,
+            200,
+            20, 2, 3, 4, 5, 6,
+            21, 2, 3, 4, 5, 6,
+            22, 2, 3, 4, 5, 6,
+            23, 2, 3, 4, 5, 6,
+            24, 2, 3, 4, 5, 6,
+            300,
+            30, 3, 4, 5, 6, 7,
+            31, 3, 4, 5, 6, 7,
+            32, 3, 4, 5, 6, 7,
+            33, 3, 4, 5, 6, 7,
+            34, 3, 4, 5, 6, 7,
+            400,
+            40, 4, 5, 6, 7, 8,
+            41, 4, 5, 6, 7, 8,
+            42, 4, 5, 6, 7, 8,
+            43, 4, 5, 6, 7, 8,
+            44, 4, 5, 6, 7, 8,
+            500,
+            50, 5, 6, 7, 8, 9,
+            51, 5, 6, 7, 8, 9,
+            52, 5, 6, 7, 8, 9,
+            53, 5, 6, 7, 8, 9,
+            54, 5, 6, 7, 8, 9
+        );
+        assertEquals(expected, target);
+    }
+}