0

I am new to Angular and RXJS - I'm trying to return normalized data from a rest api and assemble the hierarchy on the client. I created this stack blitz which is a really basic version of what I'm trying to accomplish: https://stackblitz.com/edit/angular-ffdbza

These interfaces ( ParentData, AssnData, and ChildData ) represent the JSON data being returned from the API. The Parent and Child interfaces are the way I want the data to be represented on the client (in the real app I will bind this new object to a hierarchical grid). The key points are that the Assn data has an attribute (statusCode) that needs to be applied to each Child based on the Parent.

// represents normalized data coming from the service
export interface ParentData {
  parentCode: string, 
  name: string
}

export interface AssnData {
  parentCode: string
  childId: number,
  statusCode: string
}

export interface ChildData {
  childId: number,
  type: string
}

// represents the merged data for display 
export interface Parent {
  parentCode: string,
  name: string
  kids: Child[]
}

export interface Child {
  childId: number
  type: string,
  statusCode: string
}

This is the code I have so far (grabbed from data.component.ts in the stack blitz). It is adding the Assn objects to the correct parent but I'm having trouble merging the Child object with each Assn object. I am doing a console.log to see the results.

  getRelationalData() {
    let x = combineLatest(
      this.parentData$,
      this.assnData$,
      this.childData$
    ).pipe(
      map(([pData, aData, cData]) => {
        return pData.map(p => {
          return {
            ...p,
            kids: aData.filter(a => a.parentCode === p.parentCode)
          }
        })
      })
    )
    return x;
  }

1 Answers1

0

I have a new stack blitz that is working but it doesn't seem like the most elegant code: https://stackblitz.com/edit/angular-rg6q5d?file=src/app/data.component.ts

I changed the output as well to show the before/after JSON on the screen to make it a bit easier to see what I'm trying to do (hopefully). Can anyone provide insight into making this better?

getRelationalData() {
    let x = combineLatest(
      this.parentData$,
      this.assnData$,
      this.childData$
    ).pipe(
      map(([pData, aData, cData]) => {
        return pData.map(p => {
          // get assn objects for this parent
          let assn = aData.filter(a => a.parentCode === p.parentCode);

          // get kids for this parent based on assn object (filter undefined)
          let kids = cData.map(c => this.mergeData(c, assn))
            .filter(k => k !== undefined ) ;

          // return a new parent with the newly created child objects
          return {
            ...p,
            kids: kids
          }
        })
      })
      //, tap(x => console.log(x))
    )
    return x;
  }

  mergeData(child, assnObs){
    // filter the assn objects for each child
    let assn = assnObs.find(a => a.childId === child.id);

    // return undefined so it can be filtered later
    if (assn === undefined){
      return assn;
    }

    //return the child object with the appropriate statusCode
    return Object.assign(
      {},
      child,
      {
        statusCode: assn.statusCode
      }
    )
  }

Resulting JSON - the parent and child objects are correctly associated with each other and each child has the appropriate statusCode based on the parent:

[
  {
    "parentCode": "abc",
    "name": "John",
    "kids": [
      {
        "id": "123",
        "name": "Billy",
        "age": "10",
        "statusCode": "tallest"
      },
      {
        "id": "789",
        "name": "Sue",
        "age": "6",
        "statusCode": "youngest"
      }
    ]
  },
  {
    "parentCode": "xyz",
    "name": "Jane",
    "kids": [
      {
        "id": "456",
        "name": "Bobby",
        "age": "8",
        "statusCode": "something"
      },
      {
        "id": "789",
        "name": "Sue",
        "age": "6",
        "statusCode": "whatever"
      }
    ]
  }
]