我正在使用有角度的材質樹來顯示深度嵌套的物件。在構建樹時,如何將當前路徑與值一起存盤?
const TREE_DATA = JSON.parse(JSON.stringify({
"cars": [
{
"model": "",
"make": "Audi",
"year": ""
},
{
"model": "A8",
"make": "",
"year": "2007"
}
],
"toys": {
"color": "Black",
"type": [
{
"brand": "",
"price": "$100"
}
]
},
"id": "xyz",
"books": [
{
"publisher": [
{
"authors": []
}
]
}
],
"extra": "test"
}));
@Injectable()
export class FileDatabase {
dataChange = new BehaviorSubject<FileNode[]>([]);
get data(): FileNode[] { return this.dataChange.value; }
constructor() {
this.initialize();
}
initialize() {
const dataObject = JSON.parse(TREE_DATA);
const data = this.buildFileTree(dataObject, 0);
this.dataChange.next(data);
}
buildFileTree(obj: {[key: string]: any}, level: number): FileNode[] {
return Object.keys(obj).reduce<FileNode[]>((accumulator, key) => {
const value = obj[key];
const node = new FileNode();
node.filename = key;
if (value != null) {
if (typeof value === 'object') {
node.children = this.buildFileTree(value, level 1);
} else {
node.type = value;
}
}
return accumulator.concat(node);
}, []);
}
}
目前, buildFileTree 函式回傳:
[
{
"filename": "cars",
"children": [
{
"filename": "0",
"children": [
{
"filename": "model",
"type": ""
},
{
"filename": "make",
"type": "Audi"
},
{
"filename": "year",
"type": ""
}
]
},
{
"filename": "1",
"children": [
{
"filename": "model",
"type": "A8"
},
{
"filename": "make",
"type": ""
},
{
"filename": "year",
"type": "2007"
}
]
}
]
},
{
"filename": "toys",
"children": [
{
"filename": "color",
"type": "Black"
},
{
"filename": "type",
"children": [
{
"filename": "0",
"children": [
{
"filename": "brand",
"type": ""
},
{
"filename": "price",
"type": "$100"
}
]
}
]
}
]
},
{
"filename": "id",
"type": "a"
},
{
"filename": "books",
"children": [
{
"filename": "0",
"children": [
{
"filename": "publisher",
"children": [
{
"filename": "0",
"children": [
{
"filename": "authors",
"type": []
}
]
}
]
}
]
}
]
},
{
"filename": "extra",
"type": "test"
}
]
在構建這棵樹時,如何將路徑添加到每個級別的每個“型別”?類似于“路徑”:第一個“型別”的“cars.0.model”等等。
uj5u.com熱心網友回復:
用你的buildFileTree函式試試這個
buildFileTree(obj: { [key: string]: any }, path?: string): FileNode[] {
return Object.keys(obj).reduce<FileNode[]>((accumulator, key) => {
const value = obj[key];
const node = new FileNode();
node.filename = key;
// Give the path a default value of '', or add a "." if it already has a value
path = path ? path '.' : '';
// Save this path to the node, and add the current key value to it
node.path = path key;
if (value != null) {
if (typeof value === 'object') {
// Pass the node.path value back to the buildFileTree function
node.children = this.buildFileTree(value, node.path);
} else {
node.type = value;
}
}
return accumulator.concat(node);
}, []);
}
我剛剛添加了路徑值,并將其傳遞回每個回圈中的函式。
我還洗掉了level引數,因為您似乎沒有在函式中使用它。
uj5u.com熱心網友回復:
我會先寫一個稍微不同的版本buildFileTree,然后將路徑生成分層。
我會像這樣寫出相當于你目前所擁有的(忽略未使用的length引數):
const buildFileTree = (o) =>
Object .entries (o) .map (([filename, type]) =>
Object (type) === type
? {filename, children: buildFileTree (type)}
: {filename, type}
)
最有可能的是,我會Object (type) === type用呼叫合適的助手來代替isObject (type)。 isObject很容易寫或找到。1
然后我們可以添加一個默認path引數,更新它并在遞回呼叫中傳遞它并將其包含在我們的輸出中:
const concatPath = (s1) => (s2) =>
s1 ? s1 '.' s2 : s2
const buildFileTree = (o, path = '') =>
Object .entries (o) .map (([filename, type]) =>
Object (type) === type
? {filename, path: concatPath (path) (filename), children: buildFileTree (type, concatPath (path) (filename))}
: {filename, path: concatPath (path) (filename), type}
)
但即使concatPath 提取到一個助手,這也有一些不幸的重復。我們concatPath在代碼中的三個地方使用相同的引數呼叫,在遞回的情況下,我們使用其中的兩個。我使用兩種不同的技術來解決這個問題,還有第三種更常見的技術,我不喜歡使用。第三個只是創建一個區域變數并在我們現在呼叫的三個地方使用它concatPath:
const buildFileTree = (o, p = '') =>
Object .entries (o) .map (([filename, type]) => {
const path = p ? p '.' filename : filename
return Object (type) === type
? {filename, path, children: buildFileTree (type, path)}
: {filename, path, type}
})
這沒有什么問題。但我更喜歡的風格是最小化區域變數并嘗試只使用運算式而不是陳述句。我的兩種技術彼此相似。第一個是向map呼叫添加默認引數。不幸的問題是那里的回呼已經在專案(索引和整個陣列)之后提供了兩個額外的引數,所以我們需要一些輕微的體操,在它們的位置添加虛擬引數:
const buildFileTree = (o, p = '') =>
Object .entries (o) .map (([filename, type], _, __, path = p ? p '.' filename : filename) =>
Object (type) === type
? {filename, path, children: buildFileTree (type, path)}
: {filename, path, type}
)
該_和__是為那些引數只是占位符變數。它并不可怕,但它仍然有點丑陋。我會經常選擇這個,因為它在語法上比我的其他選項簡單一些。那個增加了一個額外的IIFE作為一個地方來保存我的附加值的引數。它看起來像這樣:
const buildFileTree = (o, p = '') =>
Object .entries (o) .map (([filename, type]) => ((
path = p ? p '.' filename : filename
) => Object (type) === type
? {filename, path, children: buildFileTree (type, path)}
: {filename, path, type}
) ())
const treeData = {cars: [{model: "", make: "Audi", year: ""}, {model: "A8", make: "", year: "2007"}], toys: {color: "Black", type: [{brand: "", price: "$100"}]}, id: "xyz", books: [{publisher: [{authors: []}]}], extra: "test"}
console .log (buildFileTree (treeData))
.as-console-wrapper {max-height: 100% !important; top: 0}
這些版本中的任何一個都應該可以作業。他們都無視你的new FileNode()電話;我不知道它是什么或它有多必要,但集成它并不難。
Also, I don't know how you plan on using these paths, but for such cases, I usually prefer to store them not as .-separate strings, but as arrays of strings and integers (for array indices). That wouldn't be much different, and I think it gives more flexibility. And you can always join them back to a string with .join ('.'). We could do it like this:
const buildFileTree = (o, p = []) =>
Object .entries (o) .map (([filename, type]) => ((
path = [...p, Array .isArray (o) ? Number(filename) : filename]
) => Object (type) === type
? {filename, path, children: buildFileTree (type, path)}
: {filename, path, type}
) ())
1 I like to do this Ramda-style (disclaimer: I'm one of it's authors) and build
isObjectatop a genericis:const is = (Ctor) => (val) => val != null && val.constructor === Ctor || val instanceof Ctor const isObject = is (Object)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/334079.html
標籤:javascript 有角的 递归 角材料 树
