web-dev-qa-db-ja.com

プロパティ名でオブジェクトの値を再帰的に検索します

プロパティ名を検索し、見つかったときにその値を返すユーティリティ関数を作成しています。これは再帰的に行う必要があります:

// Function
util.findVal = (object, propName) => {
  for (let key in object) {
    if (key === propName) {
      console.log(propName)
      console.log(object[key])
      return object[key]
    } else {
      util.findVal(object[key], propName)
    }
  }
}

// Input
object: {
  photo: {
    progress: 20
  }
}

// Usage
util.findVal(object, 'progress')

ただし、コンソールログは永久に保存され、ブラウザがクラッシュします。何が悪いのですか?

編集:

これは私が関数を呼び出す方法です:

// Input

item: {
  photo: {
    file: {},
    progress: 20
  }
}

this.findProgress(item)

methods: {
  findProgress (item) {
    return util.findVal(item, this.propName)
  }
}
17
alex

Object.keys そして Array#some

function findVal(object, key) {
    var value;
    Object.keys(object).some(function(k) {
        if (k === key) {
            value = object[k];
            return true;
        }
        if (object[k] && typeof object[k] === 'object') {
            value = findVal(object[k], key);
            return value !== undefined;
        }
    });
    return value;
}

var object =  { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));
26
Nina Scholz

コードにいくつかのエラーがあります:

  • _util.findVal_を再帰的に呼び出していますが、呼び出しの結果を返していません。コードはreturn util.findVal(...)である必要があります
  • 属性名keyを再帰呼び出しに渡していない
  • 参照ループの可能性を処理していない
  • オブジェクトにキーが含まれ、キーを含むサブオブジェクトが返される場合、返される値はランダムです(キーが分析される順序によって異なります)。

3番目の問題は、無限再帰の原因となるものです。次に例を示します。

_var obj1 = {}, obj2 = {};
obj1.x = obj2; obj2.y = obj1;
_

_obj1_または_obj2_を再帰的に検索し続けるだけでは、無限再帰が発生する可能性があります。

残念ながら、JavaScriptではっきりしない理由により、オブジェクトの「アイデンティティ」を知ることは不可能です...(Python id(x)が行うこと)オブジェクトを別のオブジェクトとしか比較できませんこれは、オブジェクトが過去にすでに見られたかどうかを知るには、既知のオブジェクトを使用した線形スキャンが必要であることを意味します。

ES6では、オブジェクトをキーとして使用できるSetおよびMapを使用してオブジェクトの同一性をチェックする機能が追加されました。これにより、より高速な(準線形)検索時間が可能になります。

深さ順に実行される検索ソリューションは、たとえば次のようになります。

_function findVal(obj, key) {
    var seen = new Set, active = [obj];
    while (active.length) {
        var new_active = [], found = [];
        for (var i=0; i<active.length; i++) {
            Object.keys(active[i]).forEach(function(k){
                var x = active[i][k];
                if (k === key) {
                    found.Push(x);
                } else if (x && typeof x === "object" &&
                           !seen.has(x)) {
                    seen.add(x);
                    new_active.Push(x);
                }
            });
        }
        if (found.length) return found;
        active = new_active;
    }
    return null;
}
_

オブジェクトと属性名を指定すると、最初に見つかった深さでその名前で見つかったすべての値が返されます(複数の値が存在する場合があります。たとえば、キー_{x:{z:1}, y:{z:2}}_で_"z"_を検索する場合)値は同じ深さです)。

この関数は、自己参照構造も正しく処理し、無限検索を回避します。

6
6502

このようなelseステートメントを変更してみてください

return util.findVal(object[key],propName)

2
Sagar

この質問は、オブジェクトに階層のどこかに(キーに関係なく)特定の値が含まれているかどうかを確認する一般的なソリューションが必要な領域で見つかりました。したがって、以下はOPの質問に直接回答したり、他のソリューションを改善したりするものではありませんが、私が行ったのと同じことを探してこの投稿を見つける他の人を助ける可能性があります。

function hasValue(object, value) {
    return Object.values(object).some(function(val) {
        if (val === value) {
            return true;
        }
        if (val && typeof val === 'object') {
            return hasValue(val, value);
        }
        if (val && typeof val === 'array') {
            return val.some((obj) => {
                return hasValue(obj, value);
            })
        }
    });
}

もちろん、@ Nina Scholzのソリューションに触発されています!

1
exside

プロパティとサブプロパティのオブジェクトツリー内のどこかでプロパティ名を再帰的に検索したいと言っていると思います。もしそうなら、これが私がこれに取り組む方法です:

var object1 = _getInstance(); // somehow we get an object
var pname = 'PropNameA';

var findPropertyAnywhere = function (obj, name) {
    var value = obj[name];
    if (typeof value != 'undefined') {
        return value;
    }
    foreach(var key in obj) {
        var v2 = findPropertyAnywhere(obj[key], name);
        if (typeof v2 != 'undefined') {
            return v2;
        }
    }
    return null;
}
findPropertyAnywhere(object1, pname);
1
Glenn Ferrie

