【问题标题】:Filtering item from nested object array in typescript从打字稿中的嵌套对象数组中过滤项目
【发布时间】:2018-06-22 18:07:00
【问题描述】:

我已经找了一段时间的例子,但我找不到任何类似的东西,而且我对过滤器功能的理解也很缺乏,任何帮助都非常非常感谢。我正在尝试从嵌套项目标签 === 未定义的对象中删除元素。

this.items = [ // main MenuItem[] object (contains entire menu)
  {
    label: this.headingLabel, visible: this.booleanPipe.transform(this.labelVisible),
    items: [ // headings of the menu sit in navbar, a MenuItem[][] two dimensional array
      [
        {
          label: this.subMenuLabel, visible: this.booleanPipe.transform(this.labelVisible), separator: true,
          items: [ // items that need to be removed if they are undefined, another MenuItem[] object, potentially need to remove only some of its elements
            { label: this.subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: this.subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: undefined, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: this.subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: this.subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: undefined, visible: this.booleanPipe.transform(this.labelVisible) }
          ]
        },
      ],
      [
        {
          label: subMenuLabel, visible: this.booleanPipe.transform(this.labelVisible), separator: true,
          items: [
            { label: subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) },
            { label: subMenuItemLabel, visible: this.booleanPipe.transform(this.labelVisible) }
          ]
        }
      ]
    ]
  },
  ...

我能够从主对象中删除标签未定义的元素:

        this.items = this.items.filter(obj => obj.label !== undefined);

但我无法弄清楚如何嵌套另一个过滤器、映射或查找......不太确定如何......能够访问嵌套项目并检查它们的标签是否未定义并删除它们!

EDIT1: this.items 对象是一个 MenuItem[] 数组。 MenuItem 是一个 PrimeNG 类。它有如下定义:

export interface MenuItem {
    label?: string;
    icon?: string;
    command?: (event?: any) => void;
    url?: string;
    routerLink?: any;
    queryParams?: { [k: string]: any };
    items?: MenuItem[]|MenuItem[][];
    expanded?: boolean;
    disabled?: boolean;
    visible?: boolean;
    target?: string;
    routerLinkActiveOptions?: any;
    separator?: boolean;
    badge?: string;
    badgeStyleClass?: string;
    style?:any;
    styleClass?:string;
    title?: string;
    id?: string;
    automationId?: any;
}

为了构建 MegaMenu 组件,您必须在 typescript 文件中定义您的结构,我无法偏离这一点,因为这不是我的决定。我也无法从 PrimeNG 的库中选择另一个菜单组件。我希望我的进一步解释有所帮助,如果我一开始不够清楚,对不起。我想专注于这个问题,而不会使问题过于复杂。

this.items = [ //main MenuItem[] contains the entire menu
  {
    label: label1, visible: this.booleanPipe.transform(this.labelVisible),
    items: [ //nested MenuItem[][] contains the headings and each individual menu list for headings
      [
        {
          label: this.label2, visible: this.booleanPipe.transform(this.labelVisible), separator: true,
          items: [ //another MenuItem[] (contains dropdowns, subheadings etc)
            { label: this.label3, visible: this.booleanPipe.transform(this.labelVisible) },

希望能够移除最内层嵌套的 items 对象的主要原因是,即使您使 Angular 可见,它仍然会在菜单中呈现一个空白点:false。这使得菜单看起来非常非常丑陋和支离破碎。

感谢您迄今为止提出的所有建议并帮助弥补我在知识上的一些差距,我将在明天尝试实施所有建议的想法并进行报告。我希望我的修改能更清晰。

编辑: 我今天要签收,最近一次尝试过,但没有成功:

   for (let item of this.items) {
      for (let subItem of item.items) {
        subItem = (subItem as (MenuItem)[]).filter(obj => obj.label !== undefined);
      }
    }

【问题讨论】:

  • 这是最大深度,还是数组嵌套更深?
  • @bambam 这是最大深度,只是更长,我省略了一些长度以免多余。

标签: javascript angular primeng


【解决方案1】:

像这样使用递归调用:

    private filterItems(items:Array){
        return this.items.filter(obj => obj.label !== undefined)
           .forEach(item=> if(item.items) {item.items=this.filterArray(item.items)};
    }

第一次拨打items=this.filterItems(items)

这可能需要一些修复,因为我在没有编译器帮助的情况下在这里写了它,但它应该会给你一个非常好的开始。

【讨论】:

    【解决方案2】:

    使用reduce 创建包含所有元素的单个列表,然后进行过滤。

    【讨论】:

    • 我需要结构保持原样,因为 PrimeNG MegaMenu 使用它来创建菜单。数据正在动态加载,我需要在加载菜单数据后删除未定义的元素,以免它们在菜单中显示为空白。
    • 然后当items存在时使用递归调用。
    【解决方案3】:

    我们很清楚,您的数据如下所示:

    interface Item {
        label?: string;
        visible: boolean;
    }
    interface MiddleItem {
        label?: string;
        visible: boolean;
        separator: boolean;
        items?: Item[];
    }
    interface TopItem {
        label?: string;
        visible: boolean;
        items?: MiddleItem[][];
    }
    interface Scope {
        items?: TopItem[];
    }
    

    根据您的示例代码,您想要修改原始数据。这使得算法稍微简单一些(或者,您可以先进行深度复制,然后再执行此操作)

    一步一步,你想:

    • 检查标签是否未定义

    • 如果是,请删除该条目

    • 如果不是,则以“相同”方式处理子项

    如果您只有一个界面,递归地执行此操作将非常容易,但是您的子项都是不同的签名,并且您知道自己的深度,所以让我们把它拼出来,而不是做任何花哨的事情。

    从入口点开始:

    processScope(this);
    
    function processScope(scope: Scope) {
        if(!scope.items) return;
        scope.items = scope.items.filter(processTop); // basically your initial start
    }
    
    // this function gets called for each array entry in top.items
    // normally you see filter as using just the simplified boolean test function
    // but here we are going to have it preform a side effect - modifying the contents!
    // splitting this out to its own named function makes it easier to read / document
    function processTop(top: TopItem): boolean {
        if (!top.label) return false; // do not include in filtered array
    
        if (top.items) {
            top.items = top.items
                .map(processMiddleArr)  // process each subarray
                .filter(middleArr => middleArr.length !== 0); // map might have replaced the subarray with an empty array - filter those out
    
        return true;  // include in filtered array
    }
    
    // this gets called for each array entry in top.items
    // since top.items is a two dimensional array, this is just another array to iterate
    function processMiddleArr(middleArr: MiddleItem[]): MiddleItem[] {
        return middleArr.filter(processMiddle);
    }
    
    function processMiddle(middle: MiddleItem): boolean {
        if (middle.label === undefined) return false; // do not include in filtered array
    
        // here's another side effect, filtering the lowest level item list
        if (middle.items) middle.items = middle.items.filter(processItem);
    
        return true; // include in filtered array        
    }
    
    // broken out into its own function for consistency with the above filter calls
    // this would be fine to do inline as middle.items.filter(it => !!item.label)
    function processItem(item: Item): boolean {
        if (item.label === undefined) return false; // do not include in filtered array
    
        return true; // include in filtered array
    }
    

    【讨论】:

      猜你喜欢
      • 2020-09-28
      • 1970-01-01
      • 2020-03-22
      • 2021-08-01
      • 1970-01-01
      • 2021-06-28
      • 2016-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多