You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按嵌套属性meta.group对JavaScript对象数组进行分组

按嵌套属性meta.group分组对象数组

问题背景

从API接口获取到Directus系统的字段配置对象数组,原始数据结构如下:

[
    {
        "__typename": "directus_fields",
        "field": "heading",
        "type": "string",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": 255,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "hero",
            "validation_message": null,
            "id": 37,
            "required": false,
            "validation": null,
            "interface": "input",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "introduction",
        "type": "text",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": null,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "about_us",
            "validation_message": null,
            "id": 38,
            "required": false,
            "validation": null,
            "interface": "input-rich-text-html",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "campaign_title",
        "type": "string",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": 255,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "campaign",
            "validation_message": null,
            "id": 41,
            "required": false,
            "validation": null,
            "interface": "input",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "campaign_duration",
        "type": "string",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": 255,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "campaign",
            "validation_message": null,
            "id": 42,
            "required": false,
            "validation": null,
            "interface": "input",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "campaign_text",
        "type": "text",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": null,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "campaign",
            "validation_message": null,
            "id": 43,
            "required": false,
            "validation": null,
            "interface": "input-rich-text-html",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "campaign_image",
        "type": "uuid",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": 36,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "campaign",
            "validation_message": null,
            "id": 44,
            "required": false,
            "validation": null,
            "interface": "file-image",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "philosophy_title",
        "type": "string",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": 255,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "philosophy",
            "validation_message": null,
            "id": 45,
            "required": false,
            "validation": null,
            "interface": "input",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "philosophy_text",
        "type": "text",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": null,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "philosophy",
            "validation_message": null,
            "id": 46,
            "required": false,
            "validation": null,
            "interface": "input-rich-text-html",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "title",
        "type": "string",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": 255,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "about_us",
            "validation_message": null,
            "id": 52,
            "required": false,
            "validation": null,
            "interface": "input",
            "hidden": false
        }
    },
    {
        "__typename": "directus_fields",
        "field": "hero_text",
        "type": "text",
        "schema": {
            "__typename": "directus_fields_schema",
            "max_length": null,
            "is_unique": false,
            "is_nullable": true
        },
        "meta": {
            "__typename": "directus_fields_meta",
            "group": "hero",
            "validation_message": null,
            "id": 164,
            "required": false,
            "validation": null,
            "interface": "input-rich-text-html",
            "hidden": false
        }
    }
]

需求为将数组内所有元素按照meta.group的取值分组,相同group值的元素归集到同一分组下。
原有实现为扁平数组转树形菜单的逻辑,无法满足分组需求,错误代码如下:

const tree = [];
    const children = {};
    data.forEach((item) => {
        const newItem = { ...item } as ParsedMenuItem;
        const { id: newItem?.meta?.group, parentId: parentId = 0 } = newItem;
        children[id] = children[id] || [];
        newItem.childMenu = children[id];
        parentId
            ? (children[parentId] = children[parentId] || []).push(newItem)
            : tree.push(newItem);
    });
    return tree;

错误点说明

  • 解构赋值语法错误:解构模式中不支持newItem?.meta?.group这类可选链写法,无法正确读取嵌套的group属性
  • 逻辑不匹配:原有代码是基于id/parentId关联关系构建层级树,和当前按固定属性值聚合分组的需求完全不符
  • 未处理嵌套属性取值:没有正确定位到分组依据的meta.group字段作为聚合键

实现方案

使用reduce方法做聚合遍历,直接读取每个元素的meta.group值作为分组键即可,代码简洁且兼容性好:

function groupByMetaGroup(data) {
  return data.reduce((result, currentItem) => {
    const groupName = currentItem.meta.group;
    // 初始化对应分组的数组
    result[groupName] ??= [];
    result[groupName].push(currentItem);
    return result;
  }, {});
}

// 调用方法
const groupedData = groupByMetaGroup(apiResponseData);

注:如果需要兼容不支持空值合并运算符(??=)的旧环境,可以将result[groupName] ??= []替换为result[groupName] = result[groupName] || [],效果完全一致。

如果业务场景需要数组格式的分组结果(用于前端列表渲染等场景),可以将对象结果转为数组结构:

const groupedList = Object.entries(groupedData).map(([groupKey, fieldList]) => ({
  groupName: groupKey,
  fields: fieldList
}));

输出结果

针对提供的测试数据,最终分组结果结构如下:

{
  "hero": [heading字段对象, hero_text字段对象],
  "about_us": [introduction字段对象, title字段对象],
  "campaign": [campaign_title、campaign_duration、campaign_text、campaign_image四个字段对象],
  "philosophy": [philosophy_title、philosophy_text字段对象]
}

内容的提问来源于stack exchange,提问作者Chinomso Johnson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 17:01:03