1

I have this data structure:

[{
    id : 1,
    name : "Item 1",
    subItems : [{
            id : 1,
            name : "SubItem 1"
        },{
            id : 2,
            name : "SubItem 2"
        }
    ]
}, {
    id : 2,
    name : "Item 2",
    subItems : [{
            id : 3,
            name : "SubItem 3"
        }, {
            id : 4,
            name : "SubItem 4"
        }
    ]
}]

I make the following call to a web service to get the items: this.dataService.get("items")

Returned is an Observable<Item[]>. What Observable operators can I use to only get a concatenated list of SubItems? I would like to end up with something like this:

[{
    id : 1,
    name : "SubItem 1"
}, {
    id : 2,
    name : "SubItem 2"
},
{
    id : 3,
    name : "SubItem 3"
}, {
    id : 4,
    name : "SubItem 4"
}]

Should I use something like flatMap or concat?

6
  • Is the result (the full result) an array? It is not clear from the way you wrote it Commented Oct 31, 2016 at 17:05
  • and is it deliberate that you mix subItems and searchProfiles? Commented Oct 31, 2016 at 17:11
  • ... and is the source supposed to be an array? Commented Oct 31, 2016 at 17:12
  • @Meir It was a type, should be subItems for both of them. The result should be an array, yes. Commented Nov 1, 2016 at 6:42
  • Thought so. See my solution below, you can achieve this using the js array functions without a need for further usage rxjs Commented Nov 1, 2016 at 7:39

2 Answers 2

2

Provided it is a typo and the second element has subItems as well (and not searchProfiles), you don't need flatMap or any thing of the sort, you can do it in a plain map using js array operators:

var transformed = [].concat(...result.map(item => item.subItems));

or in your case

httpResult$.map(result => [].concat(...result.map(item => item.subItems))

if the use of different keys is deliberate, your internal map will require a bit more logic but the mapping will be quite the same

Sign up to request clarification or add additional context in comments.

Comments

1

You want to first map() operator to extract only fields you need and them flatten the arrays of objects with concatAll() (it's a little trick with higher order Observables, see Subscribing to a nested Observable for more info):

var data = [{
    id : 1,
    name : "Item 1",
    subItems : [
        { id : 1, name : "SubItem 1" },
        { id : 2, name : "SubItem 2" }
    ]
}, {
    id : 2,
    name : "Item 2",
    searchProfiles : [
        { id : 3, name : "SubItem 3" },
        { id : 4, name : "SubItem 4" }
    ]
}];

Observable.from(data)
    .map(item => {
        if (item.searchProfiles) {
            return item.searchProfiles;
        } else if (item.subItems) {
            return item.subItems
        }
    })
    .concatAll()
    .subscribe(val => console.log(val));

This prints to console:

{ id: 1, name: 'SubItem 1' }
{ id: 2, name: 'SubItem 2' }
{ id: 3, name: 'SubItem 3' }
{ id: 4, name: 'SubItem 4' }

Alternatively, if you really want the output as a single array then you can add toArray() operator between .concatAll() and .subscribe(...) and you'll receive:

[ { id: 1, name: 'SubItem 1' },
  { id: 2, name: 'SubItem 2' },
  { id: 3, name: 'SubItem 3' },
  { id: 4, name: 'SubItem 4' } ]

5 Comments

The thing that bugs me is that I in map get the entire Array with both items instead of each item, like in your example. In your case, the observable emits each item separately I guess? But I my case, the whole array is returned and emitted once, so that's what I get inside of map
@Joel That's because I'm using Observable.from(data). If you're using Observable.of(data) you'll receive it as one large array.
Ok. And then I would have to do something like .map(items => { let subItems = []; items.forEach(item => subItems.push(item.subItems)); return subItems;)? The syntax may be way off though :)
@Joel Why you need that?
If using Observable.of(data) I need to do something like that to get the result that I want. But thank you, this has helped me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.