1

I must iterate over array, find correspondent objects in other array an merge the result in a object.

Assume I have three arrays

var users = [
    { name: "A", type: 2, level: 1 },
    { name: "B", type: 1, level: 2 }
]
var types = [
    { description: "Type 1", id: 1 },
    { description: "Type 2", id: 2 }
]
var levels = [
    { description: "Level 1", id: 1 },
    { description: "Level 2", id: 1 }
]

I want to have following result:

var users = [
    { name: "A", type: 2, level: 1, levelDescription: "Level 1", typeDescription: "Type 2" },
    { name: "B", type: 1, level: 2, levelDescription: "Level 2", typeDescription: "Type 1" }
]

I know I can achieve it like that

var usersObservable = RX.Observable.fromArray(users);
var typesObservable = Rx.Observable.fromArray(types);
var levelsOBservable = Rx.Observable.fromArray(levels);

var uiUsers= [];// not really needed because I will use the same users array again.

usersObservable.map(function(user) {
      typesObservable.filter(function(type) {
        return type.id == user.type;
    }).subscribeOnNext(function(userType) {
        user.typeDescription = userType.description;
    });
    return user;
}).map(function(user) {
       levelsOBservable.filter(function(level) {
        return level.id == user.levelId;
    }).subscribeOnNext(function(level) {
        user.levelDescription = level.description;
    });
    return user;
})
.subscribeOnNext(function(user) {
    uiUsers.push(user);
})

I would like to have a solution without nested Observables.
Thanks.

2 Answers2

1

I am not sure why you are using Rx at all for this problem. You have data in space (i.e. arrays), not data over time (i.e. an observable sequence). But you force these arrays into Rx to then create a very complicated solution.

I think you are looking for something like the answer here https://stackoverflow.com/a/17500836/393615 where you would join the source array types. In your case you just "inner-join" twice to combine all three data sets.

Community
  • 1
  • 1
Lee Campbell
  • 10,631
  • 1
  • 34
  • 29
0

You can archive this by using the switchMap operator that combines the result of a filtered stream with the latest value of the original stream and uses a projection function to merge the results into a single object. This can be generalised in your example such that you can use a generic higher order function in both cases. See fiddle.

Full code (ES2015, RxJS5):

const users = [
    { name: "A", type: 2, level: 1 },
    { name: "B", type: 1, level: 2 }
];
const types = [
    { description: "Type 1", id: 1 },
    { description: "Type 2", id: 2 }
];
const levels = [
    { description: "Level 1", id: 1 },
    { description: "Level 2", id: 2 }
];

const users$ = Rx.Observable.from(users);
const types$ = Rx.Observable.from(types);
const levels$ = Rx.Observable.from(levels);

function join(s$, sourceProperty, targetProperty, streamProperty) {
    return function(initObj) {
    const stream$ = s$.filter(x => x.id === initObj[sourceProperty]);
    return Rx.Observable.combineLatest(
      Rx.Observable.of(initObj),
      stream$,
      (obj, streamObj) => {
        const prop = streamObj[streamProperty];
        return Object.assign({}, obj, { [targetProperty]: prop });
      } 
    );
  };
}

users$
  .switchMap(join(types$, 'type', 'typeDescription', 'description'))
  .switchMap(join(levels$, 'level', 'levelDescription', 'description'))
  .subscribe(x => console.log(x));
dotcs
  • 2,286
  • 1
  • 18
  • 26