膨大なコレクションがあり、コレクション内のどこかにキーでプロパティを探しています。そのキー/インデックスを含むすべてのオブジェクトへの参照またはフルパスのリストを取得するための信頼できる方法は何ですか? 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関数があり、2つのネストの深さであるネストされたオブジェクトを検索しますが、その後失敗するようです。 (例 http://codepen.io/anon/pen/bnqyh )
これはそれを行う必要があります:
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;
}
純粋な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}]
これにより、オブジェクトの配列(hay)で値(針)が検索され、結果の配列が返されます...
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;
}
プレーンなJS(またはlodashの組み合わせ)で再帰関数を(パフォーマンスで)最適に作成できるが、あなたの側から再帰をスキップし、単純な可読コード(これはパフォーマンスに応じて最適になります)、オブジェクトを再帰的にトラバースする必要があるあらゆる目的で lodash#cloneDeepWith を使用できます。
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.Push(v)}) && res
)
そのため、_.cloneDeepWith
の2番目の引数として渡すコールバックは、すべてのkey/value
のペアを再帰的にトラバースし、あなたがしなければならないことは、それぞれに対して行う操作だけです。上記のコードは、あなたのケースの一例です。これが実際の例です:
var object = {
prop1: 'ABC1',
prop2: 'ABC2',
prop3: {
prop4: 'ABC3',
prop5Arr: [{
prop5: 'XYZ'
},
{
prop5: 'ABC4'
},
{
prop6: {
prop6NestedArr: [{
prop1: 'XYZ Nested Arr'
},
{
propFurtherNested: {key100: '100 Value'}
}
]
}
}
]
}
}
let findValuesDeepByKey = (obj, key, res = []) => (
_.cloneDeepWith(obj, (v,k) => {k==key && res.Push(v)}) && res
)
console.log(findValuesDeepByKey(object, 'prop1'));
console.log(findValuesDeepByKey(object, 'prop5'));
console.log(findValuesDeepByKey(object, 'key100'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Deepdashを使用すると、 pickDeep から paths を取得できます。または indexate (build-> valueオブジェクト)
var obj = { 'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}
var cherry = _.pickDeep(obj,"d");
console.log(JSON.stringify(cherry));
// {"cc":{"d":{}},"dd":{"d":{}}}
var paths = _.paths(cherry);
console.log(paths);
// ["cc.d", "dd.d"]
paths = _.paths(cherry,{pathFormat:'array'});
console.log(JSON.stringify(paths));
// [["cc","d"],["dd","d"]]
var index = _.indexate(cherry);
console.log(JSON.stringify(index));
// {"cc.d":{},"dd.d":{}}
このようなものが機能し、それをオブジェクトに変換して下に再帰します。
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
以下がその方法です。
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 ]
Array.prototype.findpath = function(item,path) {
return this.find(function(f){return item==eval('f.'+path)});
}