javascript - 按对象数组javascript中的多个键分组
问题描述
我有一个嵌套的对象数组,如下所示。
[
{
"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;
解决方案
我将从一个非常通用的开始,groupBy
它支持基于键数组嵌套多个组:
// Takes an array of keys and array of objects and returns a nested grouping
const groupByKeys = ([k, ...ks], xs) => k
? mapObj(
ys => groupByKeys(ks, ys),
groupByKey(k, xs)
)
: xs;
// Groups an array based by a key
const groupByKey = (k, xs) => xs
.reduce(
(gs, x) => Object.assign(gs, { [x[k]]: (gs[k] || []).concat([x]) }),
{}
);
// Utility to map a function over the values of an object
const mapObj = (f, obj) => Object.fromEntries(
Object.entries(obj).map(([k, v]) => [ k, f(v) ])
);
注意:您可能会在 Ramda 或 lodash 等前端库中找到这些实用方法的维护良好、测试更好且性能更高的版本。
您现在可以使用以下方法对数据进行分组:
const groups = groupByKeys(
["region", "country", "t2", "t3", "t4", "t5", "t6", "t7"],
your_data
);
下一步将是转换为所需格式并处理所有null
路径的更难的部分。
为了使这更容易和更高效,我在每个分组层上包含了一个所有元素的数组:
const groupByKeys = ([k, ...ks], xs) => k ? Object.assign(mapObj( ys => groupByKeys(ks, ys), groupByKey(k, xs) ), { [AllKey]: xs }) : xs;
我们现在可以使用 , 和一些解构来遍历我们的Object.values
分组Array.prototype.map
:
const transform = groups => Object
.values(groups)
.map(
({ [AllKey]: allElementsInThisLayer, ...childGrouping }) => { /* ... */ }
);
现在剩下要做的就是定义转换每一层的逻辑。老实说,这就是我不太了解您想要的结果的地方。我实现了前几层,但也许你自己可以做得更好,因为你有结构化数据可以使用:
// Transformers for all layers of grouping
const Region = ({ [AllKey]: [ { region } ], ...countries }) => ({
region,
country: Object.values(countries).map(Country).filter(Country.notEmpty)
});
Region.notEmpty = ({ region }) => region !== null;
const Country = ({ [AllKey]: [ { country } ], ...t2}) => ({
desc: country,
t2: Object.values(t2).map(T2)
});
Country.notEmpty = ({ desc }) => desc !== null;
const T2 = ({ [AllKey]: [ t2 ], ...t3 }) => ({
id: t2.t2,
localLanguageName: t2.localLanguage.localLanguageName,
localLanguageCode: t2.localLanguage.localLanguageCode,
t3: Object.values(t3).map(T3)
})
const T3 = ({ [AllKey]: [ { t3 } ], ...t4 }) => ({
id: t3 // Etc.
})
这是一个可运行的片段。// Etc.
在评论处继续工作
const AllKey = Symbol();
// Utils
const mapObj = (f, obj) => Object.fromEntries(
Object.entries(obj).map(([k, v]) => [ k, f(v) ])
)
const groupByKey = (k, xs) => xs
.map(x => [x[k], x])
.reduce(
(gs, [k, v]) => Object.assign(gs, { [k]: (gs[k] || []).concat([v]) }),
{}
);
const groupByKeys = ([k, ...ks], xs) => k
? Object.assign(mapObj(
ys => groupByKeys(ks, ys),
groupByKey(k, xs)
), { [AllKey]: xs })
: xs;
// App
const keys = ["region", "country", "t2", "t3", "t4", "t5", "t6", "t7"]
const groups = groupByKeys(keys, getData());
// Transformers for all layers of grouping
const Region = ({ [AllKey]: [ { region } ], ...countries }) => ({
region,
country: Object.values(countries).map(Country).filter(Country.notEmpty)
});
Region.notEmpty = ({ region }) => region !== null;
const Country = ({ [AllKey]: [ { country } ], ...t2}) => ({
desc: country,
t2: Object.values(t2).map(T2)
});
Country.notEmpty = ({ desc }) => desc !== null;
const T2 = ({ [AllKey]: [ t2 ], ...t3 }) => ({
id: t2.t2,
localLanguageName: t2.localLanguage.localLanguageName,
localLanguageCode: t2.localLanguage.localLanguageCode,
t3: Object.values(t3).map(T3)
})
const T3 = ({ [AllKey]: [ { t3 } ], ...t4 }) => ({
id: t3 // Etc.
})
const transform = groups => Object
.values(groups)
.map(Region)
.filter(Region.notEmpty);
console.log(JSON.stringify(transform(groups), null, 2));
function getData() {
return [{
"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"
}
}]
};
.as-console-wrapper { min-height: 100% }
推荐阅读
- git - AWS Codebuild -- 执行 buildspec.yml 时如何读取提交上的标签?
- excel - Excel - 如何查找具有匹配键的所有值?
- json - 使用 PowerShell 组合嵌套的 Json
- java - Android 条件导入、预处理器宏或替代方案
- javascript - 一个滚动按钮,可滚动到 jquery 中的多个元素
- r - 从两列分配唯一 ID,其中值可以在不同行上以相反的顺序排列
- python - 如何使用 Python 3 获取文件的正确绝对路径?
- python - 上传 .pfx 格式的证书时出现错误
- vba - 使用 VBA 更改 Power Query 中的 SQL 查询
- c - 如何在 C 中将类型为 AA:BB:CC:DD:EE:FF 的字符串转换为 0xaabbccddeeff?