If I have the following:
var myObj = { "Foo":{"prop1":"abc", "prop2":123 }, "Bar":{"prop1":"def", "prop2":456 } };
Is there a quick and safe way to modify the object such that it becomes:
{ "Foo":{"prop1":"abc", "prop2":123 }, "Bar":{"PROP1":"def", "PROP2":456 } }
I'd like to change the casing of the property names of the Bar property on-the-fly. Is this possible?
Yes, it is. try this code
var myObjBar = myObj['Bar'];
for (p in myObjBar) {
if (myObjBar.hasOwnProperty(p)) {
var v = myObjBar[p],
k = p.toUpperCase();
delete myObjBar[p];
myObjBar[k] = v;
}
}
Related
I want to do:
properties.email.value without triggering an error like: Can't read 'value' of 'undefined'
However, I don't want to do:
properties.email && properties.email.value and I don't want to use an helper, something like: get(properties, 'email.value').
I really want to keep the syntax properties.email.value
I can solve this by doing:
Object.defineProperty(properties, 'email', {
get: () => properties.email && properties.email.value,
enumerable: true,
configurable: true
});
Now the getter is in charge of doing my safety check. Perfect.
But I also want to be able to do properties.name.value safely.
But as the properties object comes from the API (json), I don't know the full list of properties possible.
So, is there a way to use this "magical" get syntax for any prop access like: properties[ANYTHING].value ?
OK, I've got something like this.
But you must create properties that way.
Hope this help :)
var properties = {
phone : {
value: "123456789"
}
}
var handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : {};
}
};
var new_properties = new Proxy(properties, handler);
console.log("phone.value = " + new_properties.phone.value);
console.log("email.value = " + new_properties.email.value);
new_properties.email = {
value: 1
};
console.log("email.value after assign = " + new_properties.email.value);
The document reference here.
Edited
Even if the original properties object is unknown, this kind of usage works as well.
You could use a Proxy and get known properties and a custom result for unknow properties.
For changing properties, you could take the same approach and set the value.
var properties = { email: { value: 'foo#example.com' } },
proxy = new Proxy(
properties,
{
get: function(target, prop, receiver) {
if (prop in target) {
return target[prop] && target[prop].value
} else {
return;
}
},
set: function(target, prop, value) {
if (prop in target) {
target[prop].value = value;
} else {
target[prop] = { value };
}
}
}
);
console.log(proxy.email);
console.log(proxy.bar);
proxy.email = '41';
console.log(proxy.email);
I can't believe I'm doing this...
var wordlength = 7;
var alphabet="abcdefghijklmnopqrstuvwxyz";
alphabet += alphabet.toUpperCase() + "0123456789_";
var alen = alphabet.length;
var buildWord = function(number){
if(number===0){
return '';
}
return alphabet[number%alen]+buildWord(Math.floor(number/alen));
};
var total = Math.pow(alen, wordlength);
for(var i = 1; i<total; i++){
var w = buildWord(i);
if(isNaN(w[0]) && Object.prototype[w]===undefined){
Object.prototype[w]={};
}
}
I have a JSON object that looks a bit like this:
{
name: 'test',
details: {
description: 'This is the long description',
shortDescription: 'This is the short description (ironically longer than the description!)'
}
}
Obviously the real object is a lot more complicated than this example, but I have omitted the details because they will only complicate the question.
So, with this object, I have a function that tries to get the value of the property, it looks like this:
// Private function for matching fields
var _matchField = function (item, filter) {
// Our variables
var text = item[filter.field],
values = filter.expression.split(',');
// If we have any text
if (text) {
// Loop through our values
angular.forEach(values, function (value) {
console.log(text);
console.log(value);
// See if we have a match
if (text.toLowerCase().indexOf(value.toLowerCase()) > -1) {
// We have found a match
return true;
}
});
}
// We have found no matches
return false;
}
The issue is the line:
var text = item[filter.field],
If the property was just the name then item['name'] would work with the above object. But if I want to get the description; item['details.descrption'] doesn't work.
So I need a function that will allow me to specify a property name and it will find the property and return its value.
But before I try to write one, I was hoping there might be a simple solution that someone has come across.
you can write your custom function for this
function getProperty(json, field) {
if (json == null || field == null) {
return null;
}
var value = json;
var fields = field.split(".");
for (var i = 0; i < fields.length; i++) {
value = value[fields[i]];
if (value == null) {
return null;
}
}
return value;
}
check this plnkr example https://plnkr.co/edit/8Ayd9wnh1rJh1ycx5R1f?p=preview
You can split the reference to the object and use a function for getting the right nested object/value.
function getValue(o, p) {
if (typeof p === 'string') {
p = p.split('.')
}
return p.length ? getValue(o[p.shift()], p) : o;
}
var item = { name: 'test', details: { description: 'This is the long description', shortDescription: 'This is the short description (ironically longer than the description!)' } };
document.write(getValue(item, 'details.description'));
I solved this by creating this function:
// Private function to get the value of the property
var _getPropertyValue = function (object, notation) {
// Get all the properties
var properties = notation.split('.');
// If we only have one property
if (properties.length === 1) {
// Return our value
return object[properties];
}
// Loop through our properties
for (var property in object) {
// Make sure we are a property
if (object.hasOwnProperty(property)) {
// If we our property name is the same as our first property
if (property === properties[0]) {
// Remove the first item from our properties
properties.splice(0, 1);
// Create our new dot notation
var dotNotation = properties.join('.');
// Find the value of the new dot notation
return _getPropertyValue(object[property], dotNotation);
}
}
}
};
This is a strange one, but I'm exploring it to see if it's possible.
Let's say that I have a .NET application where I am using PubSub. I want a way to define the topic string using chained objects (not functions). The goal is to allow me a way of defining strings that lets me to take advantage of Visual Studio's IntelliSense and reduce the likelihood of spelling errors.
Here's an example:
/* Manual way */
var topic = "App.Navigation.CurrentItem"
/* Desired Solution */
// ... define the objects here ...
var topic = App.Navigation.CurrentItem;
console.log(topic); // "App.Navigation.CurrentItem"
var topic2 = App.Footer.CurrentItem;
console.log(topic2); // "App.Footer.CurrentItem"
I'd like each object to be responsible for outputing it's own value, and have the chaining process responsible for joining itself to the previous chained object via a predefined separator (in my case, a period [.]).
I've been playing with JavaScript getter syntax, but I'm curious if there's a better way.
Has anyone done something like this before, and if so, how did you solve it?
You're requirements aren't totally clear to me, but are you looking for something like this?
function namespace(ns) { this._ns = ns; }
namespace.prototype.toString = function() {return this._ns};
namespace.prototype.extend = function(suffix) {
return new namespace(this._ns + "." + suffix)
};
Usage:
App = new namespace('App');
App.Navigation = App.extend('Navigation');
App.Navigation.CurrentItem = App.Navigation.extend('CurrentItem');
console.log(App.Navigation.CurrentItem.toString()); // "App.Navigation.CurrentItem"
This is what I ended up with after reviewing StriplingWarrior's answer:
function Namespace(name, config) {
if (typeof name === "object") {
config = name;
name = null;
}
config = config || {};
this._ns = name;
this.define(config);
}
Namespace.prototype.toString = function() { return this._ns };
Namespace.prototype.define = function(config, base) {
base = base || this;
for (key in config) {
var name = (base._ns) ? base._ns + "." + key : key;
base[key] = new Namespace(name);
base.define(config[key], base[key]);
}
return base;
};
Usage:
var App = new Namespace("App", {
Navigation: {
Items: {
Current: {}
}
},
Content: {},
Footer: {
Items: {
Current: {}
}
}
});
console.log(App.toString()); // App
console.log(App.Navigation.Items.Current.toString()); // App.Navigation.Items.Current
console.log(App.Footer.toString()); // App.Footer
I also wrote a convenience method to reduce the toString():
function NS(namespace) {
return namespace.toString();
}
console.log(NS(App.Navigation.Items.Current));
Thanks again to StriplingWarrior for the the help!
Is it possible to create an array that will only allow objects of a certain to be stored in it? Is there a method that adds an element to the array I can override?
Yes you can, just override the push array of the array (let's say all you want to store are numbers than do the following:
var myArr = [];
myArr.push = function(){
for(var arg of arguments) {
if(arg.constructor == Number) Array.prototype.push.call(this, arg);
}
}
Simply change Number to whatever constructor you want to match. Also I would probably add and else statement or something, to throw an error if that's what you want.
UPDATE:
Using Object.observe (currently only available in chrome):
var myArr = [];
Array.observe(myArr, function(changes) {
for(var change of changes) {
if(change.type == "update") {
if(myArr[change.name].constructor !== Number) myArr.splice(change.name, 1);
} else if(change.type == 'splice') {
if(change.addedCount > 0) {
if(myArr[change.index].constructor !== Number) myArr.splice(change.index, 1);
}
}
}
});
Now in ES6 there are proxies which you should be able to do the following:
var myArr = new Proxy([], {
set(obj, prop, value) {
if(value.constructor !== Number) {
obj.splice(prop, 1);
}
//I belive thats it, there's probably more to it, yet because I don't use firefox or IE Technical preview I can't really tell you.
}
});
Not directly. But you can hide the array in a closure and only provide your custom API to access it:
var myArray = (function() {
var array = [];
return {
set: function(index, value) {
/* Check if value is allowed */
array[index] = value;
},
get: function(index) {
return array[index];
}
};
})();
Use it like
myArray.set(123, 'abc');
myArray.get(123); // 'abc' (assuming it was allowed)
I'd like to set a property to an object using prototype, if it's possible, so that when I try to get an undefined value, it returns a default instead of undefined, like:
obj = {
lang : {en: 'hello', it:'ciao'}
}
Example 1: obj.lang.es (undefined) - I'd like to get obj.lang.en instead of undefined.
Example 2: obj.something.else - I'd like to get false instead of an error because it can't read else of undefined.
It is probably not a good idea.
Better use a root or default key:
obj = {
lang : {root: 'hello', en: 'hello', it:'ciao'}
}
So you can ask for a lang in this way:
var hi = obj.lang.es || obj.lang.root;
You can use this in combination with a getter method:
var getTranslation = function(lang) {
return obj.lang[lang] || obj.lang.root;
};
var hi = getTranslation("es");
This is not a pretty solution, but here it goes, create a prototype object that for any language defers the result to a default language. Then your particular object inherits from that prototype object and overrides any value it wants.
var languages = ['en', 'it', 'es']; // fill with every language you will ever use
var defaultLang = 'en';
var protoLang = Object.create(null);
function getDefaultLanguage(){
return this[defaultLang];
}
languages.forEach(function(language){
Object.defineProperty(protoLang, language, {
get : getDefaultLanguage
});
});
var obj = {
lang : Object.create(protoLang, {
en : { value : 'hello' },
it : { value : 'ciao' }
})
};
obj.lang.es // "hello"
obj.lang.en // "hello"
obj.lang.it // "ciao"
The thing is that you have to define every property first, that is why you need the languages array.
I agree with #DCoder. Maybe something like this:
function getLang(lang) {
var result = this.obj[lang];
if ( typeof result == 'undefined' ) {
result = this.obj.en;
}
return result;
}
You do not need ; inside object, there no content in obj.lang, instead try it like this:
obj = {
lang: {en: 'hello', it: 'ciao'}
};
if (obj.lang.hasOwnProperty("en")) {
console.log(obj.lang.en);
} else {
console.log(false);
}