javascript convert dotnotation string into objects - javascript

I have a string like this "namespace.fun1.fun2.fun3" passed from the client. It's telling the server which function to use.
How do I safely run the function?
right now i'm doing:
var runthis = string.split('.')
namespace[runthis[1]][runthis[2]][runthis[3]]
How do I handle arbitrary depth of nesting safely?

A little function I wrote. I use it in most of my applications:
Object.lookup = (function _lookup() {
var cache = { };
return function _lookupClosure( lookup, failGracefully ) {
var check = null,
chain = [ ],
lastkey = '';
if( typeof lookup === 'string' ) {
if( cache[ lookup ] ) {
chain = cache[ lookup ].chain;
check = cache[ lookup ].check;
}
else {
lookup.split( /\./ ).forEach(function _forEach( key, index, arr ) {
if( check ) {
if( typeof check === 'object' ) {
if( key in check ) {
chain.push( check = check[ key ] );
lastkey = key;
}
else {
if( !failGracefully ) {
throw new TypeError( 'cannot resolve "' + key + '" in ' + lastkey );
}
}
}
else {
if( !failGracefully ) {
throw new TypeError( '"' + check + '" ' + ' does not seem to be an object' );
}
}
}
else {
lastkey = key;
chain.push( check = window[ key ] );
}
});
if( check ) {
cache[ lookup ] = {
chain: chain,
check: check
};
}
}
}
return {
execute: function _execute() {
return typeof check === 'function' ? check.apply( chain[chain.length - 2], arguments ) : null;
},
get: function _get() {
return check;
}
};
}
}());
usage:
Object.lookup( 'namespace.fun1.fun2.fun3' ).execute();
The first parameter is the object/method/property to resolve. The second (optional) parameter indicates whether or not the lookup() method shall fail silently or throw an exception if some property or object could not get resolved. default is 'throw'. To avoid that call
Object.lookup( 'namespace.fun1.fun2.fun3', true ).execute( 'with', 'paras' );
If .fun3 is a function, you can pass in any parameters into .execute() instead.
if you just want to receive the property value, you can also call .get() instead of .execute()
var val = Object.lookup( 'namespace.fun1.fun2.fun3' ).get();

(I may be misinterpreting the question, but this is what came to mind)
var s = "space.f.g.h.i.j.k.l.m",
a = s.split( "." ),
fn = eval( a[0] );
for ( var i = 1; i < a.length; i++ ) {
fn = fn[ a[i] ];
}
fn();
Note: this won't guard against the namespace being specified incorrectly or maliciously.

This should do:
var get = function(obj, key) {
var s = key.split('.')
, i = 1
, l = s.length;
for (; i < l; i++) {
obj = obj[s[i]];
if (!obj) return;
}
return obj;
};
get({hello:{world:{}}}, 'ns.hello.world');
edit: changed code a bit

Here's a simple for loop that should do find each object specified strating from the global scope, and then run the function it finds.
window.namespace = { fun1: { fun2: { fun3: function() { alert('hi!') }}}};
runFunc = function(address) {
var addressArray = address.split('.'),
current = window,
i = 0;
for (i = 0; i < addressArray.length; i++) {
current = current[addressArray[i]];
}
current();
};
runFunc('namespace.fun1.fun2.fun3');
http://jsfiddle.net/jfWra/1/
And here's some eror protection that will throw something meaningful if the value referenced doesnt exist or is not a function: http://jsfiddle.net/jfWra/2/

Here's another simple solution using a recursive function:
function run(str, context) {
var path = str.split(".")
if path.length == 1 {
context[path[0]].call()
return;
}
if(typeof context == 'undefined') {
context = window[path[0]]
} else {
context = context[path[0]]
}
run(path.splice(1).join('.'), context)
}

Two years later there is this module: https://github.com/daaku/nodejs-dotaccess.
var get = require("dotaccess").get;
var o = {a:{b:{c:'deep'}}};
console.log(get(o, 'a.b.c'));

Related

traversing a json for empty array value

