CoffeeScriptでJavascriptのオブジェクトをシンタックス文字列でフィルタリングする
オブジェクトをシンタックス文字列によってフィルタリングする関数を作っています。
obj =
users: [
{name:'takeda', age:'18'},
{name:'kanako', age:'19'},
{name:'sanada', age:'17'}
]
objectFilter(obj, 'users[*].age') # -> {users:[{age:18}, {age:19}, {age:17}]}
なんとか、頑張って以下のフィルタリング関数を作成することが出来ました。
フィルタリング関数
window.objectFilter = (source, syntax)->
recur = (src, obj, elems)->
elems = _.cloneDeep(elems)
# console.log _.cloneDeep(src), _.cloneDeep(obj), _.cloneDeep(elems)
elem = elems.shift()
return src if elem == undefined
[key, idx] = elem.split(':')
if idx == '*'
if key == '*'
for k of src
obj[k] = obj[k] || []
for i in [0...src?[k]?.length||0]
obj[k].push recur(src?[k]?[i], obj[k][i], elems)
else
obj[key] = obj[key] || []
for i in [0...src?[key]?.length||0]
obj[key][i] = obj[key][i] || {}
obj[key].push recur(src?[key]?[i], obj[key][i], elems)
else if idx != undefined
if key == '*'
for k of src
obj[k] = obj[k] || []
obj[k].push src[k][idx]
else
obj[key] = obj[key] || []
i = parseInt(idx)
obj[key].push recur(src?[key]?[i], obj[key][i], elems) if src?[key]?[i]
else
if key == '*'
obj = obj || {}
for k of src
obj[k] = obj[k] || {}
obj[k] = recur(src?[k], obj[k], elems)
else
obj = obj || {}
obj[key] = obj[key] || {}
obj[key] = recur(src?[key], obj[key], elems)
obj
result = {}
for v in syntax.replace(/[ ]/g,'').replace(/\]/g,'').replace(/\[/g,':').split(',')
recur(source, result, v.split('.'))
result
そして、このフィルタリング関数をテストするために以下の複雑なオブジェクトを作成し実行します。
フィルタリングするオブジェクト
obj =
hoge: 'yeah!'
fuga: {a:1, b:2, c:3}
piyo: [
{
name:'takeshi'
image: {src:'takeshi.png', info:'Smile of Takeshi'}
},
{
name:'misako'
image: {src:'misako001.png', info:'Angry of Misako'}
},
{
name:'yusuke'
image: {src:'3asdf_de.jpg', info:'happy yusuke'}
}
]
gaga: [{id:1, name:'michael'}, {id:2, name:'sally'}]
pepe: {a:[1,2,3], b:[4,5,6], c:[7,8,9]}
tata: {a:{token:'bdef', value:1}, b:{token:'gied', value:2}}
kaka: {a:[{x:'1',y:'2'},{x:'3',y:'4'}], b:[{x:'5',y:'6'},{x:'7',y:'8'}]}
シンタックスはカンマ区切りで複数指定することができる仕様になっています。
実行
syntax = 'hoge, piyo[2].image.src, gaga[*].name, ora.ora, hena[*], tata.*.value, pepe.*[1], kaka.*[*].x'
objectFilter(obj, syntax)
# 結果
# {
# hoge: 'yeah!',
# piyo: [ {image: {src: '3asdf_de.jpg'} ],
# gaga: [{name:'michael'}, {name:'sally'}],
# ora: {ora: null},
# hena: [],
# pepe: {a:[2], b:[5], c:[8]},
# tata: {a:{value:1}, b:{value:2}}
# kaka: {a:[{x:'1'},{x:'3'}], b:[{x:'5'},{x:'7'}]},
# }
恐らく、フィルタリング関数は以下のような条件を満たしているものになります。
- syntax文字列はカンマ区切りによって複数指定できる
piyo[2]
のようなシンタックスが指定された場合、配列とみなし3番目の要素にアクセスし、配列の要素数を1にするgaga[*]
やpepe.*
のようなシンタックスが指定された場合コレクションの全要素を対象としてアクセスするora.ora
のような存在しないオブジェクトが指定された場合、空のオブジェクトを作成するhena[*]
のような存在しない配列にアクセスが有った場合には空の配列を作成する- 指定したシンタックスの値がsourceオブジェクトに存在しなければnullを代入する
ちゃんと動作しているように思えます。
しかし、objectFilter(obj,'piyo[*].*.src')
を実行したときのデータが正しく取得できませんでした、コードをなんども睨みつけて格闘しているのですが、どうも上手く動作してくれません。
objectFilter(obj,'piyo[*].*.src')
# 結果
# 要素が3つから4つに増え、nameにsrc:undefinedが無く
# image.srcの値が全て同じにものになってしまっている
#
# {
# piyo:[
# {name:{}, image:{src:"3asdf_de.jpg"}},
# {name:{}, image:{src:"3asdf_de.jpg"}},
# {name:{}, image:{src:"3asdf_de.jpg"}},
# {name:{}, image:{src:"3asdf_de.jpg"}}
# ]
# }
どこがおかしいのでしょうか?