Friday, December 27, 2013

Fun with Guava: Interleaving Iterables

Problem: given some number (finite, possibly 0) of iterables, create an iterable that interleaves their elements.
For example, given [1, 2, 3] and [x, y, z], return [1, x, 2, y, 3, z]. Note that the iterables may be of different sizes -- some may be empty or even infinite. When a particular iterable has no more elements, just skip it (i.e. don't pad with nulls for the purpose of interleaving).
public static <T> FluentIterable<T> interleave(final Iterable<? extends Iterable<? extends T>> iterables) {        
    return new FluentIterable<T>() {
        @Override
        public Iterator<T> iterator() {
            return new AbstractIterator<T>() {
                final Iterator<Iterator<? extends T>> iterators; {
                    List<Iterator<? extends T>> list = Lists.newLinkedList();
                    for (Iterable<? extends T> iterable : iterables) {
                        list.add(iterable.iterator());
                    }
                    iterators = Iterators.cycle(list);
                }
                
                @Override
                protected T computeNext() {
                    while (iterators.hasNext()) {
                        Iterator<? extends T> iter = iterators.next();
                        if (iter.hasNext()) {
                            return iter.next();
                        } else {
                            iterators.remove();
                        }
                    }
                    return endOfData();
                }
            };
        }
    };
}
And thus:
System.out.println(
    interleave(
        ImmutableList.of(
            Iterables.cycle("+", "-"),
            ImmutableList.of(1, 2, 3, 4),
            Collections.emptyList(),
            Iterables.cycle(true, false, null)
        )
    ).limit(20)
);
// [+, 1, true, -, 2, false, +, 3, null, -, 4, true, +, false, -, null, +, true, -, false]

No comments:

Post a Comment