///
For example, {foo: {bar: 'baz'}} will become flattened into '['foo', 'foo.bar'].
*
*
Arrays can also be flattened.
* Their flattened keys will take the form of 'myArray[0]' and 'myArray[0].myNestedProperty'.
*/
public static flatten(object:any):Array For example, the key 'foo.bar' would return "baz" for the object For example, writing "baz" to the key 'foo.bar' would result in an object {foo: {bar: "baz"}}.
* The key 'foo[1].baz' would return 2 for the object {foo: [{bar: 1}, {baz: 2}]}.
*/
public static read(flattenedKey:string, object:any):any {
var keys:Array{foo: {bar: "baz"}}.
* Writing 3 to the key 'foo[0].bar' would result in an object {foo: [{bar: 3}]}.
*/
public static write(value:any, flattenedKey:string, object:any):void {
var currentKey:any;
var keyIndexStart = 0;
for (var charIndex = 0, length = flattenedKey.length; charIndex < length; charIndex++) {
var character = flattenedKey.charAt(charIndex);
switch(character) {
case '[':
currentKey = flattenedKey.substring(keyIndexStart, charIndex);
this.createPropertyIfMissing_(currentKey, object, Array);
break;
case ']':
currentKey = flattenedKey.substring(keyIndexStart, charIndex);
currentKey = parseInt(currentKey); // Convert index from string to int
// Special case where we're targeting this object in the array
if (charIndex === length - 1) {
object[currentKey] = value;
} else {
// If this is the first time we're accessing this Array key we may need to initialize it.
if (!object[currentKey] && charIndex < length - 1) {
switch(flattenedKey.charAt(charIndex + 1)) {
case '[':
object[currentKey] = [];
break;
case '.':
object[currentKey] = {};
break;
}
}
object = object[currentKey];
}
break;
case '.':
currentKey = flattenedKey.substring(keyIndexStart, charIndex);
// Don't do anything with empty keys that follow Array indices (e.g. anArray[0].aProp)
if (currentKey) {
this.createPropertyIfMissing_(currentKey, object, Object);
}
break;
default:
continue; // Continue to iterate...
break;
}
keyIndexStart = charIndex + 1;
if (currentKey) {
object = object[currentKey];
}
}
if (keyIndexStart < flattenedKey.length) {
currentKey = flattenedKey.substring(keyIndexStart, flattenedKey.length);
object[currentKey] = value;
}
}
/**
* Helper method for initializing a missing property.
*
* @throws Error if unrecognized property specified
* @throws Error if property already exists of an incorrect type
*/
private static createPropertyIfMissing_(key:string, object:any, propertyType:any):void {
switch(propertyType) {
case Array:
if (!object.hasOwnProperty(key)) {
object[key] = [];
} else if (!(object[key] instanceof Array)) {
throw Error('Property already exists but is not an Array');
}
break;
case Object:
if (!object.hasOwnProperty(key)) {
object[key] = {};
} else if (typeof object[key] !== 'object') {
throw Error('Property already exists but is not an Object');
}
break;
default:
throw Error('Unsupported property type');
break;
}
}
}
}