任意の数のレベルの深さで、既存のプロパティを持つことができるオブジェクトがあります。例えば:
var obj = {
db: {
mongodb: {
Host: 'localhost'
}
}
};
その上で、次のようにプロパティを設定(または上書き)したいと思います。
set('db.mongodb.user', 'root');
// or:
set('foo.bar', 'baz');
プロパティ文字列は任意の深さを持つことができ、値は任意のタイプ/ものにすることができます。
プロパティキーが既に存在している場合、値としてのオブジェクトと配列をマージする必要はありません。
前の例では、次のオブジェクトが生成されます。
var obj = {
db: {
mongodb: {
Host: 'localhost',
user: 'root'
}
},
foo: {
bar: baz
}
};
どうすればこのような機能を実現できますか?
以前のプロパティが必要な関数が必要な場合は、次のようなものを使用できます。また、ネストされたプロパティを検索して設定できたかどうかを示すフラグを返します。
function set(obj, path, value) {
var parts = (path || '').split('.');
// using 'every' so we can return a flag stating whether we managed to set the value.
return parts.every((p, i) => {
if (!obj) return false; // cancel early as we havent found a nested prop.
if (i === parts.length - 1){ // we're at the final part of the path.
obj[parts[i]] = value;
}else{
obj = obj[parts[i]]; // overwrite the functions reference of the object with the nested one.
}
return true;
});
}
再帰を使用して、ClojureScriptのassoc-in
( https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L528 )に触発され、
/**
* Associate value (v) in object/array (m) at key/index (k).
* If m is falsy, use new object.
* Returns the updated object/array.
*/
function assoc(m, k, v) {
m = (m || {});
m[k] = v;
return m;
}
/**
* Associate value (v) in nested object/array (m) using sequence of keys (ks)
* to identify the path to the nested key/index.
* If one of the values in the nested object/array doesn't exist, it adds
* a new object.
*/
function assoc_in(m={}, [k, ...ks], v) {
return ks.length ? assoc(m, k, assoc_in(m[k], ks, v)) : assoc(m, k, v);
}
/**
* Associate value (v) in nested object/array (m) using key string notation (s)
* (e.g. "k1.k2").
*/
function set(m, s, v) {
ks = s.split(".");
return assoc_in(m, ks, v);
}
注意:
提供された実装により、
assoc_in({"a": 1}, ["a", "b"], 2)
戻り値
{"a": 1}
この場合はエラーをスローすることをお勧めします。必要に応じて、assoc
にチェックを追加して、m
がオブジェクトまたは配列であることを確認し、それ以外の場合はエラーをスローできます。