javascript - 按对象数组javascript中的多个键进行分组

标签 javascript arrays object grouping

我有一个嵌套的对象数组,如下所示。

[
    {
        "region": null,
        "country": null,
        "territory": "Worldwide",
        "territoryCode": "ALL",
        "t2": null,
        "t3": null,
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 1,
            "localLanguageName": "N/A",
            "localLanguageCode": null
        }
    },
    {
        "region": "Africa",
        "country": "Madagascar",
        "territory": null,
        "territoryCode": "MG",
        "t2": "AFR",
        "t3": "MG",
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 30,
            "localLanguageName": "Malagasy, French",
            "localLanguageCode": "MLG, FRE"
        }
    },
    {
        "region": "Africa",
        "country": null,
        "territory": null,
        "territoryCode": "AFR",
        "t2": "AFR",
        "t3": null,
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 2,
            "localLanguageName": "N/A",
            "localLanguageCode": null
        }
    },
    {
        "region": "Africa",
        "country": "Morocco (incl. Western Sahara)",
        "territory": null,
        "territoryCode": "MA",
        "t2": "AFR",
        "t3": "MA",
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 35,
            "localLanguageName": "Arabic, French",
            "localLanguageCode": "ARA, FRE"
        }
    },
    {
        "region": "Africa",
        "country": "Morocco (incl. Western Sahara)",
        "territory": "Morocco (excl. Western Sahara)",
        "territoryCode": "MAXEH",
        "t2": "AFR",
        "t3": "MA",
        "t4": "MAXEH",
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 36,
            "localLanguageName": "Arabic, French",
            "localLanguageCode": "ARA, FRE"
        }
    },
    {
        "region": "Africa",
        "country": "Morocco (incl. Western Sahara)",
        "territory": "Western Sahara",
        "territoryCode": "EH",
        "t2": "AFR",
        "t3": "MA",
        "t4": "EH",
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 37,
            "localLanguageName": "Arabic, French",
            "localLanguageCode": "ARA, FRE"
        }
    }
]

我希望根据独特的地区、国家/地区、t2-t7 组合对整个数据对象进行分组,并获得如下所示的输出

[{
  "region": "Africa",
  "country": [{
      "desc": "Madagascar",
      "t2": [{
        "id": "AFR",
        "localLanguageName": "Malagasy, French",
        "localLanguageCode": "MLG, FRE"
        "t3": [{
          "id": "MG"
        }]
      }]
    },
    {
      "desc": "Morocco (incl. Western Sahara)",
      "subTerritory": [{
        "t2": "AFR",
        "t3": [{
          "id": "MA",
          "localLanguageName": "Arabic, French",
          "localLanguageCode": "ARA, FRE"
          "t4": [{
              "id": "MAXEH",
              "localLanguageName": "Arabic, French",
              "localLanguageCode": "ARA, FRE"
              "t5": [{
                "id": ""
                  .
                  .
                  .
              }]
            },
            {
              "id": "EH",
              "localLanguageName": "Arabic, French",
              "localLanguageCode": "ARA, FRE"
              "t5": [{
                "id": ""
                  .
                  .
                  .
              }]
            }]
        }]
      }]
    }]
}]

我正在寻找对数据进行分组的最有效方法。使用 HashMap 更好吗?或者 Javascript 中的 map/reduce 方法?

我已经尝试过以下方法。它显然不完整,但经过几次迭代后我陷入了困境。

    const result = Object.values(data.reduce((key, curr) => {
        const { region, country, t2, t3, t4, t5, t6, t7 } = curr;
        if (!key[country]) {
            let obj = {};
            obj.region = region;
            obj.country = country;
            obj.t2 = [{
                id: t2,
                t3: [{
                    id: t3,
                    t4: {
                        id: t4,
                        t5: t5
                    }
                }]
            }];
            key[country] = obj;
        } else {
            key[country].t2 = key[country].t2 || [];
            const foundCountry = key[country].t2.find(x => x.desc === t2);
            if (!foundCountry) {
                key[country].t2.push({
                    id: t2,
                    t3: [{
                        id: t3,
                        t4: {
                            id: t4,
                            t5: t5
                        }
                    }]
                });
            } else {
                const tx = foundCountry.find(x => x.id === t3);
                if (!tx) {
                    foundCountry.push({
                        id: t3,
                        t4: {
                            id: t4,
                            t5: t5
                        }
                    });
                } else {
                    tx.id = t3;
                    tx.t4 = t4;
                }
            }
        }
        return key;
    }, {}));
    console.log(util.inspect(result, false, null, true))
    return result;

最佳答案

这个答案提供了两种解决方案:

  • 以关卡为中心的方法,具有处理每个关卡的自定义函数。
  • 使用给定键数组和统一结果集的快速方法。

每个级别的短路自定义功能

此方法对每个级别采用不同的 View ,并允许结束特定节点的迭代。

result = data.reduce((r, o) => {
    let p = r;
    groups.every(fn => p = fn(o, p));
    return r;
}, []);

上面的代码迭代给定的数据集并使用分组函数的数组,该函数返回 truthyfalsy值(value)。如果出现值,迭代将在此级别停止。

({ region }, target) => {                                   // 1
    if (!region) return;                                    // 2
    let temp = target.find(q => q.region === region);       // 3
    if (!temp) target.push(temp = { region, country: [] }); // 4
    return temp.country;                                    // 5
}

