4
votes

Given an Observable of a sequence of source objects, how can I map multiple output Objects out of each input object using rxjava? (one to many mapping)

I have a list of dishes representing the items that compose an order of a restaurant. I need to transform each Dish into one or more OrderLine. Each Dish map creates one OrderLine for its name+price, one OrderLine for each Topping and one OrderLine if it has a note.

INPUT
List dishes = {...}

OUTPUT
List orderLines = {...}

class Dish {

  public String name;
  public List toppings;
  public String note;
  public BigDecimal price;

}

class Topping {

  public String name;
  public BigDecimal price;

}

class OrderLine {

  public String name;
  public BigDecimal price;

}

Is there a way to do so using Functional Programming and/or Reactive Programming?

I do not want to use something imperative such as:


List orderLines = new ArrayList();
for (Dish dish : dishes) {

   orderLines.add(new OrderLine(dish.name, dish.price);

   for (Topping topping : dish.toppings) {
      orderLines.add(new OrderLine(topping.name, topping.price);
   }

   if(note != null) {
      orderLines.add(new OrderLine(dish.note, "");
   }

}

instead I would like to do something like this:

Observable.from(dishes).map( /* map each dish to many OrderLine */ ).
1

1 Answers

3
votes

What you're looking for is flatMap. This operates on a stream, outputting other Observables.

Here's a discussion on map() vs flatMap(), but even more importantly here's an example (in full Java 7):

Observable.from(dishes)
    .flatMap(new Func1<Dish, Observable<OrderLine>>() {
        @Override
        public Observable<OrderLine> call(Dish dish) {
            List<OrderLine> orderLines = new ArrayList<>();
            orderLines.add(new OrderLine(dish.name, dish.price);

            for (Topping topping : dish.toppings) {
                orderLines.add(new OrderLine(topping.name, topping.price);
            }

            if(dish.note != null) {
                orderLines.add(new OrderLine(dish.note, "");
            }
            return Observable.from(orderLines);
        }
    })
    .subscribe(new Action1<OrderLine>() {
        @Override
        public void call(OrderLine orderLine) {
            ...
        }
    });

You probably want to draw the Dish-to-OrderLine functionality out into another method so you can test it, which would make this much briefer.