i have a function that loop all object properties and return value if it qualify certain condition
basically this is how i m doing
//an enum
var BillingType = Object.freeze({
PayMonthly: { key: 'Monthly', value: 1 },
PayYearly: { key: 'Yearly', value: 2 }
});
now to make it work i do this
for (var property in BillingType ) {
if (BillingType .hasOwnProperty(property)) {
if (value === BillingType [property].value) {
return BillingType [property].key;
}
}
}
it works fine but to make it generic for all enums i changed code to
getValue = function (value, object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (value === object[property].value) {
return object[property].key;
}
}
}
}
now when i try to call from other functions
enumService.getValue(1, 'BillingModel');
rather to loop all properties it start loop on its characters.
how can i convert string to object or m doing it totally wrong . any help will be appreciated
Regards
Your getValue looks fine, just call it using
enumService.getValue(1, BillingModel); // <-- no quotes
and here is a working fiddle: http://jsfiddle.net/LVc6G/
and here is the code of the fiddle:
var BillingType = Object.freeze({
PayMonthly: { key: 'Monthly', value: 1 },
PayYearly: { key: 'Yearly', value: 2 }
});
var getValue = function (value, object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (value === object[property].value) {
return object[property].key;
}
}
}
};
alert(getValue(1, BillingType));
Related
Just wanted to share a little trick I learned to pass variables into the scope of your JS Array.forEach() method.
I had a situation where I needed to use a forEach loop to build a dataset. But I needed to access variables in the current scope as well (I needed to be able to reference this in the loop).
This is the situation I was in:
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
}, this);
this.refreshGraph(dataset);
Dataset isn't accessible from within the loop. So how do we access it while iterating?
I haven't seen this solution on stack overflow and it didn't fit any question I could find.
Answer below:
With the abilities of es6
If you'll use an Arrow Function the this will be taken from
items.forEach(item => {
// You can use this as out of the forEach scope
});
From MDN Web Docs:
An arrow function does not have its own this. The this value of the
enclosing lexical scope is used; arrow functions follow the normal
variable lookup rules. So while searching for this which is not
present in current scope, an arrow function ends up finding the this
from its enclosing scope.
Another nice explanation:
https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4
If you have a function out of scope of some data yet need to access it, you can use a curried function that takes that dataset as the first parameter and can still use this normally throughout:
//curried function that uses `dataset` and `this` but it is not
//in the context where the iteration happens
function makeLoopCallback(dataset) {
return function(item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
}
}
//object to serve as `this` context for a function
var obj = {
green: "Green",
yellow: "Yellow",
red: "Red",
doSomething: function(items) {
var data = {
data: [],
bgColor:[],
};
items.forEach(makeLoopCallback(data), this);
return data;
}
}
//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];
//call the function
console.log(obj.doSomething(input))
An alternative is to use Array#reduce instead of Array#forEach with a function that takes two parameters directly. Since .reduce cannot set the this context, you can just use Function#bind to do it:
//external function that uses `dataset` and `this` but it is not
//in the context where the iteration happens
function external(dataset, item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
return dataset;
}
//object to serve as `this` context for a function
var obj = {
green: "Green",
yellow: "Yellow",
red: "Red",
doSomething: function(items) {
var data = {
data: [],
bgColor:[],
};
return items.reduce(external.bind(this), data);
}
}
//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];
//call the function
console.log(obj.doSomething(input))
The solution is to pass a JSON object as the this argument.
so before we had:
Array.forEach(function(){}, this)
// "this" is just an object ^^^^ just like anything else in JavaScript
Now we have:
Array.forEach(function(){}, {_self: this, dataset: dataset})
// you can access _self and dataset just as if they were in scope
And now you can make data changes while iterating with an anonymous function :)
Full example:
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(_self.green);
} else if (item.age < 5) {
dataset.bgColor.push(_self.yellow);
} else {
dataset.bgColor.push(_self.red);
}
}, { _self: this , dataset: dataset});
Array.prototype.forEach(callbackFun, ?this)
You can pass dataset as this argument to forEach
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
this.dataset.data.push(item.age);
if (item.age < 2) {
this.dataset.bgColor.push(this.tempThis.green);
} else if (item.age < 5) {
this.dataset.bgColor.push(this.tempThis.yellow);
} else {
this.dataset.bgColor.push(this.tempThis.red);
}
}, {tempThis:this,dataset:dataset});
this.refreshGraph(dataset);
The following should put out "Yuval"
console.log(pathFind(["book", "author", "name"], {
book: {
author: {
name: "Yuval"
}
}
}));
I've tried writing this function but it keeps returning undefined:
function pathFind(path, object) {
return path.reduce((accumulator, name) => {
if(accumulator && accumulator[name] != typeof 'object') {
accumulator[name]
} else {
undefined, object
}
})
}
What am I missing? (a typo?)
Is there a way to use recursion inside this function in combination with reduce? (i.e how to approach recursion for this?)
Try the following:
var obj = {
book: {
author: {
name: "Yuval"
}
}
};
var path = ["book", "author", "name"];
function findPath(obj, path){
if(path.length === 0) return obj;
return findPath(obj[path[0]], path.slice(1));
}
console.log(findPath(obj,path));
Tell me, how correctly to check the existence of a key in associative arrays?
For example:
var mydata = {
key1: '',
key2: {
subkey1: {
subkey1_1: {
value1: ''
value2" '',
},
},
subkey2: '';
},
}
if ((mydata.key2 != undefined) && (mydata.key2.subkey1 != undefined) && (mydata.key2.subkey1.subkey1_1 != undefined))
mydata.key2.subkey1.subkey1_1.value1 = 'test';
Too long and confusing
((mydata.key2 != undefined) && (mydata.key2.subkey1 != undefined) && (mydata.key2.subkey1.subkey1_1 != undefined))
I would like to use a simpler function, like
safeSet(mydata.key2.subkey1.subkey1_1.value1, 'test');
or
if (is_undefined(mydata.key2.subkey1.subkey1_1.value1) == true)
mydata.key2.subkey1.subkey1_1.value1 = 'test'; // now - error if 'mydata.key2.subkey1.subkey1_1' not exist
You can create custom function using reduce() to test if nested property exists. You can just pass key as string.
var mydata = {
key1: '',
key2: {
subkey1: {
subkey1_1: {
value1: '',
value2: '',
},
},
subkey2: ''
},
}
function safeSet(key, data) {
return key.split('.').reduce(function(r, e) {
return r ? r[e] : undefined;
}, data) != undefined
}
console.log(safeSet('key2.subkey1.subkey1_1.value1', mydata))
You should use the in operator:
"key" in obj // true, regardless of the actual value
Or, if you want to particularly test for properties of the object instance (and not inherited properties), use hasOwnProperty:
obj.hasOwnProperty("key") // true
hope this would help you.
Source: http://www.advancesharp.com/questions/628/checking-if-an-associative-array-key-exists-in-javascript
Alternatively, you can make use of the .has() method of Lodash.
Then, you would only need to check:
if (_.has(mydata, 'key2.subkey1.subkey1_1.value1')
mydata.key2.subkey1.subkey1_1.value1 = 'test';
For trying to get something in a nested structure I'd do something like this:
function getPath(element, path) {
var handledSoFar = [];
for (var i = 0; i < path.length; i++) {
var property = path[i];
handledSoFar.push(property);
if (typeof element[property] === 'undefined') {
throw new Error('Path ' + handledSoFar.join('->') + ' is undefined');
}
element = object[property];
}
return element;
}
var mydata = {
key1: '',
key2: {
subkey1: {
subkey1_1: {
value1: '',
value2: 'hi'
}
},
subkey2: ''
}
};
// Prints 'hi'
console.log(getPath(mydata, ['key2', 'subkey1', 'subkey1_1', 'value2']));
// Throws error 'Path key2->subkey2->subkey1_1 is undefined'
console.log(getPath(mydata, ['key2', 'subkey1', 'subkey1_1', 'value2']));
Of course keeping track of the search in handledSoFar is optional but might be useful for development / debugging.
You can also use the lodash deep field selector: lodash.get (documentation)
const get = require('lodash.get');
const set = require('lodash.set');
if (!get(mydata, 'key2.subkey1.subkey1_1.value1')) {
set(mydata, 'key2.subkey1.subkey1_1.value1', 'test');
}
You could split the path and make a check if the following element exist. If not assign an object to the new property.
Return then the value of the property.
At the end assign the value.
function setValue(object, path, value) {
var fullPath = path.split('.'),
way = fullPath.slice(),
last = way.pop();
way.reduce(function (r, a) {
return r[a] = r[a] || {};
}, object)[last] = value;
}
var object = { key1: '', key2: { subkey1: { subkey1_1: { value1: '', value2: '' } }, subkey2: '' } };
setValue(object, 'key2.subkey1.subkey1_1.value1', 'test');
console.log(object);
The problem with the example function that you proposed:
safeSet(mydata.key2.subkey1.subkey1_1.value1, 'test');
or
is_undefined(mydata.key2.subkey1.subkey1_1.value1)
Is that the mydata.key2.subkey1... part is run before the function is called. So if one of the subkeys does not exist, an exception will be thrown before your code is reached.
You could get something similar using a callback though...
safeSet(function(val) { mydata.key2.subkey1.subkey1_1.value1 = val; }, 'test')
the implementation of safeSet would then be:
var safeSet = function(setterFunc, val) {
try {
setterFunc(val);
} catch (e) {
if (e instanceof TypeError) {
return false;
} else {
throw e;
}
}
return true;
}
safeSet returns true if the value was set, and false otherwise.
I know this may be a silly question but I'm new to javascript so would appreciate some guidance.
I have the following notificationSocketEventHandler object created:
const notificationSocketEventHandler = Object.create(socketHandlerProto, {
validators: {
created: [],
destroyed: [],
loadedFromSocket: [],
updated: [],
addedto: {
relation: []
},
removedfrom: {
}
},
created: function (data) {
if (this.validateProfileData(data, validators.created)) {} else {}
},
destroyed: function (data) {},
updated: function (data) {},
loadedFromSocket: function (data) {
console.log('Loaded from socket')
console.log(data)
}
})
This event handler is being used to listen for notifications from a socket
so and is set like this
$.globals.socket.on('notifications',notificationSocketEventHandler);
the event prototype is defined as such:
$.globals.socket = {
events: {},
on: function (attr, func) {
if (!attr) return false
this.events[attr] = this.events[attr] || [];
this.events[attr].push(func)
return true
},
remove(attr, func) {
if (!events[attr]) return false
this.events[attr].forEach(function (f, indx) {
if (f === func) {
events[attr].slice(indx, 1)
}
})
},
trigger: function (attr, thisArg, paramArgs) {
if (Array.isArray(attr) && attr.length) {
var obj = this.events[attr[0]]
for (var i = 1; i < attr.length; i++) {
if (!obj) return
obj = obj[attr[i]]
}
if (typeof obj == 'function') obj.apply(thisArg, paramArgs)
if (Array.isArray(obj)) {
obj.forEach(function (c) {
if (typeof c == 'function') c.apply(thisArg, paramArgs)
})
}
return
}
if(this.events[attr]){
console.log(this.events[attr])
this.events[attr].forEach(function (f) {
if (typeof f === 'function')
f.apply(thisArg, paramArgs)
})
}
}
}
The problem I am having is that after the notificationSocketEventHandler object gets passed to the $.globals.socket.on function and ultimately pushed into the events object, the properties of the notificationSocketEventHandler such as created, destroyed,loadedFromSocket which are defined as functions before being passed to the $.globals.socket.on function, suddenly become `undefined' once within the events object, why is this?
Object.create is a little confusing - you can't use a normal object as the second parameter, it has to be an object of 'property descriptors'. To work properly your code would need to be formatted along these lines:
const notificationSocketEventHandler = Object.create(socketHandlerProto, {
'validators': {
value: {
'created': {
value: []
},
'destroyed': {
value: []
},
'loadedFromSocket': {
value: []
}
}
}
});
Unless you iterate over those properties it's going to be tedious. You would be much better off avoiding Object.create and just making an object normally:
const notificationSocketEventHandler = {
created: [],
destroyed: [],
etc...
}
or adding the properties to the constructor's prototype (as an aside, people usually make constructor names begin with an uppercase letter so it's immediately obvious they're constructors - saves you having to add 'Proto' at the end of the name for one thing. Anyway):
var SocketHandler = function {
this.created = [];
this.destroyed = []
etc...
}
const notificationSocketEventHandler = new SocketHandler
or
var SocketHandler = {};
SocketHandler.prototype.created = [];
SocketHandler.prototype.destroyed = [];
etc...
If I have a nested set of plain old javascript objects (for example, having been returned from JSON), how do I them into Ember.js objects (or at least getting the binding functionality working)?
For example, if I have an object like:
var x = {
bar: {
baz: "quux"
}
}
Then I turn that into an Ember object:
var y = Ember.Object.create(x);
Then setting the value of "baz" won't update any views I have, because it is just a normal js object, not an Ember object.
I know I can just recursively go over the object keys, and do Ember.Object.create all the way down, but is there an alternative approach?
I'm not sure how you're attempting to set the value of baz, after you've created the Ember.Object, but you should make sure you use an observer-aware setter function. For this example, I'd suggest using setPath().
For example:
var x = {
bar: {
baz: "quux"
}
};
var y = Ember.Object.create(x);
y.setPath('bar.baz', 'foo');
jsFiddle example, showing a view update after setting: http://jsfiddle.net/ebryn/kv3cU/
Here's my version:
import { typeOf } from '#ember/utils'
import EmberObject from '#ember/object'
export default function deepEmberObject(anything) {
if (typeOf(anything) === 'array') {
return anything.map(a => deepEmberObject(a))
} else if (typeOf(anything) === 'object') {
let converted = Object.keys(anything).reduce((acc, k) => {
acc[k] = deepEmberObject(anything[k])
return acc
}, {})
return EmberObject.create(converted)
} else {
return anything
}
}
test:
import deepEmberObject from 'zipbooks/utils/deep-ember-object'
import { module, test } from 'qunit'
module('Unit | Utility | deep-ember-object', function() {
test('it works', function(assert) {
let result = deepEmberObject({ pandas: [{ id: 3, children: [{ name: 'Bobby', features: { weight: 3 } }] }] })
assert.equal(
result
.get('pandas')[0]
.get('children')[0]
.get('features')
.get('weight'),
3
)
})
})
For some reason I had to define nested objects independently in order to ensure the computed works properly (even the enumerated ones).
For that I end up crafting these 2 utility functions:
import EmberObject from '#ember/object';
import { A } from '#ember/array';
function fromArrayToEmberArray(array) {
const emberArray = A();
array.forEach(function(item) {
if (Array.isArray(item)) {
emberArray.push(fromArrayToEmberArray(item));
} else if (item && typeof item === 'object') {
emberArray.push(fromObjectToEmberObject(item));
} else {
emberArray.push(item);
}
});
return emberArray;
}
function fromObjectToEmberObject(pojo) {
const emberObject = EmberObject.create();
for (const key in pojo) {
const keyObject = pojo[key];
if (Array.isArray(keyObject)) {
emberObject.set(key, fromArrayToEmberArray(keyObject))
} else if (keyObject && typeof keyObject === 'object') {
emberObject.set(key, fromObjectToEmberObject(keyObject))
} else {
emberObject.set(key, keyObject);
}
}
return emberObject;
}
export default {fromObjectToEmberObject};