🙆

ネストしたオブジェクトからキー指定で値を取り出したかった覚書

2020/11/17に公開2

動機

再帰で脳筋実装しても良いけど、なんとなくreduce使いたかった

やりたかった事

let data = {b: 500, g:{value: 30}, c:[32]}

こっから

let keys = ["g", "value"]

配列で探索パスを指定すると

search(data, keys)
// -> 30

値が取り出せる。

ゆるふわ実装

function search(data,keys){
    return keys.reduce((current, key) => {
        try{
            return current[key]
        } catch(e) {
            return undefined
        }
    }, data)
}

ぱっと見、クロージャで再帰してるのとそんなに変わらない

テストしてみる

できたっぽいのでテストしてみる

オブジェクトキーで探索

data = {b: 500, g:{value: 30}, c:[32]}
keys = ["b"];

search(data, keys);
// -> 500

ネストされたオブジェクトを探索

data = {b: 500, g:{value: 30}, c:[32]}
keys = ["g", "value"]

search(data, keys)
// -> 30

ネストされたオブジェクト/配列を探索

data = {b: 500, g:{value: 30}, c:[32]}
keys = ["c", 0]

search(data, keys)
// -> 32

キーが存在しなければundefined返す

data = {b: 500, g:{value: 30}, c:[32]}
keys = ["c", 100]

search(data, keys)
// -> undefined

まとめ

良い感じに動いた。

Discussion

standard softwarestandard software

以前、こちらの記事を書いたことがあります。

JavaScript オブジェクトの深くネストされているプロパティに安全にアクセスする getProperty - Qiita
https://qiita.com/standard-software/items/bb044217d0a4b394b8e2

やっていることは同じですが、
オブジェクトのプロパティパスを文字列指定してsplit('.')しています。

search関数は、存在判定にtry catch の例外を挟むのはちょっと動作コストがかさみそうでしたので、次のようにしてもいいのかもと思いました。

const objectToString = value => {
  return Object.prototype.toString.call(value);
};

const isObject = (value) => {
  if (objectToString(value) !== '[object Object]') {
    return false;
  }
  return true;
}

function search(data,keys){
    return keys.reduce((current, key) => {
      if (!isObject(current)) {
        return undefined;
      }
      if (!(key in current)) {
        return undefined;
      }
      return current[key]
    }, data)
}
okd.shokd.sh

なるほど。意図的に例外挟んじゃうとコスト重くなっちゃうんですね。
予測可能な場合は真面目に分岐を書くようにします!