我想在不使用回圈的情況下在以下嵌套陣列中找到一個值:
let children = [
{
name: 'grand 1',
children: [
{
name: 'parent 1.1',
children: [
{
name: 'child 1.1.1',
children: [
// more...
]
},
// more...
],
},
// more...
],
},
// more...
];
如果我只在水平軸上搜索,這就是我要做的:
let childrenHorizontal = [
{ name: 'grand 1' },
{ name: 'grand 2' },
{ name: 'grand 3' },
// more
];
function findHorizontal(name, childrenHorizontal) {
let [head, ...tail] = childrenHorizontal;
if (head.name === name)
return head;
else if (tail.length)
return findHorizontal(name, tail);
}
但是如何水平和垂直搜索嵌套陣列?
uj5u.com熱心網友回復:
更一般地說,我們可以撰寫一個deepFind函式,它接受任意謂詞并回傳一個函式,該函式以深度優先的方式測驗樹,直到找到匹配的節點,undefined如果沒有找到匹配則回傳。(畢竟這是 JS!)然后我們可以撰寫findByName一個函式,它接受一個目標名稱并傳遞給deepFind一個函式,該函式測驗給定節點是否name與該目標匹配。它可能看起來像這樣:
const deepFind = (pred) => ([head, ...tail]) =>
head == undefined
? undefined
: pred (head)
? head
: deepFind (pred) (head .children) || deepFind (pred) (tail)
const findByName = (target) => deepFind (({name}) => name == target)
let children = [{name: 'grand 1', children: [{name: 'parent 1.1', children: [{name: 'child 1.1.1', children: []}]}, {name: 'parent 1.2', children: []}]}]
console .log ('parent 1.1:', findByName ("parent 1.1") (children))
console .log ('child 1.1.1:', findByName ("child 1.1.1") (children))
console .log ('parent 1.2:', findByName ("parent 1.2") (children))
.as-console-wrapper {max-height: 100% !important; top: 0}
(請注意,我parent 1.2在樹中的明顯位置添加了一個節點,以演示搜索一個節點的多個子節點。)
這會找到與我們的謂詞匹配的樹的預序遍歷中的第一個節點。我們使用 JavaScript 運算子的短路功能||在找到匹配項后立即停止。
uj5u.com熱心網友回復:
好的,我想通了。訣竅是連接兩個軸:
function find(name, children) {
let [head, ...tail] = children;
if (head.name === name)
return head;
return find(name, [...head.children, ...tail]);
}
但后來我意識到,當我只能使用邏輯 OR(使用null-coalescing 運算子)時,連接陣列是一種浪費:
function find(name, children) {
let [head, ...tail] = children
if (head.name === name) return head
return find(name, head.children) ?? find(name, tail) // no concat!
}
而且我還了解到,您必須始終檢查輸入陣列是否為空,否則head可能未定義!
function find(name, children) {
if (children.length == 0) return; // exit early if children is empty
let [head, ...tail] = children;
if (head.name === name) return head;
return find(name, head.children) ?? find(name, tail);
}
恢復尾呼叫的一種可能方法是使用連續傳遞樣式:
const identity = x => x
function find(name, children, k = identity) {
if (children.length == 0) return;
let [head, ...tail] = children;
if (head.name === name) return k(head);
return find(name, head.children, result => // tail find
result ?? find(name, tail, k) // tail find
);
}
我還了解到解構賦值let [head, ...tail]會創建新tail陣列 - 為沿途的每個節點!find是對結構的一種讀取操作,因此在此程序中創建一次性資料會很浪費:
function find(name, children, i = 0) {
if (i >= children.length) return; // exit if i is out of bounds
let head = children[i] // "head" is now a cursor over the array
if (head.name === name) return head;
return find(name, head.children) ?? find(name, children, i 1);
}
如果我希望用戶防止意外傳遞初始i引數,我可以包裝在一個外部函式中。如果需要,我可以為尾呼叫添加 CPS 技術。
uj5u.com熱心網友回復:
您需要使用遞回:
const data = [{
name: 'grand 1',
children: [{
name: 'parent 1.1',
children: [{
name: 'child 1.1.1',
children: []
},
// more...
],
},
// more...
],
},
// more...
];
const find = (name, children) => {
for (const child of children) {
// if the chidren's name corresopnds to the one provided, return it
if (child.name === name) return child
// here is the recursion
const e = find(name, child.children)
// if we found it on the child, we return the one found, else we keep going
if (e) return e
}
// if none of the children or grand-children ... found, return null
return null
}
console.log(find('parent 1.1', data))
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/537112.html
