从打字稿中的嵌套对象数组中过滤项目

时间:2018-06-22 18:07:01

标签: javascript angular primeng

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

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);

但是我无法弄清楚如何嵌套另一个过滤器,映射或查找...不确定如何..以便能够访问嵌套项并检查其标签中的未定义项并将其删除!< / p>

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组件,您必须在打字稿文件中定义结构,由于这不是我的要求,因此我无法脱离它。我也无法从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) },

希望能够删除最嵌套的内部项目对象的主要原因是,即使您将其可见,Angular仍会在菜单中呈现空白点:false。这使菜单看起来非常非常丑陋且分散。

感谢到目前为止的所有建议并帮助弥补了我的一些知识空白,我将在明天尝试实施所有建议的想法并进行报告。希望我的编辑内容更加清晰。

编辑: 我今天要签约,这是我最近的尝试,但没有成功:

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分: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 :(得分:1)

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

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
}