2013-03-26 57 views
34

我有一個巨大的集合,我正在尋找一個屬性集合內的關鍵某處。獲取包含該鍵/索引的所有對象的引用列表或完整路徑的可靠方法是什麼?我使用jQuery和lodash,如果它有幫助,你可以忘記無限指針遞歸,這是一個純粹的JSON響應。在深層物體中按名稱查找屬性

fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "d"); 
// [o.c] 

fn({ 'a': 1, 'b': 2, 'c': {'d':{'e':7}}}, "e"); 
// [o.c.d] 

fn({ 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd'); 
// [o.cc,o.cc.dd] 

FWIW lodash具有_.find函數,會發現有兩個窩深嵌套的對象,但似乎在那之後失敗。 (例如http://codepen.io/anon/pen/bnqyh

回答

45

這應做到:

function fn(obj, key) { 
    if (_.has(obj, key)) // or just (key in obj) 
     return [obj]; 
    // elegant: 
    return _.flatten(_.map(obj, function(v) { 
     return typeof v == "object" ? fn(v, key) : []; 
    }), true); 

    // or efficient: 
    var res = []; 
    _.forEach(obj, function(v) { 
     if (typeof v == "object" && (v = fn(v, key)).length) 
      res.push.apply(res, v); 
    }); 
    return res; 
} 
+3

這幾乎爲我工作。我不得不將第3行更改爲return [obj [key]];'而是讓我得到一個key值的數組而不是包裝對象 – 2014-07-31 15:40:17

+2

@ChrisMontgomery:是的,但是OP想要「所有對象*包含*那個鍵「(可能他正在對結果進行'pluck()') – Bergi 2014-07-31 16:42:07

+1

@Bergi有意義。您的解決方案適用於我所有的場景,而Al Jey的回答與對象內數組內的對象有關。 – 2014-07-31 17:11:51

2

類似這樣的東西可以工作,將其轉換爲對象並遞歸。

function find(jsonStr, searchkey) { 
    var jsObj = JSON.parse(jsonStr); 
    var set = []; 
    function fn(obj, key, path) { 
     for (var prop in obj) { 
      if (prop === key) { 
       set.push(path + "." + prop); 
      } 
      if (obj[prop]) { 
       fn(obj[prop], key, path + "." + prop); 
      } 
     } 
     return set; 
    } 
    fn(jsObj, searchkey, "o"); 
} 

小提琴:jsfiddle

21

一個純JavaScript的解決辦法如下所示:

function findNested(obj, key, memo) { 
    var i, 
     proto = Object.prototype, 
     ts = proto.toString, 
     hasOwn = proto.hasOwnProperty.bind(obj); 

    if ('[object Array]' !== ts.call(memo)) memo = []; 

    for (i in obj) { 
    if (hasOwn(i)) { 
     if (i === key) { 
     memo.push(obj[i]); 
     } else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) { 
     findNested(obj[i], key, memo); 
     } 
    } 
    } 

    return memo; 
} 

這裏是你如何使用這個功能:

findNested({'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd'); 

和結果將是:

[{x: 9}, {y: 9}] 
+0

該解決方案確實*不*嵌套數組的工作,例如findNested({'a':[{'b':{'c':7}}],'b')'@ Bergi's – 2014-07-31 17:50:01

+1

@ChrisMontgomery,這是因爲原始問題只包含對象而沒有有任何數組,我已經更新了示例。 – 2014-08-07 19:17:44

+0

@AlJey,對於你的lodash例子,我使用了'var o = {a:{really:'long'},obj:{that:{keep:'going'}}}'然後'findNested(o,'that' )'這給了我'RangeError:超過最大調用堆棧大小'。第一個作品雖然精彩! – Jon49 2015-02-16 20:45:45

4

這將深搜索對象(乾草)的值(針)的數組,然後返回的結果數組...

search = function(hay, needle, accumulator) { 
    var accumulator = accumulator || []; 
    if (typeof hay == 'object') { 
    for (var i in hay) { 
     search(hay[i], needle, accumulator) == true ? accumulator.push(hay) : 1; 
    } 
    } 
    return new RegExp(needle).test(hay) || accumulator; 
} 
+0

它對我很好 – robert 2015-08-26 15:00:23

+0

@ryanyuyu - 它可讀,不會接近縮小。這很簡單,但需要了解很多智商。這是一個美麗但複雜的解決方案,它強調大腦的極限:) – vsync 2016-09-29 15:04:08

+1

@vsync此後代碼已被編輯。請參閱[原始修訂](https://stackoverflow.com/revisions/28700151/1) – ryanyuyu 2016-09-29 15:22:24

0
Array.prototype.findpath = function(item,path) { 
    return this.find(function(f){return item==eval('f.'+path)}); 
} 
+4

您應該避免使用「僅限代碼」答案。請提供一些關於您的代碼的背景。 – croxy 2016-11-17 16:05:42

+0

IE支持'Array.find'嗎?如果沒有,你需要一個polyfill來支持傳統的IE。 – Shanimal 2016-11-17 17:29:14

0

我是這樣做的:

function _find(obj, field, results) 
{ 
    var tokens = field.split('.'); 

    // if this is an array, recursively call for each row in the array 
    if(obj instanceof Array) 
    { 
     obj.forEach(function(row) 
     { 
      _find(row, field, results); 
     }); 
    } 
    else 
    { 
     // if obj contains the field 
     if(obj[ tokens[ 0 ] ] !== undefined) 
     { 
      // if we're at the end of the dot path 
      if(tokens.length === 1) 
      { 
       results.push(obj[ tokens[ 0 ] ]); 
      } 
      else 
      { 
       // keep going down the dot path 
       _find(obj[ tokens[ 0 ] ], field.substr(field.indexOf('.') + 1), results); 
      } 
     } 
    } 
} 

以測試它:

var obj = { 
    document: { 
     payload: { 
      items:[ 
       {field1: 123}, 
       {field1: 456} 
       ] 
     } 
    } 
}; 
var results = []; 

_find(obj.document,'payload.items.field1', results); 
console.log(results); 

輸出

[ 123, 456 ]