RXJS Operators for Nested Arrays

Viewed 1036

I have a output like below from an observable. I want to transform this output into a string format as for all true records "John~Ford,Fiat". I want to acheive this using RXJS operators. Can someone show me how can i get to this.

[{
  "name":"John",
  "age":30,
  "selected" : true,
  "cars": [
      { "selected":"true",  "name":"Ford", "models": "Mustang"},
      { "selected":"false", "name":"BMW", "models": "320" },
      { "selected":"true", "name":"Fiat", "models":"500" }
  ]
 },
 {
  "name":"alex",
  "age":40,
  "selected" : false
  "cars": [
      { "selected":"true",  "name":"tesla", "models": "x"},
      { "selected":"false", "name":"merc", "models": "300" },
      { "selected":"true", "name":"honda", "models":"accord" }
   ]
  }
]

I am able to filter the records but not sure how to format the to the string output("John~Ford,Fiat".)

myComp.myService.carOptions$.pipe(
      take(1),
      flatMap(carOptions => carOptions as options[]),
      filter(carOption => carOption.selected === true),
      flatMap(options => options.values as values[]),
      filter(optionValue => optionValue.isSelected === true),
      toArray()
);
2 Answers

This isn't an RxJS problem, it's a javascript array problem.

The filter RxJS operator guards the pipe and only lets values through that meet its criteria. It doesn't filter out items within the values that come through.

The take filter you have in there at the moment will only let the first value through and no more. You may not want to keep that in there, but that's not the primary issue here.

Instead, you want to take what's coming out of the observable, and transform it into a different value. This is done inside an RxJS map operator.

myComp.myService.carOptions$.pipe(
  map((result: any): string => {
    return result
      .filter(x => x.selected)
      .map(x => {
        const cars = x.cars
          .filter(x => x.selected)
          .map(x => x.name)
          .join(',');
        return `${x.name}~${cars}`;
      }).join('|');
  })
).subscribe(result => {      
  console.log(result);
});

That map inside the function is transforming the array in your question to an array of strings matching your criteria.

Firstly it only keeps the selected top-level objects. Then it maps each object to a string. That string is a concatenation of "name" "~" and a joined list of car names where selected is true.

I have assumed that car.selected should be a boolean type. You will need to amend the check if it is actually a string type.

DEMO: https://stackblitz.com/edit/angular-snatak

https://stackblitz.com/edit/angular-rxjs-filter-problem

  ngOnInit() {
    of(values)
      .pipe(
        map(items => items.filter(item => item.selected)),
        map(items =>
          items.map(item => {
            const cars = item.cars
              .filter(car => car.selected === "true")
              .map(car => car.name)
              .join(",");
            return item.name + "~" + cars;
          })
        )
      )
      .subscribe(val => {
        console.log(val);
        this.result = val;
        this.str = val.join(" | ");
      });
  }

John~Ford,Fiat | alex~tesla,honda

Related