i have a below json
{
"loanDetails": [
{
"vehicleDetail": {
"RCBookImageReferences": {
"imagePathReferences": [
{
}
]
}
},
"chargeDetails": [
{
}
],
"commissionDetails": [
{
}
],
"disbursementDetails": [
{
}
]
}
]
}
in the above json i need to traverse every key and if i find it emty then set the parent as empty array ie the output should be as below
{"loanDetails":[]}
i used the code below
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
But it did not give me the expected result.I'm stuck here any help will be much helpful.
The function clean takes an object and loops over its keys, calling clean recursively
on each object-valued property.
If the result of cleaning is an empty object, delete the key in question.
If the object itself turns out to be empty, return undefined, triggering deletion of the property holding that object at the higher level.
function clean(obj) {
var isEmpty = true;
for (var key in obj) {
var val = obj[key];
if (val === null || typeof val !== 'object' || (obj[key] = clean(val))) {
isEmpty = false;
} else {
delete obj[key];
}
}
return isEmpty ? undefined : obj;
}
>> a = { x: 1, b: { y: [] }, c: { d: { } } }
>> clean(a)
<< Object {x: 1}
This should make it recursive. With two solutions.
Solution 1: empty test function
var boolValue = true;
for(var prop in obj) {
if(obj.hasOwnProperty(prop) && typeof obj[prop] === 'object')
{
boolValue = recursiveIsEmpty(obj[prop]);
}
else
{
return false;
}
}
return boolValue ;
//test and set empty string
recursiveIsEmpty(jsonDataObj['loanDetails']) ? jsonDataObj['loanDetails'] = [] : null;
Solution 2 recursive empty function that empties parent obj
function recursiveIsEmpty(obj) {
var boolValue = true;
for(var prop in obj) {
if(obj.hasOwnProperty(prop) && typeof obj[prop] === 'object')
{
boolValue = recursiveIsEmpty(obj[prop]);
if (boolValue)
{
delete obj[prop]; //an object is empty. Delete from parent;
}
}
else
{
return false;
}
}
return boolValue; //returns an empty object
}
recursiveIsEmpty(jsonDataObj['loanDetails']) //returns jsonDataObj['loanDetails'] = [];
This checks if obj has a property that is an object. If so load that object and check it's properties. If not return false, because that will be string or number and that confirms the object is not empty.
Your JSON-string is not valid. When corrected, you can use a reviver function parameter (see MDN) to remove 'empty' arrays (aka properties with criteria you specify).
To be clear, the reviver function takes care of the traversing on all levels of the parsed object. If it returns undefined the property is removed from the object. The reviver used in the snippet thus removes all properties containing arrays with empty objects, or empty arrays.
The snippet demonstrates this.
// json string corrected
var foo = '{"loanDetails": [{"vehicleDetail": {"RCBookImageReferences": '+
'{"imagePathReferences": [{}]}}, "chargeDetails": [{}],'+
'"commissionDetails": [{}],"disbursementDetails": [{}]}]}';
// parse it, using reviver parameter
var fooparsed = JSON.parse( foo,
function (key, value) { //<= reviver here
return (value.length && value.length == 1 &&
value[0] instanceof Object &&
Object.keys(value[0]).length == 0) ||
value instanceof Array && !value.length
? undefined : value;
}
);
// print
Helpers.log2Screen( Object.print(fooparsed) );
<script src="http://kooiinc.github.io/JSHelpers/Helpers-min.js"></script>
if you are doing this using ajax then you should go with seriallizing the jason array using javascript.
at the time of passing data through json
data: "your data",
use this
data:$(form).serialize(),
it will pass all the key of that form which you are passing ,
if you want to see its result the try to print it on console
var inputObj = {
"loanDetails": [{
"vehicleDetail": {
"RCBookImageReferences": {
"imagePathReferences": [{}]
}
},
"chargeDetails": [{}],
"commissionDetails": [{}],
"disbursementDetails": [{}]
}, {
"vehicleDetail": {
"RCBookImageReferences": {
"imagePathReferences": [{
"Valid": "Working"
}]
}
},
"chargeDetails": [{}],
"commissionDetails": [{}],
"disbursementDetails": [{}]
}],
"Superman": {
"Name": ""
},
"SpiderMan": {
"Name": "Senthil"
}
}
function flatten(target, opts) {
var output = {},
opts = opts || {},
delimiter = opts.delimiter || '.'
function getkey(key, prev) {
return prev ? prev + delimiter + key : key
};
function step(object, prev) {
Object.keys(object).forEach(function(key) {
var isarray = opts.safe && Array.isArray(object[key]),
type = Object.prototype.toString.call(object[key]),
isobject = (type === "[object Object]" || type === "[object Array]")
if (!isarray && isobject) {
return step(object[key], getkey(key, prev))
}
output[getkey(key, prev)] = object[key]
});
if (Object.keys(object) == "") {
if (object instanceof Array) {
output[prev] = [];
} else {
output[prev] = {};
}
}
};
step(target)
return output
};
function unflatten(target, opts) {
var opts = opts || {},
delimiter = opts.delimiter || '.',
result = {}
if (Object.prototype.toString.call(target) !== '[object Object]') {
return target
}
function getkey(key) {
var parsedKey = parseInt(key)
return (isNaN(parsedKey) ? key : parsedKey)
};
Object.keys(target).forEach(function(key) {
var split = key.split(delimiter),
firstNibble, secondNibble, recipient = result
firstNibble = getkey(split.shift())
secondNibble = getkey(split[0])
while (secondNibble !== undefined) {
if (recipient[firstNibble] === undefined) {
recipient[firstNibble] = ((typeof secondNibble === 'number') ? [] : {})
}
recipient = recipient[firstNibble]
if (split.length > 0) {
firstNibble = getkey(split.shift())
secondNibble = getkey(split[0])
}
}
// unflatten again for 'messy objects'
recipient[firstNibble] = unflatten(target[key])
});
//Array Check
var keys = Object.keys(result);
if (keys.length > 0 && keys[0] === "0") {
var output = [];
keys.forEach(function(key) {
output.push(result[key])
});
return output;
}
return result
};
var flatted = flatten(inputObj);
var keys = Object.keys(flatted);
keys.forEach(function(key) {
if (JSON.stringify(flatted[key]) === "{}" || JSON.stringify(flatted[key]) == "") {
// console.log(key)
delete flatted[key];
var paths = key.split(".");
if (paths.length >= 2) {
var int = parseInt(paths[1])
if (isNaN(int)) {
key = paths[0];
flatted[key] = {};
} else {
key = paths[0] + "." + int;
flatted[key] = {};
}
var newKeys = Object.keys(flatted);
for (var j = 0; j < newKeys.length; j++) {
var omg = newKeys[j];
if (omg.indexOf(key) != -1 && omg.length > key.length) {
delete flatted[key];
}
}
}
}
})
console.log(flatted)
var output = unflatten(flatted);
alert(JSON.stringify(output))