回避できる場合は、独自のユーティリティを作成しないでください。

jsonpath のようなものを使用します

サポートされている構文の例:

JSONPath                   Description
$.store.book[*].author      The authors of all books in the store
$..author                   All authors
$.store.*                   All things in store, which are some books and a red bicycle
$.store..price              The price of everything in the store
$..book[2]                  The third book
$..book[(@.length-1)]       The last book via script subscript
$..book[-1:]                The last book via slice
$..book[0,1]                The first two books via subscript union
$..book[:2]             The first two books via subscript array slice
$..book[?(@.isbn)]          Filter all books with isbn number    
1
Andrei Gec

キーが見つからない場合は、考えてみてください。

検索の代わりにこのようなことができると思います

return object[propName] || null 

コードにブレークポイントがありませんでした。直接関連する属性だけでなく、オブジェクト全体を検索しようとしていると思います。ここにコードの編集があります。

編集:

util.findVal = (object, propName) =>{
 if(!!object[propName]){
   return object[propName]
 }else{
   for (let key in object) {
     if(typeof object[key]=="object"){
      return util.findVal(object[key], propName)
     }else{
      return null
     }
  }
 }
}
1
Ryad Boubaker

指定された名前のフィールドのを返します。

dataはルートノード/オブジェクトです。 keyNameは、フィールド/メンバーの文字列名です。

keyNameがそれ自体がオブジェクトであるフィールドを指定している場合、そのオブジェクトが返されます。

function find (data, keyName) {
  for (const key in data) {

    const entry = data[key]
    if (key === keyName)
      return entry

    if (typeof entry === 'object') {
      const found = find(entry, keyName)

      if (found)
        return found
    }
  }
}

forループは各フィールドを通過し、そのフィールドがオブジェクトの場合、そのオブジェクトに再帰します。

0
Paul Sumpner

答えは、取得したい複雑さに依存します。たとえば、JSON解析済み配列には関数が含まれていません-そして、オブジェクトツリーの親ノードに設定されたプロパティ値が含まれていないことはかなり確実です。

このバージョンは、オブジェクトツリーの検索中に見つかった最初のプロパティ名のプロパティ値を返します。 undefinedは、名前付きプロパティが見つからなかったか、値がundefinedの場合に返されます。違いを伝えるには、いくつかの変更が必要になります。すでに検索されている親ノードを再検索したり、nullオブジェクトのスキャンを試みたりすることはありません。

let util = {};

util.findVal = (object, propName, searched=[]) => {
  searched.Push( object)
  for (let key in object) {
    if (key === propName) {
      return object[key]
    } 
    else {
      let obj = object[ key]
      if( obj && (typeof obj == "object" || typeof obj == "function")) {
          if( searched.indexOf(obj) >=0) {
              continue
          }
          let found = util.findVal(obj, propName, searched)
          if( found != searched) {
              return found
          }
      }
    }
  }
  searched.pop();
  // not in object:
  return searched.length ? searched : undefined
}
0
traktor53

私はこれが古い投稿であることを知っていますが、キーによって値を再帰的に見つけることで発生した問題に答えることが役立つと思いました。私はニーナ・ショルツの答えをさらに発展させ、次のことを思いつきました。再帰的に呼び出されるたびにすべてのキーの配列を作成するわけではないので、より高速になるはずです。また、キーが見つからない場合は、明示的にfalseを返します。

function findVal(obj, keyToFind) {
  if (obj[keyToFind]) return obj[keyToFind];

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      const value = findVal(obj[key], keyToFind);
      if (value) return value;
    }
  }
  return false;
}

var object =  { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));
0
Lee Morris

古い質問ですが、オブジェクトの階層でプロパティがどこかに存在するかどうかを確認するには、この単純なオプションを試してください

var obj = {
  firstOperand: {
    firstOperand: {
      firstOperand: {
        sweptArea: 5
      }
    }
  }
};

function doesPropertyExists ( inputObj, prop )
{
  return JSON.stringify(obj).indexOf( "\""+ prop +"\":" ) != -1;
};

console.log( doesPropertyExists( obj, "sweptArea" ) );
console.log( doesPropertyExists( obj, "firstOperand" ) );
console.log( doesPropertyExists( obj, "firstOperand22" ) );
0
gurvinder372

私はこの関数を書いてしまいました。これは、次の場所にある関数のリファクタリングです。 再帰的にオブジェクトをループしてプロパティリストを作成

chrome devtoolsにスタックオーバーフローを回避するための深度パラメーターを追加しました。

function iterate(obj, context, search, depth) {
    for (var property in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, property)) {
            if(typeof obj[property] == 'function') continue;

            if( property == search ){
                console.log(context+property);
                return;
            }

            if (typeof obj[property] == "object" && depth < 7) {
                //console.log('--- going in: ' + context+property);
                iterate(obj[property], context+property+'.', search, depth+1);
            }
            /*else {
                console.log(context+property);
            }*/
        }
    }
}
0
elewinso