RxJS: JSON data with an array, processing each item further in the stream

会有一股神秘感。 提交于 2019-11-28 05:57:46

问题


I receive a HTTP response, which contains, if all goes well, a single array, coded as JSON.
I want to get this array, filter out some items, and process the passing items as events.

What I do so far is:

    return this._http.get(url)
        .map((res:Response) => res.json())
        .map((data:any) => {
            if (!Array.isArray(data) || data.length == 0) {
                throw new Error("No items returned, URL: " + url);
            }
            let projects = <ProjectModel[]>service.fromJSONarray(data, this._http);
            return Observable.from(projects)
                .filter((project: ProjectModel) => project.parentProject == null)
                .subscribe(project -> ...)
        })

However I don't like the nesting. I assume there's a way to do this:

    return this._http.get(url)
        .map((res:Response) => res.json())
        .map((data:any) => {
            ...
            let projects = <ProjectModel[]>service.fromJSONarray(data, this._http);
            ???
        })
        .filter((project: ProjectModel) => project.parentProject == null)
        .subscribe(project -> ...)

How to achieve that?


回答1:


Assuming you're using RxJS 5 there's even easier way with concatAll() and its undocumented feature for flattening nested arrays even though concatAll() is made to work with Higher-Order Observables.

let data = '[{"id":1},{"id":2},{"id":3},{"id":4}]';

Observable.of(data)
    .map(JSON.parse)
    .concatAll() // each object is emitted separately
    .map(p => p) // transform to ProjectModel
    .filter(p => true) // filter whatever you want
    .subscribe(v => console.log(v));

If service.fromJSONarray returns just a single instance of ProjectModel it can be this simple. If you need service.fromJSONarray to return an array you'd put it between map(JSON.parse) and concatAll(). This demo prints to console:

{ id: 1 }
{ id: 2 }
{ id: 3 }
{ id: 4 }

However, if you were returning an Observable from service.fromJSONarray that emits other observables because it needs to do some extra HTTP requests you'd need to use concatMap() or flatMap() depending on whether you want to keep the same order of items or not:

let data = '[{"id":1},{"id":2},{"id":3},{"id":4}]';

function fromJSONarray(arr) {
    return Observable.from(arr)
        .flatMap(p => Observable.of(p));
}

Observable.of(data)
    .map(d => fromJSONarray(JSON.parse(d)))
    .concatAll()
    .map(p => p) // transform to ProjectModel
    .subscribe(v => console.log(v));

The result is the same.

Similar questions:

  • Subscribing to a nested Observable

  • Merge subarrays using Observables

  • RxJS Refactor nested map statement




回答2:


Any time you're mapping to an array of Observables, you should be thinking flatMap!

In your example, that looks like:

return this._http.get(url)
    .map((res:Response) => res.json())
    .flatMap((data:any) => {
        if (!Array.isArray(data) || data.length == 0) {
            throw new Error("No items returned, URL: " + url);
        }
        let projects = <ProjectModel[]>service.fromJSONarray(data, this._http);
        return Observable.from(projects);
    })
    .filter((project: ProjectModel) => project.parentProject == null)
    //...etc

Or, more simply

Observable
  .range(0, 2)
  .flatMap(() => Observable.from([1, 2, 3]))
  .filter((foo) => foo !== 1)
  .subscribe((res) => console.log(res));

// 2
// 2
// 3
// 3


来源:https://stackoverflow.com/questions/41092488/rxjs-json-data-with-an-array-processing-each-item-further-in-the-stream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!