Kirill
Kirill

Reputation: 8331

Split Flowable to multiple groups based on next element in RxJava

I have a Flowable<String> which returns a huge list of strings with a back-pressure. Each string could be a title (group) or could be a list item. Titles starts at the beginning of line, items has preceding white-spaces e.g.:

Title-1
  item-1
  item-2
  item-3
Title-2
  item-1
  item-2
Title-3
  item-1

I need to split origin Flowable to multiple Flowables of items based on item's title: Flowable<String> -> Flowable<Flowable<String>>, where first flowable has items:

[title-1.item-1, title-1.item-2, title-1.item-3]

second: [title-2.item-1, title-2.item-2]

and third: [title-3.item-1].

I tried to use Flowable.groupBy() but didn't find a way to group items based on surrounding items.

Does some standard RxJava method exist to perform this operation? (of course, without loading all items in memory).

Upvotes: 0

Views: 225

Answers (2)

Progman
Progman

Reputation: 19555

You can use Flowable.scan() to build the next value of a flowable based on the previous build value. Use it to build the entries in the form of "titel.item". See the following example:

List<String> data = new ArrayList<String>();
data.add("Title-1");
data.add("  item-1");
data.add("  item-2");
data.add("  item-3");
data.add("Title-2");
data.add("  item-1");
data.add("  item-2");
data.add("Title-3");
data.add("  item-1");
    
Flowable<String> dataFlowable = Flowable.fromIterable(data);
Flowable<String> adjustedFlowable = dataFlowable.scan("", (a,b) -> {
    if (a.equals("")) {
        // it's the first loop
        return b;
    }
    if (!b.startsWith("  ")) {
        // it's a title
        return b;
    }
    // it's a list item, so build it
    String[] split = a.split("\\.", -2);
    return split[0]+"."+(b.trim());
    }
);
adjustedFlowable.blockingSubscribe(it -> {
    System.out.println(it);
});

This will generate the following output:

Title-1
Title-1.item-1
Title-1.item-2
Title-1.item-3
Title-2
Title-2.item-1
Title-2.item-2
Title-3
Title-3.item-1

As you see the items are now enriched with the title they belong to. From there you can use any filter() or groupBy() you want.

Upvotes: 2

borichellow
borichellow

Reputation: 1001

you can use Flowable.scan(). So you'll have something like:

originalFloawble
    .scan("" to "" as Pair<String, String>) {prev, newIncome -> 
        if (newIncome is Title) newIncome to ""
        else prev.first to newIncome
    }
    .filter { (title, item) -> item.isNotEmpty() }
    .groupBy { it.first }
    .map { it.map { (title, item) -> "$title.$item" } }

Upvotes: 0

Related Questions