Repository: jena
Updated Branches:
  refs/heads/master 87c484631 -> 14ed53fa7


JENA-1641: Iter.flatMap.

Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/7ac69b3a
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/7ac69b3a
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/7ac69b3a

Branch: refs/heads/master
Commit: 7ac69b3ad7e2be3398b9daee72954ffa0efb36c3
Parents: 87c4846
Author: Andy Seaborne <[email protected]>
Authored: Tue Nov 27 14:21:26 2018 +0000
Committer: Andy Seaborne <[email protected]>
Committed: Tue Nov 27 14:21:26 2018 +0000

----------------------------------------------------------------------
 .../org/apache/jena/atlas/iterator/Iter.java    | 17 +++++
 .../jena/atlas/iterator/IteratorFlatMap.java    | 69 ++++++++++++++++++
 .../jena/atlas/iterator/IteratorFlatten.java    | 76 ++++++++++++++++++++
 .../apache/jena/atlas/iterator/TestIter.java    | 33 +++++++++
 4 files changed, 195 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/7ac69b3a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java 
b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
index 5e615ed..797f3d5 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
@@ -287,6 +287,19 @@ public class Iter<T> implements Iterator<T> {
         return toList(map(list.iterator(), converter)) ;
     }
 
+    /** 
+     * Apply a function to every element of an iterator, to produce possibly 
multiple mapping each time.
+     * See {@link Stream#flatMap}
+     */
+    public static <T, R> Iterator<R> flatMap(Iterator<T> iter, Function<T, 
Iterator<R>> mapper) {
+        // Combined mapping and flattening  
+        return new IteratorFlatMap<>(iter, mapper);
+        // For reference: an alternative splitting the mapping out:
+        //   Iterator<Iterator<R>> pipeline = Iter.map(iter, mapper);
+        //   Iterator<R> outcome = new IteratorFlatten<>(pipeline);
+        // IteratorFlatten is only one line and one field less complicated 
than IteratorFlatMap
+    }
+    
     /**
      * Apply an action to everything in stream, yielding a stream of the
      * same items.
@@ -777,6 +790,10 @@ public class Iter<T> implements Iterator<T> {
         return iter(map(iterator, converter)) ;
     }
 
+    /** FlatMap each element using given function of element to iterator of 
mapped elements.s */
+    public <R> Iter<R> flatMap(Function<T, Iterator<R>> converter) {
+        return iter(flatMap(iterator, converter)) ;
+    }
     /**
      * Apply an action to everything in the stream, yielding a stream of the
      * original items.

http://git-wip-us.apache.org/repos/asf/jena/blob/7ac69b3a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java 
b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java
new file mode 100644
index 0000000..f223cff
--- /dev/null
+++ 
b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatMap.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.iterator;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/** flatMap iterator.
+ * See {@link Stream#flatMap}
+ */
+public class IteratorFlatMap<IN,OUT> implements Iterator<OUT> {
+    private boolean         finished = false;
+    private Iterator<OUT>   current  = null;
+    private Iterator<IN>    input;
+    final private Function<IN, Iterator<OUT>> mapper;
+
+    public IteratorFlatMap(Iterator<IN> iter, Function<IN, Iterator<OUT>> 
mapper) {
+        this.input = iter;
+        this.mapper = mapper ;
+    }
+    
+    @Override
+    public boolean hasNext() {
+        if ( finished )
+            return false;
+        // !finished and current == null : happens at the start.
+        if ( current != null && current.hasNext() )
+            return true;
+        // Stage finished or this is the first call.
+        //current = null;
+        while ( input.hasNext() ) {
+            IN x = input.next();
+            current = mapper.apply(x);
+            if ( current == null || ! current.hasNext() )
+                continue;
+            // There is at least one item in the new current stage.
+            return true;
+        }
+        // Nothing more.
+        current = null;
+        finished = true;
+        return false;
+    }
+    
+    @Override
+    public OUT next() {
+        if ( !hasNext() )
+            throw new NoSuchElementException();
+        return current.next();
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/7ac69b3a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatten.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatten.java 
b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatten.java
new file mode 100644
index 0000000..5316252
--- /dev/null
+++ 
b/jena-base/src/main/java/org/apache/jena/atlas/iterator/IteratorFlatten.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.iterator;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/** Iterator that flattens an iterator of iterators ({@code 
Iterator<Iterator<X>>}). */
+public class IteratorFlatten<X> implements Iterator<X> {
+    private boolean     finished = false;
+    private Iterator<X> current  = null;
+    final private Iterator<Iterator<X>> pipeline;
+
+    public IteratorFlatten(Iterator<Iterator<X>> pipeline) {
+        this.pipeline = pipeline;
+    }
+    
+    @Override
+    public boolean hasNext() {
+        if ( finished )
+            return false;
+        // !finished and current == null : happens at the start.
+        if ( current != null && current.hasNext() )
+            return true;
+        // Stage finished or this is the first call.
+        while(pipeline.hasNext()) {
+            current = pipeline.next();
+            if ( current == null || ! current.hasNext())
+                continue;
+            // There is at least one item in the new current stage.
+            return true;
+        }
+        // Nothing more.
+        current = null;
+        finished = true;
+        return false;
+    }
+
+    @Override
+    public X next() {
+        if ( !hasNext() )
+            throw new NoSuchElementException();
+        return current.next();
+    }
+    
+//    /** Advance an iterator, skipping nulls.
+//     * Return null iff the iterator has ended.
+//     * @implNote
+//     * Unlike a filtering out null from an iterator (e.g. {@link 
Iter#filter(Iterator, Predicate)}),
+//     * this code does not create intermediate objects.  
+//     */
+//    private static <X> X /*Iter.*/advance(Iterator<X> iter) {
+//        while (iter.hasNext()) {
+//            X item = iter.next();
+//            if ( item != null )
+//                return item;
+//        }
+//        return null;
+//    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/7ac69b3a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java 
b/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
index 13ba84b..c803240 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/iterator/TestIter.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse ;
 import static org.junit.Assert.assertTrue ;
 
 import java.util.* ;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.IntStream;
 
@@ -195,6 +196,38 @@ public class TestIter
         test(it, "xx", "yy", "zz");
     }
        
+    @Test
+    public void flatmap_01() {
+        Iterator<String> it = Iter.flatMap(data2.iterator(), item -> 
Arrays.asList(item+item, item).iterator());
+        test(it, "xx", "x", "yy", "y", "zz", "z");
+    }
+    
+    @Test
+    public void flatmap_02() {
+        List<Integer> data = Arrays.asList(1,2,3);
+        Iterator<Integer> it = Iter.flatMap(data.iterator(), x -> {
+            if ( x == 2 ) return Iter.nullIterator();
+            return Arrays.asList(x*x).iterator();
+        });
+        test(it, 1, 9);
+    }
+
+    @Test
+    public void flatmap_03() {
+        List<Integer> data = Arrays.asList(1,2,3);
+        Function<Integer, Iterator<String>> mapper = x -> {
+                switch(x) {
+                    case 1: return Iter.nullIterator();
+                    case 2: return Arrays.asList("two").iterator();
+                    case 3: return Iter.nullIterator();
+                    default: throw new IllegalArgumentException(); 
+                }
+            };
+            
+        Iter<String> it = Iter.iter(data.iterator()).flatMap(mapper);
+        test(it, "two");
+    }
+
     private Predicate<String> filter = item -> item.length() == 1;
    
     @Test

Reply via email to