Ne0kropolis
Ne0kropolis

Reputation: 21

AngularTS recursive filter on nested array

I have a problem which requires a function that can recursively traverse a nested array of objects and filter elements out based on whether or not they are present in a list of views.

For example, given a list:

[{
    "label": "Heading",
    "items": [{
      "label": "Subheading",
      "items": [{
          "id": "item1_priv",
          "label": "item1"
        },
        {
          "id": "item_priv2",
          "label": "item2"
        },
        {
          "label": "Subheading",
          "items": [{
            "id": "item_priv3",
            "label": "item3"
          }]
        }
      ]
    }]
  },
  {
    "label": "Heading2",
    "items": [{
      "label": "Subheading",
      "items": [{
          "id": "item_priv4",
          "label": "item4"
        },
        {
          "id": "item_priv5",
          "label": "item5"
        }
      ]
    }]
  }
]

and a list of views:

canView = ['item1','item2','item4','item5']

The function needs to filter out item3 in the list as it is not present in canView.

I have already made a function that does this:

public filterView(list: any[]): any {

  for (const item of list) {
    if (item.items) {
      item.items = this.filterView(item.items);
    } else {
      list = ((list.filter((i: any) => this.hasView(i.id))));
    }
  }

  return list;
}

However, my question is: Is it possible to rewrite this function as a lambda using list.filter()? I'm new to Angular to and Typescript so I'm having a tough time wrapping my head around using filter (or nested filters) in order to traverse the list based on whether not it possesses items.

my assumption would be a function along the lines of

list.filter(item => return(item.items && this.hasView(item.items.id)))

but this doesn't seem to work on a list with dynamic depth. Any advice would be appreciated as other solutions to similar problems I've seen have only had lists with fixed depths.

Upvotes: 1

Views: 944

Answers (1)

Matt McCutchen
Matt McCutchen

Reputation: 30939

The only change I would suggest is to do the list filtering once rather than inside the loop:

public filterView(list: any[]): any {

  for (const item of list) {
    if (item.items) {
      item.items = this.filterView(item.items);
    }
  }

  return list.filter((i: any) => this.hasView(i.id));
}

Beyond that, given that filterView is doing two different things (filtering the top-level list and mutating the child lists of the descendants), it seems to me that any more concise way of writing it is going to be harder to understand.

Upvotes: 1

Related Questions