9

I am quite new to RxJava (and Reactive paradigm in general), so please bear with me.

Suppose I have this News and this nested Comment data structure:

public class News {
  public int id;
  public int[] commentIds; //only top level comments
  public News(int id, int[] commentIds) {
    this.id = id;
    this.commentIds = commentIds;
  }
}

public class Comment {
  public int id;
  public int parentId; //ID of parent News or parent comment
  public int[] childIds;
  public Comment(int id, int parentId, int[] childIds) {
    this.id = id;
    this.parentId = parentId;
    this.childIds = childIds;
  }
}

and suppose I have this API endpoint:

getComments(int commentId) //return Observable<Comment> for Comment with ID commentId

Now, let's assume:

getComments(1); //will return Comment(1, 99, [3,4])
getComments(2); //will return Comment(2, 99, [5,6])
getComments(3); //will return Comment(3, 1, [])
getComments(4); //will return Comment(4, 1, [])
getComments(5); //will return Comment(5, 2, [])
getComments(6); //will return Comment(6, 2, [])

**

Now, if I have News n = News(99, [1,2]), how do I get all of its children comment recursively? i.e. to get comments with ID [1,2,3,4,5,6]?

**

I have searched and stumbled upon this: https://jkschneider.github.io/blog/2014/recursive-observables-with-rxjava.html

This is the recursion function:

public class FileRecursion {
    static Observable<File> listFiles(File f) {
        if(f.isDirectory())
            return Observable.from(f.listFiles()).flatMap(FileRecursion::listFiles);
        return Observable.just(f);
    }

    public static void main(String[] args) {
          Observable.just(new File("/Users/joschneider/Desktop"))
                  .flatMap(FileRecursion::listFiles)
                  .subscribe(f -> System.out.println(f.getAbsolutePath()));
    }
}

It shows an example on how to do recursive observable calls, but the inner function (f.listFiles()) is a blocking operation (doesn't return another Observable). In my case, the inner function (getComments) is a non-blocking function that returns another Observables. How do I do that?

Any help will be much appreciated.

Egor Neliuba
  • 14,784
  • 7
  • 59
  • 77
HoverPhoenix
  • 177
  • 1
  • 4
  • 13

1 Answers1

5

This does practically the same thing described in the article:

Observable<Comment> getInnerComments(Comment comment) {
    if (comment.childIds.length > 0)
        return Observable.merge(
                Observable.just(comment),
                Observable.from(comment.childIds)
                        .flatMap(id -> getComments(id))
                        .flatMap(this::getInnerComments));
    return Observable.just(comment);
}

public static void main(String[] args) {
    getComments(1)
          .flatMap(this::getInnerComments)
          .subscribe(c -> System.out.println(comment.toString()));
}

I start with the comment with id = 1, then I pass it to getInnerComments(). The getInnerComments() checks if the comment has children. If it does, it iterates over every child id (Observable#from) and loads every child with your getComments(int) API. Then every child is passed to the getInnerComments() to do the same procedure. If a comment doesn't have children, it is immediately returned using Observable#just.

This is pseudo-code and it wasn't tested, but you should get the idea.


Below is an example of how to get all comments and then aggregate them to one List<Comment>.

getNews(99)
        .flatMap(news -> Observable.from(news.commentIds))
        .flatMap(commentId -> getComments(commentId))
        .flatMap(comment -> getInnerComments(comment))
        .toList()
        .subscribe(commentList -> { });
Egor Neliuba
  • 14,784
  • 7
  • 59
  • 77
  • Egor, that recursion only got me the **leaf** comment (i.e. comment that doesn't have any children). Looks like the non-leaf comment got subscribed inside the recursion function. Any idea? Wait, I didn't see your updated answer. Let me contemplate first. – HoverPhoenix Jul 06 '15 at 14:36
  • 1
    Hm, you are right, I haven't thought of that. I edited my answer - now `getInnerComments()` returns the comment passed to it and then does the other stuff. – Egor Neliuba Jul 06 '15 at 14:44
  • @HoverPhoenix, wait a sec, I didn't understand your comment the first time I read it. Let me open up my IDE and try some stuff. – Egor Neliuba Jul 06 '15 at 14:57
  • You rocks, Egor. It worked! The key point I'm missing is `flatMap` inside `flatMap`. Moreover, now I understand why operator MERGE is needed! Thank you! I have accepted your post as the answer. – HoverPhoenix Jul 06 '15 at 15:04
  • Yep, I have just tested it and it works fine. Glad I could help you!. – Egor Neliuba Jul 06 '15 at 15:08
  • Wait, the key point is not `flatMap` inside `flatMap` -_-". I can put the inner `flatMap` to the outside and achieve the same result. When I tried it the first time, I used `int[] commentIds` as the recursion parameter. I was just too focused on how to solve the `non-blocking function` part to realize that I was wrong since the beginning. Anyway, thanks again, Egor. – HoverPhoenix Jul 06 '15 at 15:36