ember-data.js failure on script init

I'm using all of the latest dependencies for emberjs and yet I end up with an error on initializing the scripts in the page at ember-data-latest.min.js with:
Uncaught TypeError: Object function () {...} has no method 'registerInjection'
Before even writing a single line for my app. Why is that?
Appreciate any kind help.
Here's my script list:
jquery-1.10.2.min.js
handlebars-v1.3.0.js
ember-1.5.1.js
ember-states.js
ember-data-latest.min.js
And here's the full error:
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, nullDescriptor);
o_defineProperty(this, '__nextSuper', undefinedDescriptor);
var m = meta(this), proto = m.proto;
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
if (typeof properties !== 'object' && properties !== undefined) {
throw new Ember.Error("Ember.Object.create only accepts objects.");
}
if (!properties) { continue; }
var keyNames = Ember.keys(properties);
for (var j = 0, ll = keyNames.length; j < ll; j++) {
var keyName = keyNames[j];
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
Ember.assert("`actions` must be provided at extend time, not at create " +
"time, when Ember.ActionHandler is used (i.e. views, " +
"controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this)));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
this.init.apply(this, arguments);
m.proto = proto;
finishChains(this);
sendEvent(this, "init");
} has no method 'registerInjection'
You need to make sure to use the latest version which gets published at Ember build site.
The latest build for Ember Data is found here.

How to restore circular references (e.g. "$id") from Json.NET-serialized JSON?

Is there an existing javascript library which will deserialize Json.Net with reference loop handling?
{
"$id": "1",
"AppViewColumns": [
{
"$id": "2",
"AppView": {"$ref":"1"},
"ColumnID": 1,
}
]
}
this should deserialize to an object with a reference loop between the object in the array and the outer object
The answers given almost worked for me, but the latest version of MVC, JSON.Net, and DNX uses "$ref" and "$id", and they may be out of order. So I've modified user2864740's answer.
I should note that this code does not handle array references, which are also possible.
function RestoreJsonNetReferences(g) {
var ids = {};
function getIds(s) {
// we care naught about primitives
if (s === null || typeof s !== "object") { return s; }
var id = s['$id'];
if (typeof id != "undefined") {
delete s['$id'];
// either return previously known object, or
// remember this object linking for later
if (ids[id]) {
throw "Duplicate ID " + id + "found.";
}
ids[id] = s;
}
// then, recursively for each key/index, relink the sub-graph
if (s.hasOwnProperty('length')) {
// array or array-like; a different guard may be more appropriate
for (var i = 0; i < s.length; i++) {
getIds(s[i]);
}
} else {
// other objects
for (var p in s) {
if (s.hasOwnProperty(p)) {
getIds(s[p]);
}
}
}
}
function relink(s) {
// we care naught about primitives
if (s === null || typeof s !== "object") { return s; }
var id = s['$ref'];
delete s['$ref'];
// either return previously known object, or
// remember this object linking for later
if (typeof id != "undefined") {
return ids[id];
}
// then, recursively for each key/index, relink the sub-graph
if (s.hasOwnProperty('length')) {
// array or array-like; a different guard may be more appropriate
for (var i = 0; i < s.length; i++) {
s[i] = relink(s[i]);
}
} else {
// other objects
for (var p in s) {
if (s.hasOwnProperty(p)) {
s[p] = relink(s[p]);
}
}
}
return s;
}
getIds(g);
return relink(g);
}
I'm not aware of existing libraries with such support, but one could use the standard JSON.parse method and then manually walk the result restoring the circular references - it'd just be a simple store/lookup based on the $id property. (A similar approach can be used for reversing the process.)
Here is some sample code that uses such an approach. This code assumes the JSON has already been parsed to the relevant JS object graph - it also modifies the supplied data. YMMV.
function restoreJsonNetCR(g) {
var ids = {};
function relink (s) {
// we care naught about primitives
if (s === null || typeof s !== "object") { return s; }
var id = s['$id'];
delete s['$id'];
// either return previously known object, or
// remember this object linking for later
if (ids[id]) {
return ids[id];
}
ids[id] = s;
// then, recursively for each key/index, relink the sub-graph
if (s.hasOwnProperty('length')) {
// array or array-like; a different guard may be more appropriate
for (var i = 0; i < s.length; i++) {
s[i] = relink(s[i]);
}
} else {
// other objects
for (var p in s) {
if (s.hasOwnProperty(p)) {
s[p] = relink(s[p]);
}
}
}
return s;
}
return relink(g);
}
And the usage
var d = {
"$id": "1",
"AppViewColumns": [
{
"$id": "2",
"AppView": {"$id":"1"},
"ColumnID": 1,
}
]
};
d = restoreJsonNetCR(d);
// the following works well in Chrome, YMMV in other developer tools
console.log(d);
DrSammyD created an underscore plugin variant with round-trip support.
Ok so I created a more robust method which will use $id as well as $ref, because that's actually how json.net handles circular references. Also you have to get your references after the id has been registered otherwise it won't find the object that's been referenced, so I also have to hold the objects that are requesting the reference, along with the property they want to set and the id they are requesting.
This is heavily lodash/underscore based
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['lodash'], factory);
} else {
factory(_);
}
})(function (_) {
var opts = {
refProp: '$ref',
idProp: '$id',
clone: true
};
_.mixin({
relink: function (obj, optsParam) {
var options = optsParam !== undefined ? optsParam : {};
_.defaults(options, _.relink.prototype.opts);
obj = options.clone ? _.clone(obj, true) : obj;
var ids = {};
var refs = [];
function rl(s) {
// we care naught about primitives
if (!_.isObject(s)) {
return s;
}
if (s[options.refProp]) {
return null;
}
if (s[options.idProp] === 0 || s[options.idProp]) {
ids[s[options.idProp]] = s;
}
delete s[options.idProp];
_(s).pairs().each(function (pair) {
if (pair[1]) {
s[pair[0]] = rl(pair[1]);
if (s[pair[0]] === null) {
if (pair[1][options.refProp] !== undefined) {
refs.push({ 'parent': s, 'prop': pair[0], 'ref': pair[1][options.refProp] });
}
}
}
});
return s;
}
var partialLink = rl(obj);
_(refs).each(function (recordedRef) {
recordedRef['parent'][recordedRef['prop']] = ids[recordedRef['ref']] || {};
});
return partialLink;
},
resolve: function (obj, optsParam) {
var options = optsParam !== undefined ? optsParam : {};
_.defaults(options, _.resolve.prototype.opts);
obj = options.clone ? _.clone(obj, true) : obj;
var objs = [{}];
function rs(s) {
// we care naught about primitives
if (!_.isObject(s)) {
return s;
}
var replacementObj = {};
if (objs.indexOf(s) != -1) {
replacementObj[options.refProp] = objs.indexOf(s);
return replacementObj;
}
objs.push(s);
s[options.idProp] = objs.indexOf(s);
_(s).pairs().each(function (pair) {
s[pair[0]] = rs(pair[1]);
});
return s;
}
return rs(obj);
}
});
_(_.resolve.prototype).assign({ opts: opts });
_(_.relink.prototype).assign({ opts: opts });
});
I created a gist here

