嗨,所以我想將給定的平面物件陣列(loc. dataset)轉換為具有重復鍵的樹狀結構。
注意:我的實際輸入資料是大約 140K 個物件元素,具有完全相同的 4 個鍵,如下所示。
輸入樣本:
[
{
"continent_name":"Asia",
"country_name":"Iran",
"subdivision_1_name":"Chaharmahal and Bakhtiari Province",
"city_name":"Lir Abi"
},
{
"continent_name":"Europe",
"country_name":"Cyprus",
"subdivision_1_name":"Ammochostos",
"city_name":"Protaras"
},
{
"continent_name":"Asia",
"country_name":"Iran",
"subdivision_1_name":"West
Azerbaijan Province",
"city_name":"Post"
},
{
"continent_name":"Africa",
"country_name":"Somalia",
"subdivision_1_name":"Bakool",
"city_name":"Oddur"
}
]
輸出樣本:
[
{
label: "Asia",
children: [
{
label: 'Iran',
children: [
{
label: 'Chaharmahal and Bakhtiari Province',
children: [
{
label: 'Lir Abi',
children: []
}
]
},
{
label: 'West Azerbaijan Province',
children: [
{
label: 'Post',
children: []
}
]
}
]
}
]
},
{
label: "Africa",
children: [
{
label: 'Somalia',
children: [
{
label: 'Bakool',
children: [
{
label: 'Oddur',
children: []
}
]
}
]
}
]
},
{
label: "Europe",
children: [
{
label: 'Cyprus',
children: [
{
label: 'Ammochostos',
children: [
{
label: 'Protaras',
children: []
}
]
}
]
}
]
}
]
這是我試圖使用的代碼:
const returnTree = []
function unflatten(data, property, returnArr) {
for (let i = 0; i < data.length; i ) {
const currObj = data[i];
const currContinent = data[i][property]
let continentIdx = returnArr.findIndex(obj => obj.label === currContinent)
if (continentIdx === -1) {
continentIdx = returnArr.length
returnArr.push({
'label': currContinent,
'children': [currObj]
})
} else {
returnArr[continentIdx].children.push(currObj)
}
// exceeed max call stack if I continue even one more level in
unflatten(returnArr[continentIdx].children, 'country_name', returnTree)
}
console.log(returnArr)
return returnArr
}
unflatten(inputData, 'continent_name', returnTree)
我遇到的問題是我使用這種遞回方法超過了最大呼叫堆疊,我想知道是否有更好的方法來處理這個問題,也許是迭代的?
任何幫助,將不勝感激!謝謝!
uj5u.com熱心網友回復:
另一種方法是將物件作為哈希表和每個子陣列的結果集。
const
data = [{ continent_name: "Asia", country_name: "Iran", subdivision_1_name: "Chaharmahal and Bakhtiari Province", city_name: "Lir Abi" }, { continent_name: "Europe", country_name: "Cyprus", subdivision_1_name: "Ammochostos", city_name: "Protaras" }, { continent_name: "Asia", country_name: "Iran", subdivision_1_name: "West Azerbaijan Province", city_name: "Post" }, { continent_name: "Africa", country_name: "Somalia", subdivision_1_name: "Bakool", city_name: "Oddur" }],
keys = ["continent_name", "country_name", "subdivision_1_name", "city_name"],
result = data
.reduce((r, o) => {
keys.reduce(function (q, k) {
const label = o[k];
if (!q[label]) q._.push({ label, children: (q[label] = { _: [] })._ });
return q[label];
}, r);
return r;
}, { _: [] })
._;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
uj5u.com熱心網友回復:
我不認為遞回是這里的方法,因為對于每個屬性值(用來制作一個物件),在生成的結構中只有一個位置可以是。在迭代屬性時,將最后一個外部陣列保存在外部變數中,您可以.find查看是否已經插入了匹配的物件 - 如果沒有,則創建一個。
const input=[{continent_name:"Asia",country_name:"Iran",subdivision_1_name:"Chaharmahal and Bakhtiari Province",city_name:"Lir Abi"},{continent_name:"Europe",country_name:"Cyprus",subdivision_1_name:"Ammochostos",city_name:"Protaras"},{continent_name:"Asia",country_name:"Iran",subdivision_1_name:"West Azerbaijan Province",city_name:"Post"},{continent_name:"Africa",country_name:"Somalia",subdivision_1_name:"Bakool",city_name:"Oddur"}];
const output = [];
for (const obj of input) {
let nestedArr = output;
for (const [key, value] of Object.entries(obj)) {
const existingInnerObj = nestedArr.find(({ label }) => label === value);
if (existingInnerObj) {
nestedArr = existingInnerObj.children;
} else {
const newObj = { label: value, children: [] };
nestedArr.push(newObj);
nestedArr = newObj.children;
}
}
}
console.log(output);
您可以通過將創建的物件保存在地圖中或按標簽索引的物件中來降低復雜性,或者首先在物件中創建它們,然后稍后將它們轉換為陣列(這會將O(n)操作.find轉換O(1)為屬性查找)。
uj5u.com熱心網友回復:
此版本配置了您要嵌套的鍵串列,并且它與每個鍵一起重復出現。它的遞回僅在結果樹的級別上。如果這有遞回深度問題,那么還有許多更重要的問題需要處理。??
const omitting = (prop) => ({[prop]: p, ...rest}) => rest
const group = (fn) => (xs) => Object .values (xs .reduce
((a, x, _, __, k = fn (x)) => ((a [k] = a [k] || []), (a [k] .push (x)), a),
{}
))
const labelGroup = ([label, ...labels]) => (xs) =>
label == undefined
? xs
: group (x => x [label]) (xs) .map (ys => ({
label: ys [0] [label],
children: labelGroup (labels) (
ys .map (omitting (label)) .filter (x => Object .keys (x) .length)
)
}))
const divisions = [{continent_name: "Asia", country_name: "Iran", subdivision_1_name: "Chaharmahal and Bakhtiari Province", city_name: "Lir Abi"}, {continent_name: "Europe", country_name: "Cyprus", subdivision_1_name: "Ammochostos", city_name: "Protaras"}, {continent_name: "Asia", country_name: "Iran", subdivision_1_name: "West Azerbaijan Province", city_name: "Post"}, {continent_name: "Africa", country_name: "Somalia", subdivision_1_name: "Bakool", city_name: "Oddur"}]
const labels = ['continent_name', 'country_name', 'city_name', 'subdivision_1_name']
console .log (
labelGroup (labels) (divisions)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
我們從兩個輔助函式開始:
omitting回傳省略指定鍵的物件的副本。group接受一個鍵生成函式,并回傳一個函式,該函式接受一個值陣列并將它們分組到子陣列中,每個子陣列映射到一個鍵。
主函式labelGroup接受一個標簽串列進行分組并回傳一個函式,該函式接受一個值陣列,并將這些值遞回地分組到共享標簽的組中,通過提取公共標簽來映射結果,從每個標簽中省略當前鍵,以及回傳labelGroup使用剩余鍵應用于它們的遞回結果。當沒有標簽要提取時,遞回會觸底。
唯一棘手的一點是filter遞回應用程式之前的呼叫。我們用它來洗掉完全空的物件。當您對物件中的每個標簽進行分組時,這將為您提供所需的結果,如此處所做的,但也允許您在較小的標簽串列上進行分組。所以如果你只通過'continent_name'and 'countryName',你會得到這樣的結果:
{
"label": "Asia",
"children": [
{
"label": "Iran",
"children": [
{
"subdivision_1_name": "Chaharmahal and Bakhtiari Province",
"city_name": "Lir Abi"
},
{
"subdivision_1_name": "West Azerbaijan Province",
"city_name": "Post"
}
]
}
]
},
/* ... */
}
I think this is a fairly flexible technique. We might make it still more flexible by allowing us to group on composite keys or select multiple fields that show up at each level, and we might make the fields label and children configurable. But that's for another day.
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/437638.html
標籤:javascript 递归 树 展平
