1

This is more a logical problem then a RxJS problem, I guess, but I do not get it how to solve it.

[input 1] From a cities stream, I will receive 1 or 2 objects (cities1 or cities2 are test fixtures).
1 object if their is only one language available, 2 objects for a city with both languages.

[input 2] I do also have a selectedLanguage ("fr" or "nl")

[algo] If the language of the object corresponds the selectedLanguage, I will pluck the city. This works for my RxJS when I receive 2 objects (cities2) But since I also can receive 1 object, the filter is not the right thing to do

[question] Should I check the cities stream FIRST if only one object exists and add another object. Or what are better RxJS/logical options?

const cities1 = [
    {city: "LEUVEN", language: "nl"}
];

const cities2 = [
    {city: "BRUSSEL", language: "nl"},
    {city: "BRUXELLES", language: "fr"}
    ];

const selectedLang = "fr"
const source$ = from(cities1);
const result = source$.pipe(
    mergeMap((city) => {
        return of(selectedLang).pipe(
            map(lang => {
                return {
                    lang: city.language,
                    city: city.city,
                    selectedLang: lang
                }
            }),
            filter(a => a.lang === selectedLang),
            pluck('city')
        )
    }
    )
);
result.subscribe(console.log)

Jonas
  • 23
  • 5
  • Since you are talking about streams, how does your program know that after receiving `{city: "LEUVEN", language: "nl"}` there will not be another notification for the "fr" language? Also, how does the program know that the 2 subsequent notifications `{city: "BRUSSEL", language: "nl"}` and `{city: "BRUXELLES", language: "fr"}` refer to the same city and not 2 different cities? – Picci Jan 19 '21 at 09:11
  • No answers in the question, please. I have rolled back/edited your question and removed the answer. Add the answer in the answer section only. – Sabito stands with Ukraine Jan 20 '21 at 12:28

2 Answers2

0

If selectedLang is not an observable (i.e. you don't want this to change) then I think it would make it way easier if you keep it as a value:

const result = source$.pipe(
   filter(city => city.language === selectedLang)
   map(city => city.city)
);

There's nothing wrong from using external parameters, and it makes the stream easier to read.

Now, if selectedLang is an observable, and you want result to always give the city with that selectedLang, then you probably need to combine both streams, while keeping all the cities received so far:

const selectedLang$ = of(selectedLang); // This is actually a stream that can change value
const cities$ = source$.pipe(
   scan((acc, city) => [...acc, city], [])
);

const result = combineLatest([selectedLang$, cities$]).pipe(
   map(([selectedLang, cities]) => cities.find(city => city.language == selectedLang)),
   filter(found => Boolean(found))
   map(city => city.city)
)

Edit: note that this result will emit every time cities$ or selectedLang$ changes and one of the cities matches. If you don't want repeats, you can use the distinctUntilChanged() operator - Probably this could be optimised using an exhaustMap or something, but it makes it harder to read IMO.

olivarra1
  • 3,269
  • 3
  • 23
  • 34
0

Thanks for your repsonse. It's great value for me. Indeed I will forget about the selectedLang$ and pass it like a regular string. Problem 1 solved

I'll explain a bit more in detail my question. My observable$ cities$ in fact is a GET and will always return 1 or 2 two rows.

leuven:
[ { city: 'LEUVEN', language: 'nl', selectedLanguage: 'fr' } ]

brussel:
[
  { city: 'BRUSSEL', language: 'nl', selectedLanguage: 'fr' },
  { city: 'BRUXELLES', language: 'fr', selectedLanguage: 'fr' }
]

In case it returns two rows I will be able to filter out the right value

filter(city => city.language === selectedLang) => BRUXELLES when selectedLangue is "fr"

But in case I only receive one row, I should always return this city.

What is the best solution to this without using if statements? I've been trying to work with object destruct and scaning the array but the result is always one record.

// HTTP get
const leuven: City[] = [ {city: "LEUVEN", language: "nl"} ];

// same HTTP get
const brussel: City[] = [ {city: "BRUSSEL", language: "nl"},
                          {city: "BRUXELLES", language: "fr"}
                        ];

mapp(of(brussel), "fr").subscribe(console.log);

function mapp(cities$: Observable<City[]>, selectedLanguage: string): Observable<any> {
    return cities$.pipe(
        map(cities => {
            return cities.map(city => { return {...city, "selectedLanguage": selectedLanguage }}
            )
        }),
        // scan((acc, value) => [...acc, { ...value, selectedLanguage} ])
    )
}
Jonas
  • 23
  • 5