JS: How to return 'undefined' instead of throwing error 'cannot read property x of undefined'

What is the best way to have js return undefined rather than throw an error when a parent property does not exist?
Example
a = {}
b = a.x.y.z
// Error: Cannot read property 'value' of undefined
// Target result: b = undefined
You have to check for the existence of each property:
var b;
if (a.x && a.x.y && a.x.y.z) {
b = a.x.y.z
}
Or, simliar to another poster's "safeGet" function:
var get = function (obj, ns) {
var y = ns.split('.');
for(var i = 0; i < y.length; i += 1) {
if (obj[y[i]]) {
obj = obj[y[i]];
} else {
return;
}
}
return obj;
};
Use:
var b = get(a, 'x.y.z');
try {
a = {}
b = a.x.y.z
}
catch (e) {
b = void 0;
}
I would go for slightly verbose:
var b = ((a.x || {}).y || {}).z
you could write a safeGet helper function, something like:
edited for drilldown as suggested in comments by arcyqwerty
var getter = function (collection, key) {
if (collection.hasOwnProperty(key)) {
return collection[key];
} else {
return undefined;
}
};
var drillDown = function (keys, currentIndex, collection) {
var max = keys.length - 1;
var key = keys[currentIndex];
if (typeof collection === 'undefined') {
return undefined;
}
if (currentIndex === max) {
return getter(collection, key);
} else {
return drillDown(keys, currentIndex + 1,
getter(collection, key));
}
};
var safeGet = function (collection, key) {
if (key.indexOf(".") !== -1) {
return drillDown(key.split("."), 0, collection);
} else {
return getter(collection, key);
}
};
a = { x: 1 };
b = safeGet(a, 'x.y.z');
http://jsfiddle.net/YqdWH/2/

