2013-02-04 63 views
1

我試圖創建一個函數來「解析」一個對象中嵌套屬性的點分隔的路徑。如何返回對象中屬性的命名空間路徑?

所以說,你有下列對象:

var obj = { 
    A: { 
     A_1: { 
     }, 
     A_2: { 
      A_2_a: {}, 
      A_2_b: { 
       A_2_b_1: {}, 
       A_2_b_2: {}, 
      } 
     }, 
     A_3: {}, 
    }, 
    B: { 
     B_1: { 
     }, 
     B_2: { 
      B_2_a: { 
      }, 
      B_2_b: { 
       B_2_b_1: {}, 
       B_2_b_2: {}, 
      } 
     }, 
     B_3: {}, 
    }, 
}; 

我想打電話給像resolve(obj, "B_2_b_2")對象上的功能,它有完整的對象路徑返回屬性,即:B.B_2.B_2_b.B_2_b_2

+2

如果相同的屬性名稱在'obj'中出現兩次,該怎麼辦? –

+1

你到目前爲止嘗試過什麼?你會發佈一個或兩個你最好的「失敗的嘗試」? –

+0

或者,你只是想分割一個下劃線? –

回答

1

假設一個命名約定,就像在你的榜樣對象:

function resolve(id) { 
    var parts = id.split("_"); 
    var path = []; 
    for (var i=0; i<parts.length; i++) 
     path.push(parts.slice(0, i+1).join("_")); 
    return path; 
} 

> resolve("B_2_b_2") 
["B", "B_2", "B_2_b", "B_2_b_2"] 
> resolve("B_2_b_2").join(".") 
"B.B_2.B_2_b.B_2_b_2" 

隨着路徑數組,你可以很容易地遞歸在你的嵌套的對象來獲得屬性值。


數據對象中的樹搜索很簡單。但是,我們可以通過假設一個命名約定來優化:

function resolve(obj, id) { 
    if (id in obj) 
     return [id]; // we've found it 
    var path; 
    for (var l=id.length-1; l>0; l--) { 
     var sub = id.substr(0, l); 
     if (sub in obj && (path = resolve(obj[sub], id))) { 
      path.unshift(sub); 
      return path; 
     } 
    } 
    for (var prop in obj) { 
     if (path = resolve(obj[prop], id)) { 
      path.unshift(prop); 
      return path; 
     } 
    } 
    return null; 
} 
+0

嗯..所以基本上先創建一個路徑數組,然後在遍歷對象的同時進行搜索,然後查找hasOwnProperty匹配項?嗯..你可能會找到一些東西。我會嘗試。 – Xaxis

+0

等一下,看不到這是行不通的:那會假定一個屬性的路徑將保持一個給定的命名約定。這不是我的意圖。 – Xaxis

+0

您沒有命名約定?那麼你尋找什麼呢?否則,屬性名稱可能發生在對象的任何地方(以及多次)。 – Bergi

1

尤里卡!我想到了!下面的答案是我正在編寫的庫的形式,但它應該相對容易遵循。

事實證明,最佳行動過程(據我可以告訴)是首先使用一個單獨的函數,建立從包含所有屬性路徑的目標對象的對象:

/** 
* Returns an object containing all of the property paths of an object. Each 
* property path is referenced by the property name. 
* @param {object} The object to target 
* @return {object} Object containing paths ELSE undefined 
*/ 
paths: function(obj, path, lastKey, nextKey) { 
    var o, key, 
     path = path ? path : {}, 
     lastKey = lastKey ? lastKey : "", 
     nextKey = nextKey ? nextKey : ""; 

    for (o in obj) {  

     // Push path onto stack 
     path[o] = (nextKey + "." + lastKey + "." + o).replace(/^[.]+/g, ""); 

     // Pass updated "nextKey" along with next recurse 
     key = nextKey + "." + lastKey; 

     // Call again on all nested objects 
     if ((lib).isPlainObject(obj[o])) { 
      (lib).paths(obj[o], path, o, key); 
     } 
    } 

    return (lib).len(path) ? path : undefined; 
}, 

然後我們使用resolve方法作爲路徑方法的「包裝器」,返回目標屬性鍵的名稱空間。

resolve: function(obj, key) {  
    return (lib).paths(obj)[key]; 
}, 

使用我張貼的對象原本以上:

var res = o.resolve(obj, "A_2_b_1"); 
// Returns "A.A_2.A_2_b.A_2_b_1" 

僅供參考,該paths方法返回一個對象,它看起來是這樣的:

// { 
    // A: [...] 
    // A_1: [...] 
    // A_2: [...] 
    // A_2_a: [...] 
    // A_2_b: [...] 
    // A_2_b_1: [ 
    // 0: "A_2_b_1" 
    // 1: "A.A_2.A_2_b.A_2_b_1" 
    // ] 
    // A_2_b_2: [...] 
    // A_2_c: [...] 
    // A_3: [...] 
    // B: [...] 
    // ... 
// } 

其中每個屬性映射到它在物體中的路徑。

+1

請將您的'resolve'方法改爲一個簡單的'return lib.paths(obj)[key];'並從數組中移除該鍵。順便說一句,建立一個完整的'路徑'對象只是爲了查找一條路徑效率很低 – Bergi

+0

好主意。感謝您的建議!當我試圖解決問題時,我傾向於用更加明確的方式來寫東西。然後我重構。但你是完全正確的。謝謝。 – Xaxis

+0

關於效率:在庫實現中,給定對象的路徑只能計算一次,鏈中的方法將訪問該數據。 – Xaxis