Elegant way to get index of filter or first with RX Java

I like Samuel's answer. I've written a Transformer implementation of his stuff which is Java 6/7 compliant and uses a Long index (I have long streams to deal with!).

You call it like this:

Observable.just("a", "b").compose(MapWithIndex.<String>instance());

Here's the class (source and unit tests here):

package com.github.davidmoten.util;

import java.util.Iterator;

import rx.Observable;
import rx.Observable.Transformer;
import rx.functions.Func2;

import com.github.davidmoten.util.MapWithIndex.Indexed;

public final class MapWithIndex<T> implements Transformer<T, Indexed<T>> {

    private static class Holder {
        static final MapWithIndex<?> INSTANCE = new MapWithIndex<Object>();
    }

    @SuppressWarnings("unchecked")
    public static <T> MapWithIndex<T> instance() {
        return (MapWithIndex<T>) Holder.INSTANCE;
    }

    @Override
    public Observable<Indexed<T>> call(Observable<T> source) {

        return source.zipWith(NaturalNumbers.instance(), new Func2<T, Long, Indexed<T>>() {

            @Override
            public Indexed<T> call(T t, Long n) {
                return new Indexed<T>(t, n);
            }
        });
    }

    public static class Indexed<T> {
        private final long index;
        private final T value;

        public Indexed(T value, long index) {
            this.index = index;
            this.value = value;
        }

        @Override
        public String toString() {
            return index + "->" + value;
        }

        public long index() {
            return index;
        }

        public T value() {
            return value;
        }

    }

    private static final class NaturalNumbers implements Iterable<Long> {

        private static class Holder {
            static final NaturalNumbers INSTANCE = new NaturalNumbers();
        }

        static NaturalNumbers instance() {
            return Holder.INSTANCE;
        }

        @Override
        public Iterator<Long> iterator() {
            return new Iterator<Long>() {

                private long n = 0;

                @Override
                public boolean hasNext() {
                    return true;
                }

                @Override
                public Long next() {
                    return n++;
                }

                @Override
                public void remove() {
                    throw new RuntimeException("not supported");
                }
            };
        }

    }

}

There is not one correct way to do this. Samuel's answer can be one. It also depends the bigger picture on what is you array and how you need to handle the results in the subscriber.

Here is a (java7) example where the Observer emits a ArrayList<String>:

strings.map(new Func1<ArrayList<String>, ArrayList<Integer>>() {
            @Override
            public ArrayList<Integer> call(ArrayList<String> strings) {
                ArrayList<Integer> list = new ArrayList<>();
                for(String string: strings){
                    if(statement){
                        list.add(strings.indexOf(string));
                    }
                }
                return list;
            }
        }).subscribe(new Action1<ArrayList<Integer>>() {
            @Override
            public void call(ArrayList<Integer> integers) {
                doSomethingWithThePositions(integers);
            }
        });

And here is an example where the Observer emits a String per time from your ArrayList:

final ArrayList<String> strings1 = new ArrayList<>();
Observable.from(strings1)
        .filter(new Func1<String, Boolean>() {
            @Override
            public Boolean call(String s) {
                return !s.isEmpty();
            }
        })
        .subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                int i = strings1.indexOf(s);
                doSomethingWithThePosition(i);
            }
        });

There used to be mapWithIndex and zipWithIndex operators in RxJava, but they were removed, see here why.

So you have to write some library boilerplate once:

class Indexed<T> {
    final int index;
    final T value;
    public Indexed(T value, int index) {
        this.index = index;
        this.value = value;
    }
    @Override
    public String toString() {
        return index + ") " + value;
    }
}

Iterable<Integer> naturals = IntStream.iterate(0, i -> i + 1)::iterator;

But then, you can get it reasonably concise:

Observable<String> obs = Observable.just("zero", "one", "two", "three");
obs.zipWith(naturals, (s, i) -> new Indexed<String>(s, i))
   .filter(e -> e.value.length() > 4)
   .subscribe(System.out::println);