2015-01-05 48 views
1

我有一個嵌套的對象,與此類似:有n個級別轉換嵌套對象到數組

var obj = { 
    "prop1": { 
     "prop1A": "A", 
     "prop1B": { 
        "prop1BA": "BA" 
     }, 
     "prop1C": "C" 
    } 
}; 

我的最終目標是要過濾此目的是特定預先定義的鍵,根據另一模式對象,例如:

var filterSchema = { 
    "prop1":["prop1A", {"prop1B":["prop1BA"]}] 
}; 

(過濾器鍵是預定義的,如果你有更好的想法,我可以以不同結構此對象...

輸出應該是一個數組中。我們的案例:

["A","BA"] 

我設法使用遞歸遍歷對象。我想知道是否有實現這一目標更優雅的方式(嘗試使用jQuery的地圖,沒有運氣/擴展)

編輯 我知道,這是一個「N」級問題應該由遞歸來解決。這裏的區別在於我有預定義的過濾器,它已經具有「N」級別。所以我雖然也許我可以使用過濾器數組過濾Objet並將其轉換爲數組。

EDIT2 謝謝大家的不同答案。這是我自己的問題(我一直在尋找一個更優雅的一個在一開始)解決方案: My solution

  var obj = { 
     "prop1": { 
      "prop1A": "A", 
      //"prop1B": { 
      //   "prop1BA": "BA" 
      //}, 
      "prop1C": "C", 
      "prop1D": "D", 
      "prop1E": {"prop1E1": "444"} 
     }, 
     "prop2": "12345" 
    }; 


    var schemaObj = { 
     "prop1": { 
      "prop1A": "true", 
      "prop1B": { 
         "prop1BA": "true" 
      }, 
      "prop1C": "true" 
     }, 
     "prop2": "true" 
    }; 

var resultsArray = []; 
var keys = Object.keys(schemaObj); 
for(var i=0;i<keys.length;i++){ 
    if(obj[keys[i]]){ 
     parser(schemaObj[keys[i]], obj[keys[i]]);  
    } 
} 


function parser(v,o){ 
    if(typeof v === "string"){ 
     resultsArray.push(o);  
    } 
    else{ 
    var keys2 = Object.keys(v); 
    for(var j=0;j<keys2.length;j++){ 
     if(o[keys2[j]]){ 
     parser(v[keys2[j]], o[keys2[j]]); 
     } 
    } 
    } 
} 

console.log(resultsArray); 

只是一個關於這個問題的提醒 - 我已經有遞歸解決方案。我正在尋找不同的解決方案

+6

拆下來JSON的所有引用。 –

+1

好奇,在這裏如何不雅地遞歸?似乎(不考慮性能問題)成爲一個理想的用例... –

+0

這裏沒有其他辦法,但遞歸 – hindmost

回答

0

Javascript有eval,它允許您在運行時創建新代碼。一個可能的解決方案是使用遞歸只有一次創建一個字符串,它看起來像:

code = "[obj.prop1.prop1A, obj.prop1.prop1B.prop1BA]" 

那麼你就可以

f = eval("function(obj){return " + code + "]}") 

創建一個數據轉換功能,並用它來與f(x)讓你的陣列。

如果您必須多次提取數據,這也是最有效的解決方案。

例如:

function mkFilter(schema) { 
    var parts = []; 
    function xtract(s, prefix) { 
     if (typeof s === "string") { 
      parts.push(prefix + s); 
     } else if (s && s.constructor === Array) { 
      s.forEach(function(x){ xtract(x, prefix); }); 
     } else { 
      for (var f in s) { 
       xtract(s[f], prefix + f + "."); 
      } 
     } 
    } 
    xtract(schema, "obj."); 
    var code = "(function(obj){ return [" + parts.join(", ") + "]; })"; 
    return eval(code); 
} 

傳遞schemaFilter作爲參數mkFilter將返回給定的對象的函數返回陣列;與您的輸入:

console.log(mkFilter(filterSchema)(obj)); 

顯示['A', 'BA']。當然,如果您需要多次使用不同的對象重複使用相同的過濾器,這種方法纔有意義。

如果對象可能有缺少的部分,你不希望過濾器失敗,但代碼生成所需要的陣列,在短短undefined值將略有改變:

var code = "(function(obj){ return ["; 
parts.forEach(function(p){ 
    var chain = p.split("."); 
    var expr = ""; 
    for (var i=0; i<chain.length-1; i++) { 
     expr += chain.slice(0, i+1).join(".") + " && "; 
    } 
    code += expr + p + ","; 
}); 
code += "]; })"; 

這將在您的例子創建過濾器評估

(function(obj){ 
    return [obj && obj.prop1 && obj.prop1.prop1A, 
      obj && obj.prop1 && obj.prop1.prop1B && 
        obj.prop1.prop1B.prop1BA,]; 
}) 
+0

...只有當所有請求的屬性都存在於源對象中時才起作用。 (FYI,不是我的downvote) – Tomalak

+0

@Tomalak:你可以將這個調用包裝在一個'try'中或者生成一個表達式,例如'[obj && obj.prop1 && obj.prop1.prop1A,...]' – 6502

+0

但是,潛在的結果數組中有很多「未定義」的值。但它仍然是一個有創意的解決方案。 – Tomalak

0

以下功能似乎做你想要什麼:

function filter(obj, schema, out) { 
    var i, schemaItems, schemaItem, isItemLevel; 

    if (!obj || !schema) return; 

    out = out || {values: []}; 
    isItemLevel = Array.isArray(schema); 
    schemaItems = isItemLevel ? schema : Object.keys(schema); 

    for (i = 0; i < schemaItems.length; i++) { 
     schemaItem = schemaItems[i]; 
     if (isItemLevel && typeof schemaItem === "string") { 
      out.values.push(obj[schemaItem]); 
     } else if (typeof schemaItem === "object") { 
      filter(obj, schemaItem, out); 
     } else if (typeof schemaItem === "string") { 
      filter(obj[schemaItem], schema[schemaItem], out); 
     } 
    } 

    return out.values; 
} 

稱爲

var obj = { 
    "prop1": { 
     "prop1A": "A", 
     "prop1B": { 
      "prop1BA": "BA" 
     }, 
     "prop1C": "C" 
    } 
}; 

var filterSchema = { 
    "prop1":["prop1A", {"prop1B":["prop1BA"]}] 
}; 

filter(obj, filterSchema); 

回報:

["A", "BA"] 

用一粒鹽拿去吧,這是迄今爲止沒有測試好,夠了,我當然不主張這是最優雅解決這個問題的方法。

它的工作原理是這樣的:

  • 遍歷schema的項目(其是一個數組或一個對象)
  • 每個schemaItem
    • 如果我們在一個陣列和所述schemaItem是一個字符串,輸出各自的屬性值obj
    • 否則如果schemaItem本身就是一個對象,遞歸,但保持在的同一級別
    • 否則如果schemaItem是一個字符串,遞歸,鑽進既objschema
+0

如果沒有明確的規範,很難說清楚,但看起來您的代碼假定filterSchema始終是一個對象。例'obj = {x:「A」}和'filterSchema = [「x」]'無效?我會說結果應該是'[「A」]'。 – 6502

+0

是的,我想這將是(不得不猜測是討厭)。我想嵌套循環可以不嵌套來支持這種情況。無論如何,一個具有兩個遞歸點的函數並不適合。 – Tomalak

+0

對於我來說,兩次遞歸調用在我看來都是最簡潔的方法,即使嵌套數組在這種'filterSchema'格式下沒有意義。 – 6502

0

使用jQuery的地圖功能。你可以嘗試在控制檯中的示例代碼片段,但jQuery的必須包含

a = { aa: '123', ab: 'asdasd'} 
$.map(a, function(key,val){ 
    return val; 
}); 
// The map creates an array with the value you return from the code block. 
// Output is ["aa", "ab"] 

參考文獻見 http://api.jquery.com/jQuery.map/

+1

一些事情。你的回調參數是倒退的,我不認爲這接近OP的需求。 –

+0

你是對的@squint我不知道數組原型中的映射方法,謝謝 – illusionist

相關問題