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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.