. ドットつなぎのプロパティへのアクセス

階層構造のオブジェクトのプロパティに、文字列で指定してアクセスしたい場合ありますよね。

例えば、以下のobjで、nameを取得したい場合に

```js
var obj = {
  user: {
    name: 'hoge'
  }
}
```

以下で取得できるようにします。

```js
getValue(obj, 'user.name');
```

こんなことも。

```js
getValue(obj, 'user.name.first.ja');
```

設定する場合は

```js
setValue(obj, 'user.age', 43);
```

こんなことも。

```js
setValue(obj, 'user.name.first.en', 'Bob');
```


プロパティを持つか調べる場合は

```js
hasValue(obj, 'user.name');
```

プロパティを削除する場合は

```js
delValue(obj, 'user.name');
```


以下、プログラム全体

```js
"use strict";

/**
 * @desc obj[key] 取得
 * @param {Object} obj アクセスされるオブジェクト
 * @param {String} key アクセスするプロパティを表す文字列
 * @param {Any} [val=undefined] プロパティが存在しなかった場合の返り値
 * @return {Any} プロパティが存在した場合はその値を、無ければ<code>val</code>の値
 */
module.exports.getValue = function (obj, key, val) {
  return this.getObject(obj, key, function (obj, key) {
    obj = obj[0];
    if (obj === undefined) {
      return val;
    } else if (obj === null) {
      return val;
    } else if (obj[key] === undefined) {
      return val;
    } else {
      return obj[key];
    }
  }, true);
};

/**
 * @desc obj[key] 取得
 * @param {Object} obj アクセスされるオブジェクト
 * @param {String} key アクセスするプロパティを表す文字列
 * @return {Any} プロパティが存在した場合はその値
 */
module.exports.getValues = function (obj, key) {
  return this.getObject(obj, key, function (obj, key) {
    return obj.reduce(function (ary, v) {
      v[key] !== undefined && ary.push(v[key]);
      return ary;
    }, []);
  }, true);
};

/**
 * @desc obj[key] 設定
 * @param {Object} obj アクセスされるオブジェクト
 * @param {String} key アクセスするプロパティを表す文字列
 * @param {Any} val セットする値
 * @return {Boolean} プロパティを設定したかどうか？
 */
module.exports.setValue = function (obj, key, val) {
  return this.getObject(obj, key, function (obj, key) {
    return obj.reduce(function (memo, obj) {
      return obj && (obj[key] = val);
    }, false);
  }, false);
};

/**
 * @desc obj[key] 確認
 * @param {Object} obj アクセスされるオブジェクト
 * @param {String} key アクセスするプロパティを表す文字列
 * @return {Boolean} プロパティが存在するかどうか？
 */
module.exports.hasValue = function (obj, key) {
  return this.getObject(obj, key, function (obj, key) {
    return obj.reduce(function (memo, obj) {
      return memo || key in obj;
    }, false);
  }, true);
};

/**
 * @desc obj[key] 削除
 * @param {Object} obj アクセスされるオブジェクト
 * @param {String} key アクセスするプロパティを表す文字列
 * @return {Boolean} プロパティを削除できたか？
 */
module.exports.delValue = function (obj, key) {
  return this.getObject(obj, key, function (obj, key) {
    return obj.reduce(function (memo, obj) {
      return memo || delete obj[key];
    }, false);
  }, true);
};

/**
 * @desc obj[key]にアクセスできるように
 * @param obj
 * @param key
 * @param callback
 * @param readonly
 * @returns {*}
 */
module.exports.getObject = function (obj, key, callback, readonly) {

  var keys = key.split(".");
  var k = keys.pop();

  if (obj === undefined || obj === null) {
    obj = [];
  } else if (!(obj instanceof Array)) {
    obj = [obj];
  }

  obj = keys.reduce(function (obj, key) {
    return obj.reduce(function (ary, obj) {
      if (obj[key] !== undefined) {
        ary = ary.concat(obj[key]);
      } else if (!readonly) {
        obj[key] = {};
        ary.push(obj[key]);
      }
      return ary;
    }, []);
  }, obj);

  return callback(obj, k);
};
```