Javascript inline replace undefined with empty string - javascript

I have this function:
function callWS(input) {
var output = {
"type" : input["type"] || "",
"mark" : input["mark"] || "",
"model" : input["model"] || "",
"year" : input["year"] || ""
};
return output;
}
I want the user to call this function in many ways:
callWS(); ==> {"type":"","mark":"","model":"","year":""}
callWS({"type":"moto"}); ==> {"type":"moto","mark":"","model":"","year":""}
callWS({"type":"moto","mark":"audi"}); ==> {"type":"moto","mark":"audi","model":"","year":""}
And in case a parameters is undefined, to initialize it as an empty string. Currently my function does not work in the first case, but in the other is working.
When I call it like callWS() I get:
Uncaught TypeError: Cannot read property 'type' of undefined
To be honest I don't know why it works for the 2 and 3 case but I need to make it work for the first case also. I know that if I use:
if (input["type"])
will do the trick but I need an inline solution. Is this possible somehow?

You have to supply input variable itself with default value too.
function callWS(input) {
input = input || {};
...
}
Otherwise you access properties on unexisting (undefined) object which lead to error (what you have now).
On other hand accessing unexisting properties on existing object isn't treated as error in JS.

You can write your own extend method and use that.
That way you can have a default object, giving it which ever default values and then merge it with the object passed into the function.
function extend(a, b){
for(var key in b){
if(b.hasOwnProperty(key)){
a[key] = b[key];
}
}
return a;
}
function callWS(input) {
var defaultInput = {
"type": "",
"mark": "",
"model":"",
"year": ""
}
var output = extend(defaultInput, input);
return output;
}
console.log(callWS());
console.log(callWS({"type":"moto"}));
console.log(callWS({"type":"moto","mark":"audi"}));

In ES6, write this as
function callWS({type = "", mark = "", model = "", year = ""} = {}) {
return {type, mark, model, year};
}
Here's another approach not involving ES6. Many libraries contain a default utility, which will apply some properties to another object if they are missing.
function deflt(obj, defaults) {
var keys = Object.keys(defaults);
for (var i = 0; i < keys.length: i++) {
var key = keys[i];
if (!(key in obj)) obj[key] = defaults[key];
}
return obj;
}
You can use this as follows:
function callWS(input) {
input = input || {};
deflt(input, {type: "", mark: "", model: "", year: ""});
return input;
}
Note: as written this will return a modified version of the input. Adjust as necessary if this is not what you want.

Related

circular reference issue occured when passed the model with json.stringify function

I want to replace all fields with null values so that it does not proceed with the model further. Now when I am creating a replacer function and passing the model. I am getting a circular reference issue. Is there any better way to do this so that I do not get the error
const replacer = function (k, v) {
if (v === '') {
return undefined
}
return v
}
return JSON.parse(JSON.stringify(this.getData(), replacer))
I'm not sure that I fully got your goal, but if you have an array of data, and that you want to replace each empty string item by undefined you could simply do
const example_data = ["hello", "", "", "world", "!", ""];
const empty_to_undefined = example_data.map(item => item === "" ? undefined : item);
console.log(empty_to_undefined)

javascript or json expression to parse json to get a certain value

