3

I would like to understand the .distinct operator in my use-case:

I do a search for a country via user input and want to show only one object with a specific content in a property called country.

Explanation:

I have a BehaviorSubject with the content of various objects:

[
 {id:1, country: "United Kingdom", city:"London"},
 {id:2, country: "United Kingdom", city:"Manchester"},
 {id:3, country: "Germany", city:"Berlin"},
 ...
]

The type of the array is for example loc[]:

interface loc {
  id: number;
  country: string;
  city: string;
}

This is the filtering via user input (called 'query' in the code below):

BehaviorSubject
   .map(x => x.filter((l) => 
     l.country.toLowerCase().indexOf(query.toLowerCase()) > -1))

If the user input is 'United' I get a result array with two objects.

To get only one object I used another .map to handle the duplicates (Standard js code to remove duplicates from an array) and return an array with only one object.

  1. How do I remove the duplicates in the array with .distinct?
  2. If you look at the first .map the type of x is loc[]. How do I get the items of the array in the .map operator and not the array type?

Thanks in advance

1 Answer 1

3

Like with everything in rxjs: There are tons of ways, how to do this - this is how I would do it:

About the distinct: As described in the docs, it takes an optional keySelector-function as the first parameter, where you can return the country:

.distinct(entry => entry.country)

Here is the full example:

const allEntries$ = Rx.Observable.from([
 {id:1, country: "United Kingdom", city:"London"},
 {id:2, country: "United Kingdom", city:"Manchester"},
 {id:3, country: "Germany", city:"Berlin"}
])
  .publishReplay()
  .refCount();

const userInput$ = new Rx.ReplaySubject("");
// just for demonstration-purposes
userInput$.subscribe(input => console.log(">> Userinput: " + input));

// this is the important block
const result$ = userInput$
  .switchMap(input => allEntries$
    .filter(forCountry(input))
    .distinct(byCountry)
  );

// some helper for the filter, you could also do this inline, but it reads better this way
function forCountry(country) {
  country = country || "";
  coutnry = country.toLowerCase();
  return entry => entry.country.toLowerCase().indexOf(country) >= 0;
}

// some helper for the distinct, you could also do this inline, but it reads better this way
function byCountry(entry) {
  return entry.country;
}

// --- Simulation start
result$.subscribe(result => console.log(">>>> Result: " + result.city)); // activate the stream

userInput$.next("united");
userInput$.next("germ");
userInput$.next("e");
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

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

2 Comments

Thanks for the answer. How would you handle your example when you have an BehaviorSubject<ArrayofObjects> instead of the Observable.of<ArrayofObjects>?
That should make no difference - the only thing is, that the distinct might behave differently if that BehaviorSubject changes more often then the userInput - but that could be fixed with adding and additional level in the current switchMap.

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.