How to extract only certain properties of json, returning as an observable using rxjs

1.6k views Asked by At

For example this is the json which I am receiving,

{
    "events": [...
    ],
    "total": 12341,
    "students": [
        {
            "id": 1,
            "first_name": "John",
            "last_name": "Apple"
        },
        {
            "id": 2,
            "first_name": "Bob",
            "last_name": "Banana"
        },
        {
            "id": 3,
            "first_name": "Charles",
            "last_name": "Carrot"
        }
    ]
}

And I want to transform the data to the following form, and return it as an observable

[
    {
        "first_name": "John",
        "last_name": "Apple"
    },
    {
        "first_name": "Bob",
        "last_name": "Banana"
    },
    {
        "first_name": "Charles",
        "last_name": "Carrot"
    }
]

I have tried the following, but it returns undefined.

getStudentsName(): Observable<any> {
    const requestUrl = this.rootURL + `students/`;
    let studentsInfo = this.http.get<any>(requestUrl).pipe(map(o => o.students));
    return studentsInfo.pipe(map(students => {students.first_name, students.last_name}));
  }

returns undefined when subscribing to observable

this.getStudentsInfoService.getStudentsName()
      .subscribe((result) => console.log('here', result));
4

There are 4 answers

0
Owen Kelvin On

The Problem

The Problem is with your how you return your observable

Lets break the code down

let studentsInfo = this.http.get<any>(requestUrl).pipe(map(o => o.students));

In the above studentsInfo will be of type Observable<any>

Next line is as per below

return studentsInfo.pipe(
  map(students => {
    students.first_name, students.last_name
  }
));

Lets have a look at the below section

{
  students.first_name, students.last_name
}

This part of the section actually has no return statement hence by default javascript returns undefined!

Solution

To use arrow function without a return statement, you will need to wrap {} inside a () like below

students => ({ })

Below will work

getStudentsName(): Observable<any> {
  return this.http.get<any[]>(`${this.routeURL}/students`).pipe(
    map(o => o.students));
  }
0
MoxxiManagarm On

Your problem is, that students is an array, but you handle it as an object. You need to add a nested map: 1 for rxjs, 1 for the array

return studentsInfo.pipe(
  map(studentsArray => studentsArray.map(student => ({
    first_name: student.first_name,
    last_name: student.last_name
  }))),
);

PS.: Using types instead of any would have shown you that. Neither you nor the other responders saw this issue due to missing typing.

10
Hassan On

It looks like it can't find students. Try this :

return studentsInfo.pipe(map(s => { return {
  first_name: s.first_name,
  last_name: s.last_name,
}}));
1
okihnjo On

Here is a short code snippet.

const object = {
    "total": 12341,
    "students": [
        {
            "id": 1,
            "first_name": "John",
            "last_name": "Apple"
        },
        {
            "id": 2,
            "first_name": "Bob",
            "last_name": "Banana"
        },
        {
            "id": 3,
            "first_name": "Charles",
            "last_name": "Carrot"
        }
    ]
}

let arr: any = [];
object.students.forEach(person => arr.push(
    {
        "first_name": person.first_name,
        "last_name": person.last_name
    }))

console.log(arr)


    [LOG]: [{ "first_name": "John", "last_name": "Apple" }, 
{ "first_name": "Bob", "last_name": "Banana" }, { "first_name": "Charles", "last_name": "Carrot" }] 

You can iterate over students with a foreach loop and then create a new json which you then push in your new array

this.http.get<any>(requestUrl).pipe(map(o => o.students.foreach(person => arr.push({
...}));