6

I am retrieving a document from PouchDB in an Angular Service. The document is retrieved in the following format:

{
"_id":"segments",
"_rev":"1-4f0ed65cde23fe724db13bea1ae3bb13",
"segments":[
    { "name":"Aerospace" },
    { "name":"Auto repair" },
    { "name":"Commercial" },
    { "name":"Education" },
    { "name":"Energy" },
    { "name":"Farm/ranch" },
    { "name":"Furniture" },
    { "name":"Heavy Equipment" },
    { "name":"Hobbyist" },
    { "name":"Infrastructure" },
    { "name":"Luxury/Leisure" },
    { "name":"Military" },
    { "name":"MUP" },
    { "name":"Processing" },
    { "name":"Rail" },
    { "name":"Transportation" }
]}

And I want to map that to a new Array that would look like:

[
  { value: "Aerospace", viewValue: "Aerospace" },
  { value: "Auto Repair", viewValue: "Auto Repair" },
  { value: "Commercial", viewValue: "Commercial" }
  ...
 ]

To accomplish this, I have tried this code in my Service:

getSegments(): Observable<any[]> {
  return from(this.database.get('segments'))
  .pipe(
    map((results) => results.segments)
  );
}

And I transform the array in my Component like this:

segments: SegmentsLookup[] = [];
...
this.lookupDbService.getSegments()
  .subscribe(data => {
    data.forEach(element => {
      this.segments.push({value: element.name, viewValue: element.name});
  });
});

This works but I know there is a way to map this properly back in the Service code. Also, when done this way, the compiler complains about the "results.segments" stating "Property "segments" does not exist on type '{}'.

How do I map the data retrieved to the Array that I need in the Service's "getSegments" method?

3 Answers 3

15

You can do the transformation is 2 steps:

  • pipe/map to extract the segments
  • array/map to convert to the final data type

Please see an example here: https://stackblitz.com/edit/angular-ikb2eg?file=src%2Fapp%2Fapp.component.ts

let transformedData = observableData.pipe(
  map(data => {
    console.log(data, data.segments.length);
    return data.segments.map(element => {
      return { value: element["name"], viewValue: element["name"] };
    });
  })
);

transformedData.subscribe(data => {
  this.mylist = data;
});
Sign up to request clarification or add additional context in comments.

Comments

4

You can use the RxJS operator flatMap, which is a alias of mergeMap.

The official documentation describes flatMap as follows:

Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences or Promises or array/iterable into one observable sequence.

We can use flatMap and then the RxJS operator map like this:

  • flatMap to extract the segments
  • map to convert to the final data type
const transformedData = observableData.pipe(
  flatMap(data => data.segments),
  map(segment => ({
    value: segment['name'],
    viewValue: segment['name'],
  })),
)

transformedData.subscribe(data => {
  this.mylist = data;
});

Detailed explanation of how this works:

This article nicely explains how flatMap works and implements a version of flatMap, which works with arrays rather than RxJS observables, as follows:

function flatMap (arr, fn) {
  return arr.reduce((flatArr, subArray) => flatArr.concat(fn(subArray)), [])
}

If we use this with the result of your database query we'll see we extract the segments.

const data = {
"_id": "segments",
"_rev": "1-4f0ed65cde23fe724db13bea1ae3bb13",
"segments": [
  { "name": "Aerospace" },
  { "name": "Auto repair" },
  // ...
]};

const segments = flatMap([data], x => x.segments);

console.log(segments);

// > [
// >  { "name": "Aerospace" },
// >  { "name": "Auto repair" },
// >  ...
// > ]

The RxJS flatMap operator returns an observable stream of segments, rather than an array of segments.

1 Comment

Will you need a combineAll() to convert it back to an array?
2

You can extend the map function and remove it from the component as follows :

map(result => {
        result = result.segments;
        let data = [];
        result.forEach(element => {
            data.push({value: element.name, viewValue: element.name});
        });
        return data;
 });

Comments

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.