I'm looking for suggestions on how to select properties on the child level or deeper dynamically.
For example, here is the method that I am currently using. The method parameters is the object
and either a string
or array
for the property name. If a string
is passed used, it will just will get the property. The array is iterated through in order to select the property value.
getMappedDataProperty: function(obj, map) {
var m, result, _i, _len;
if (typeof map === 'string') {
return obj[map];
}
result = obj;
for (_i = 0, _len = map.length; _i < _len; _i++) {
m = map[_i];
result = result[m];
}
return result;
}
Even though this works, I keep thinking to myself that there has to be a better way of accomplishing this. Suggestions?
2 Answers 2
From a once over:
getMappedDataProperty
is a clumsy name, how aboutgetProperty
.receiving a string is functionally the same as an array with 1 entry, so you could
map = (typeof map === 'string') ? [map] : map;
There is no need for the underscores for
i
andlen
, also, I would rather seelength
thanlen
the use of the temporary variable
m
is overkill, you could go straight for
result = result[map[i]];
I would counter propose:
getProperty: function( o, map )
{
map = (typeof map === 'string') ? [map] : map.slice();
var result = o;
while ( map.length ) {
result = result[ map.shift() ];
}
return result;
}
I would love to know the context in which you use this, since I cannot see a good use case for it.
-
\$\begingroup\$ Good point on the name. Didn't think about the while loop working for just one in the array. Why do you have the
map.slice()
in the example on the third line? \$\endgroup\$chafnan– chafnan2014年01月24日 20:07:58 +00:00Commented Jan 24, 2014 at 20:07 -
\$\begingroup\$ I marked as answered, but would still like to see others opinions about this. \$\endgroup\$chafnan– chafnan2014年01月24日 20:31:28 +00:00Commented Jan 24, 2014 at 20:31
-
\$\begingroup\$ To answer what I am doing: I created a generic filter that will output a flat array of a specific property value of the dataset. The issue is that some of the properties are at the child level and others at a grandchild level. Needed to handle getting for both levels. \$\endgroup\$chafnan– chafnan2014年01月24日 20:34:46 +00:00Commented Jan 24, 2014 at 20:34
I'm going to suggest on top of @konijn's changes that:
- You shouldn't clone the
map
as that's just extra overhead as is using shift. I prefer your earlier attempt of just iterating the keys to be honest. - You should check if
result[map[i]]
exists before you attempt to access it. SogetProperty({}, ['foo', 'bar'])
won't attempt to accessresult['foo'] => undefined['bar']
. As I mentioned in a comment on the OP I've previously implemented this method and I handled this case by returningundefined
but at the bare minimum you should acknowledge it. - I would also suggest you handle numbers in your implementation.
I would suggest an implementation along the lines of:
getProperty: function(result, map) {
var type = typeof map, i = 0, len = map.length;
if (type === 'string' || type === 'number') return result[map];
for (; i < len; i++) {
if(map[i] in result) result = result[map[i]];
else return undefined;
}
return result;
}
Later if you decide to handle a map string like 'foo.bar'
it would also be easy to extend this implementation (and it'd start to look even more like mine lol). All it would take is adapting the string case to map = map.toString().split('.')
-
\$\begingroup\$ +1, I forgot about the fact that the function is very trusting! \$\endgroup\$konijn– konijn2014年01月24日 22:32:36 +00:00Commented Jan 24, 2014 at 22:32
'foo.bar[0]' => ['foo', 'bar', '0']
\$\endgroup\$