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