I need to be able to parse this json array/object for a value where an element matches a value.
For example, I wanted to get the value of $, where the #name = nameGreen.
{
"startNode": {
"result": {
"node1": {
"$": "A_Value"
},
"attribute-value": [
{
"#name": "nameRed",
"$": "attribute1"
},
{
"#name": "nameBlue",
"$": "attribute2"
},
{
"#name": "nameGreen",
"$": "attribute3"
},
{
"#name": "nameYellow",
"$": "attribute4"
}
]
}
}
}
Getting the value of node1 was easy console.log(['startNode'].result.node1['$']); will get me the value of A_Value.
I'm just having problems writing logic for,
1. when #name = nameGreen,
2. then get the value for $
Note: This json node is dynamic, so the #name and $ values will always change.
Really appreciate the help.
You'll need to loop through the actual values to examine them. For example,
var attrs = ['startNode'].result.['attribute-value']
for (var i = 0; i < attrs.length; i++) {
if (attrs[i]['#name'] == 'nameGreen') ...
}
(Obviously you can write some nicer code to do this.)
I'd recommend you look for some libraries that help you extract this data in a better way, as it looks like you are parsing XML.
JSBin Example
Heres one way to do it, looping through "attribute-value" the array:
var a = d.startNode.result["attribute-value"];
for (var i = 0; i < a.length; i++) {
if (a[i]['#name'] === 'nameRed') {
console.log(a[i].$);
}
}
Things to note:
If a variable has a '-' in it, try using bracket notation as opposed to dot notation.
Same with odd characters like #
However, that is not required when using a $ as you can see from the code above
General function as required (ES6)
const getThing = (arr, key, test, match) => (
arr.filter(el => (el[key] === test))[0][match]
);
var arr = obj.startNode.result['attribute-value'];
var out = getThing(arr, '#name', 'nameGreen', '$');
DEMO
Or the same thing in more verbose ES5:
function getThing(arr, key, test, match) {
return arr.filter(function (el) {
return el[key] === test;
})[0][match];
};
DEMO
In ES6 you could also use the find method of array objects or a polyfill for it to locate an "attribute-value" object before reading its $ property:
var nodes = { ..... }; // the posted object.
var findObj = ( arrayOfObj, propName, propVal) =>
arrayOfObj.find( (obj) => obj[ propName] === propVal );
var atr = findObj( nodes.startNode.result["attribute-value"], "#name", "nameGreen");
console.log( atr ? "$ for namegreen: " + atr.$ : "not found")
where findObj is a function to search arrayOfObj for an object with property propName with value propVal, and the arrow function supplied to find returns true or false for the match.

Create object by grouping camelCase properties