JavaScript fake dictionaries

I've declared Javascript arrays in such a way that I could then access them by a key, but it was a long time ago, and I've forgotten how I did it.
Basically, I have two fields I want to store, a unique key, and its value. I know there is a way to do it.. something like:
var jsArray = new {key: 'test test', value: 'value value'},
new {key: 'test 2', value: 'value 2'};
and accessed like:
value = jsArray[key]
Can someone remind me?
You can do it in different ways:
var a = {'a':0, 'b':1, 'c':2};
var b = new Array();
b['a'] = 0;
b['b'] = 1;
b['c'] = 2;
var c = new Object();
c.a = 0;
c.b = 1;
c.c = 2;
var myFancyDictionary = {
key: 'value',
anotherKey: 'anotherValue',
youGet: 'the idea'
}
If you are already using Prototype, try using its Hash. If using jQuery, try using Map.
Here is a JavaScript class that provides a simple dictionary.
if( typeof( rp ) == "undefined" ) rp = {};
rp.clientState = new function()
{
this.items = new Object();
this.length = 0;
this.set = function( key, value )
{
if ( ! this.keyExists( key ) )
{
this.length++;
}
this.items[ key ] = value;
}
this.get = function( key )
{
if ( this.keyExists( key ) )
{
return this.items[ key ];
}
}
this.keyExists = function( key )
{
return typeof( this.items[ key ] ) != "undefined";
}
this.remove = function( key )
{
if ( this.keyExists( key ) )
{
delete this.items[ key ];
this.length--;
return true;
}
return false;
}
this.removeAll = function()
{
this.items = null;
this.items = new Object();
this.length = 0;
}
}
Example use:
// Add a value pair.
rp.clientState.set( key, value );
// Fetch a value.
var x = rp.clientState.Get( key );
// Check to see if a key exists.
if ( rp.clientState.keyExists( key )
{
// Do something.
}
// Remove a key.
rp.clientState.remove( key );
// Remove all keys.
rp.clientState.removeAll();

Categories