分组函数至少有四到五个部分。

  1. 函数签名包含源对象或通过 destructuring 的子集和一个目标。目标可以是数组或对象。

  2. 检查分组值是否存在,如果不存在则返回。这也结束了所有后续组的迭代。这一点是可选的。

  3. 搜索寻找所需群组的正确对象。

  4. 检查该组是否存在,如果不存在,则创建一个具有所需属性的新组

  5. 函数的返回值,可以是数组,也可以是对象,具体取决于下一个分组函数。

<小时/>

这种方法的主要优点是能够使用自定义结构处理不同级别,并且可以省略空/未给定的分组值。

主要缺点是每个级别都有一个功能。

const
    groups = [
        ({ region }, target) => {
            if (!region) return;
            let temp = target.find(q => q.region === region);
            if (!temp) target.push(temp = { region, country: [] });
            return temp.country;
        },
        ({ country: desc }, target) => {
            if (!desc) return;
            let temp = target.find(q => q.desc === desc);
            if (!temp) target.push(temp = { desc, t2: [] });
            return temp.t2;
        },
        ({ t2: id, localLanguage: { localLanguageName, localLanguageCode } }, target) => {
            if (!id) return;
            let temp = target.find(q => q.id === id);
            if (!temp) target.push(temp = { id, localLanguageName, localLanguageCode, t3: [] });
            return temp.t3;
        },
        ({ t3: id }, target) => {
            if (!id) return;
            let temp = target.find(q => q.id === id);
            if (!temp) target.push(temp = { id, t4: [] });
            return temp.t4;
        },
        ({ t4: id }, target) => {
            if (!id) return;
            let temp = target.find(q => q.id === id);
            if (!temp) target.push(temp = { id, t5: [] });
            return temp.t5;
        }
    ],
    data = [{ region: null, country: null, territory: "Worldwide", territoryCode: "ALL", t2: null, t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 1, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Madagascar", territory: null, territoryCode: "MG", t2: "AFR", t3: "MG", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 30, localLanguageName: "Malagasy, French", localLanguageCode: "MLG, FRE" } }, { region: "Africa", country: null, territory: null, territoryCode: "AFR", t2: "AFR", t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 2, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: null, territoryCode: "MA", t2: "AFR", t3: "MA", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 35, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Morocco (excl. Western Sahara)", territoryCode: "MAXEH", t2: "AFR", t3: "MA", t4: "MAXEH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 36, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Western Sahara", territoryCode: "EH", t2: "AFR", t3: "MA", t4: "EH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 37, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }],
    result = data.reduce((r, o) => {
        let p = r;
        groups.every(fn => p = fn(o, p));
        return r;
    }, []);
   console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

快速统一的方法,每个级别都有相同的结构

{ id, children: [] }

这是每个级别的结果结构。 id 反射(reflect)实际的组值,children 包含相同结构的其他对象或最终对象。

此方法采用一个对象来表示关卡的所有键以及内部的结果_

它首先使用值过滤顶部组,然后通过从(最终)对象中提取 level 属性来减少组。

如果实际级别的组不存在,则会创建带有数组的新属性。该对象包含一个带有空数组的属性_。该数组与该级别子级的后续可见对象共享相同的对象引用。

在缩减结束时,将推送没有访问过的属性的最终对象。

最后返回下划线属性,因为它包含所有嵌套组。

<小时/>

这种方法的主要优点是通过给定的键立即添加所需的分组。

主要的缺点是得到一个通用的结果,它不提供特定级别的定制。它不会过滤不需要的对象。

const
    keys = ['region', 'country', 't2', 't3', 't4', 't5', 't6', 't7'],
    data = [{ region: null, country: null, territory: "Worldwide", territoryCode: "ALL", t2: null, t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 1, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Madagascar", territory: null, territoryCode: "MG", t2: "AFR", t3: "MG", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 30, localLanguageName: "Malagasy, French", localLanguageCode: "MLG, FRE" } }, { region: "Africa", country: null, territory: null, territoryCode: "AFR", t2: "AFR", t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 2, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: null, territoryCode: "MA", t2: "AFR", t3: "MA", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 35, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Morocco (excl. Western Sahara)", territoryCode: "MAXEH", t2: "AFR", t3: "MA", t4: "MAXEH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 36, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Western Sahara", territoryCode: "EH", t2: "AFR", t3: "MA", t4: "EH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 37, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }],
    result = data
        .reduce((t, o) => {
            const groups = keys.filter((flag => k => flag = flag && o[k])(true));
            groups
                .reduce(function (r, k) {
                    let id;
                    ({ [k]: id, ...o } = o);
                    if (!r[id]) {
                        r[id] = { _: [] };
                        r._.push({ id, children: r[id]._ });
                    }
                    return r[id];
                }, t)._.push(o);
            return t;
        }, { _: [] })
        ._;
 
   console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

很难理解规范解决方案应该覆盖给定数据集的哪一部分、是否或任何数据集以及目标操作正在寻找什么。因此,这个答案由两部分组成,但如果给出提示,任何人都会受益。

关于javascript - 按对象数组javascript中的多个键进行分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60943243/

相关文章:

c - 32 位和 64 位系统上的数组指针

javascript - 在 IE 中动态添加到 DOM 时 Flash 对象不起作用

javascript - Keen IO JavaScript SDK 用于发送数据/事件

javascript - 如何将包含 HTML 代码的 JavaScript 字符串移动到它自己的文件中

java - 使用 arrayList 的排序算法

python - python中的测地距离变换

javascript - 如何从 NodeJS 运行 CLI 命令?

javascript - 如何: If data attribute equals radio select add class

javascript - 带索引的文字数组?

php - MongoDB、数组存储和对象查询