Recently I've found that I have had to create a object from attributes on a HTML tag. I am doing this in a AngularJS environment, so hyphenated attributes are converted to camelCase, but I could also do the same using data- attributes and dataset
So for example I have:
<element person-name="Grant" animation-jump="123" />
Which gives the object
{
"personName" : "Grant",
"animationJump" : "123"
{
My problem is that I then want to convert that camelCase object into a structured object:
{
"person" : {
"name" : "Grant" },
"animation" : {
"jump" : "123" }
}
I've created a JSFiddle of my QUint Unit Test https://jsfiddle.net/gdt3bonw/
It's actually working for the case I want which is only 1 level, but I would like to get it working for any number of levels because I foresee that it will be needed and so I can release the code publicly.
We will loop through the keys of the object using reduce, building up the result. We decompose each key into its components, such as personName into person and name. We loop over these components, creating subobjects if they do not already exist. Finally, we add the final component to the innermost subobject as a property with the value in question.
Object.keys(input).reduce((result, key) => {
var parts = key.match( /(^|[A-Z])[a-z]+/g) . map(part => part.toLowerCase());
var leaf = parts.pop();
var obj = result;
parts.forEach(part => obj = obj[part] = obj[part] || {});
obj[leaf] = input[key];
return result;
}, {});
You can't use that in this way, and I don't think that it would be a logic proposal. Below I explain why it wouldn't.
obj[["animation","jump"]] = "123"
replace it with
obj["animation"]["jump"] = "123"
and it's all fine.
Why I don't support your idea?
It's messy to use, there is no style in doing that.
There is no logic in using an array as an object key
There is another way of calling an object item by key: using a dot, and that won't support your idea. I think everyone can imagine why.
Why do you need to convert the attribute to camelCase in the first place..? Just do
function arrayToStructuredObject(obj,props){
if (props.length){
obj[props[0]] = props.length > 1 ? {} : value;
arrayToStructuredObject(obj[props.shift()],props);
}
return obj;
}
var props = "animation-jump-tremendous-pinky".split("-"),
value = "123",
obj = {},
sobj = {};
sobj = arrayToStructuredObject(obj, props);
Besides i would like to remind that using the bracket notation to create a property is only possible if the reference that the bracket notation is used upon is predefined as an object. Such as
var o1; // <- undefined
o1["myProp"] = 1; // Uncaught TypeError: Cannot set property 'myProp' of undefined
while
var o2 = {}; // Object {}
o2["myProp"] = 1; // <- 1
then again
o2["myProp"]["myOtherProp"] = 2; // <- 2 but won't type coerce o2.myProp to Object
So speaking of proposals, i am not sure if utilizing bracket notation directly over undefined variables yet as another object creation pattern makes sense or not.
Well in any case one complete solution would be
var inp = {"personName" : "Grant", "animationJump" : "123", "fancyGirlTakesARide" : "987"},
result = Object.keys(inp).reduce(function(p,c,i){
var props = c.replace(/[A-Z]/g, m => "-" + m.toLowerCase()).split("-");
return arrayToStructuredObject(p,props,inp[c])
},{});
function arrayToStructuredObject(obj,props,val){
if (props.length){
obj[props[0]] = props.length > 1 ? {} : val;
arrayToStructuredObject(obj[props.shift()],props,val);
}
return obj;
}
Though I loved the method of splitting the camelCase props by a look-ahead (/?=[A-Z]/) it takes an extra job of lower casing the whole array of prop strings regardless they are already lowercase or not. So i guess this might be slightly faster. (..or not due to the recursive nature of it)
This is not the best solution, but you can actually use arrays as key, in this particular situation, by converting them to a string:
obj[["animation","Jump"].join()] = "123";
This will work with your original object.
A solution, which uses Regex to split camel case string.
var obj = { "animationsJump": "123", "animationsRun": "456", "animationsHide": "789", "personName": "Grant", "personPetsDog": "Snowy", "personPetsCat": "Snowball" },
newObject = {};
Object.keys(obj).forEach(function (k) {
var path = k.split(/(?=[A-Z])/).map(function (s) {
return s.toLowerCase();
}),
last = path.pop();
path.reduce(function (r, a) {
r[a] = r[a] || {};
return r[a];
}, newObject)[last] = obj[k];
});
document.write('<pre>' + JSON.stringify(newObject, 0, 4) + '</pre>');

Multiple level attribute retrieval using array notation from a JSON object

I've got a mentally taxing problem here, where I've got a JSON object retrieved using a collection in Backbone. This is what the object looks like:
{
"MatchID": "00000001",
"Date": "1970-01-01T00:00:00.000Z",
"OriginalID": "",
"Stage": {
"StageNumber": "0",
"StageType": "Stage Type"
},
"Round": {
"RoundNumber": "0",
"Name": "Round Name"
},
"Leg": "1",
"HomeTeam": {
"TeamID": "0",
"Name": "Home Team Name"
},
"AwayTeam": {
"TeamID": "0",
"Name": "Away Team Name"
},
"Venue": {
"VenueID": "0",
"Name": "Venu Name"
},
"Referee": null,
}
What I want to do with this data, is filter it based on a particular attribute, such as the Venue.Name or Date attributes (which are different depths into the object, and can be deeper than two levels for some of the other data). I've got the following code inside a Backbone collection to filter and return a new collection with the contents filtered appropriately:
findWhere: function (Attribute, Value)
{
return new Project.Collections.Fixtures(this.filter(function (fixture)
{
return eval('fixture.attributes.' + Attribute) == Value;
}));
}
This allows me to specify in an attribute which attribute I want to filter by, and what I want it to be equal to, for any depth of object. The problem is, I really don't want to use "eval" to do this, but obviously I can't use "[Attribute]" for something like "AwayTeam.TeamID", as it won't work.
Does anyone know of a method I can use to achieve this functionality without using eval?
Something like this would let you traverse the hierarchy of objects to find a value:
var x = {
y: {
z: 1
}
};
function findprop(obj, path) {
var args = path.split('.'), i, l;
for (i=0, l=args.length; i<l; i++) {
if (!obj.hasOwnProperty(args[i]))
return;
obj = obj[args[i]];
}
return obj;
}
findprop(x, 'y.z');
You could add this as a method to your Fixtureobject:
Fixture = Backbone.Model.extend({
findprop: function(path) {
var obj = this.attributes,
args = path.split('.'),
i, l;
for (i=0, l=args.length; i<l; i++) {
if (!obj.hasOwnProperty(args[i]))
return;
obj = obj[ args[i] ];
}
return obj;
}
});
and use it to extract the value
var f = new Fixture();
f.findprop("HomeTeam.TeamID");
The findWhere method could then be rewritten as
findWhere: function (Attribute, Value)
{
return new Project.Collections.Fixtures(this.filter(function (fixture){
return fixture.findprop(Attribute) === Value;
}));
}
And a Fiddle to play with http://jsfiddle.net/nikoshr/wjWVJ/3/
Attributes in JavaScript objects can be accessed by square-bracket, string identifiers as well as the standard dot-notation.
In other words, this:
fixture.attributes.something
is the same as this:
fixture.attributes["something"]
You can also pass variable names in to the square brackets, the value of the variable is used as the key to retrieve.
So you can change your code to this:
findWhere: function (Attribute, Value)
{
return new Project.Collections.Fixtures(this.filter(function (fixture)
{
return fixture.attributes[Attribute] === Value;
}));
}
As you pointed out in the comments, this only handles one level objects and attributes. To get the nested attributes, you'll need to split the "Attribute" variable and loop through the parts. I like #nikoshr's solution for that.
What about using eval() like this:
var myObject = {
first: 'Some',
last: 'Person',
address: {
city: 'Melbourne',
country: 'Australia'
}
}
var propPath = 'address.city';
var city = eval("myObject."+propPath);
console.log(city); // = Melbourne
I took nikoshr's answer and added some recursive flair to it:
var findprop = function (obj, path) {
var args = (typeof path === 'string') ? path.split('.') : path,
thisProperty = obj[args[0]];
if (thisProperty === undefined) { return; } //not found
args.splice(0, 1); //pop this off the array
if (args.length > 0) { return findprop(thisProperty, args); } //recurse
else {return thisProperty; }
};
I'm not sure if there is much benefit to the recursion cpu cycle-wise, but I like recursive functions when they are appropriate

Add new attribute (element) to JSON object using JavaScript

How do I add new attribute (element) to JSON object using JavaScript?
JSON stands for JavaScript Object Notation. A JSON object is really a string that has yet to be turned into the object it represents.
To add a property to an existing object in JS you could do the following.
object["property"] = value;
or
object.property = value;
If you provide some extra info like exactly what you need to do in context you might get a more tailored answer.
var jsonObj = {
members:
{
host: "hostName",
viewers:
{
user1: "value1",
user2: "value2",
user3: "value3"
}
}
}
var i;
for(i=4; i<=8; i++){
var newUser = "user" + i;
var newValue = "value" + i;
jsonObj.members.viewers[newUser] = newValue ;
}
console.log(jsonObj);
A JSON object is simply a javascript object, so with Javascript being a prototype based language, all you have to do is address it using the dot notation.
mything.NewField = 'foo';
With ECMAScript since 2015 you can use Spread Syntax ( …three dots):
let people = { id: 4 ,firstName: 'John'};
people = { ...people, secondName: 'Fogerty'};
It's allow you to add sub objects:
people = { ...people, city: { state: 'California' }};
the result would be:
{
"id": 4,
"firstName": "John",
"secondName": "Forget",
"city": {
"state": "California"
}
}
You also can merge objects:
var mergedObj = { ...obj1, ...obj2 };
thanks for this post. I want to add something that can be useful.
For IE, it is good to use
object["property"] = value;
syntax because some special words in IE can give you an error.
An example:
object.class = 'value';
this fails in IE, because "class" is a special word. I spent several hours with this.
You can also use Object.assign from ECMAScript 2015. It also allows you to add nested attributes at once. E.g.:
const myObject = {};
Object.assign(myObject, {
firstNewAttribute: {
nestedAttribute: 'woohoo!'
}
});
Ps: This will not override the existing object with the assigned attributes. Instead they'll be added. However if you assign a value to an existing attribute then it would be overridden.
extend: function(){
if(arguments.length === 0){ return; }
var x = arguments.length === 1 ? this : arguments[0];
var y;
for(var i = 1, len = arguments.length; i < len; i++) {
y = arguments[i];
for(var key in y){
if(!(y[key] instanceof Function)){
x[key] = y[key];
}
}
};
return x;
}
Extends multiple json objects (ignores functions):
extend({obj: 'hej'}, {obj2: 'helo'}, {obj3: {objinside: 'yes'}});
Will result in a single json object
You can also dynamically add attributes with variables directly in an object literal.
const amountAttribute = 'amount';
const foo = {
[amountAttribute]: 1
};
foo[amountAttribute + "__more"] = 2;
Results in:
{
amount: 1,
amount__more: 2
}
You can also add new json objects into your json, using the extend function,
var newJson = $.extend({}, {my:"json"}, {other:"json"});
// result -> {my: "json", other: "json"}
A very good option for the extend function is the recursive merge. Just add the true value as the first parameter (read the documentation for more options). Example,
var newJson = $.extend(true, {}, {
my:"json",
nestedJson: {a1:1, a2:2}
}, {
other:"json",
nestedJson: {b1:1, b2:2}
});
// result -> {my: "json", other: "json", nestedJson: {a1:1, a2:2, b1:1, b2:2}}
Uses $.extend() of jquery, like this:
token = {_token:window.Laravel.csrfToken};
data = {v1:'asdass',v2:'sdfsdf'}
dat = $.extend(token,data);
I hope you serve them.
Following worked for me for add a new field named 'id'.
Angular Slickgrid usually needs such id
addId() {
this.apiData.forEach((item, index) => {
item.id = index+1;
});

Categories

Resources