I have a process that returns a json object:
data={key1:[], key2:[], key3:[{key1:"a"}, {key2:"b"}], key4:[{key1:"c"}, {key2:"d"}]}
I want know if there is a simple way to filter this json object to remove the properties where the value is an empty array.
Once filtered I can then loop through the remaining properties and action the array elements.
First, we have to iterate over properties in an object.
for (var prop in data) {
if (data.hasOwnProperty(prop)) {
// Logic here
}
}
Then it's a simple check to filter out empty array properties
if (data[prop].length == 0) {
delete data[prop]
}
The full solution,
for (var prop in data) {
if (data.hasOwnProperty(prop)) {
if (data[prop].length == 0) {
delete data[prop]
}
}
}
I would prefer to create a new object that omits the empty arrays instead of deleting from the existing object.
var data={key1:[], key2:[], key3:[{key1:"a"}, {key2:"b"}], key4:[{key1:"c"}, {key2:"d"}]}
var cleanData = Object.keys(data).reduce((obj, key) => {
if (data[key] && data[key].length) {
obj[key] = data[key]
}
return obj
}, {})
Using lodash should make this pretty simple:
var filtered = _.omitBy(data, function(value) {
return Array.isArray(value) && value.length == 0;
});
You can now loop through the remaining elements in the filtered object to take further actions on them.
Try this working demo :
var data = {
key1:[],
key2:[],
key3:[
{key1:"a"},
{key2:"b"}
],
key4:[
{key1:"c"},
{key2:"d"}
]
};
for (var i in data) {
if (data[i].length == 0) {
delete data[i]
}
}
console.log(data);
Related
How can I write a test for an empty value of a specific JSON name pair. For example I have this JSON:
{
"data": {
"sectionGroupName": "PPConfig:APIMethod",
"sections": {}
},
"success": true,
"errorMessage": ""
}
I want to check if sections is empty, like it is in this case. I have other successful tests written like this:
tests["Status code is 200"] = responseCode.code === 200;
var body = JSON.parse(responseBody);
tests["Success Response"] = body.success === true;
tests["No Error message"] = body.errorMessage === "";
tests.Data = body.data.sectionGroupName === "PPConfig:APIMethod";
But I haven't been able to find successful test code for checking if the value of a specific name is an empty dictionary. Can someone help me with this as an example please?
You can get the list of properties of sections and test its length.
let sectionKeys = Object.keys(body.data.sectionGroupName)
if(sectionKeys.length){
//Proceed with section
} else {
//Proceed when it's empty
}
See Object.keys()
from this link
to check if it's a dictionary (use your 'sections' as v)
function isDict(v) {
return !!v && typeof v==='object' && v!==null && !(v instanceof Array) && !(v instanceof Date) && isJsonable(v);
}
Then check that it is empty (from this other link) use:
function isEmpty(obj) {
for (var x in obj) { return false; }
return true;
}
That should work
I'm trying to create an array of JSON objects in typescript. And following is the approach I have used.
var queryMutations:any = _.uniq(_.map(mutationData.result,
function(mutation:Mutation) {
if (mutation && mutation.gene) {
var item = {facet: "MUTATION", term: mutation.gene + " " + mutation.proteinChange}
return item;
}
else {
return {};
}
}));
var jsonString = JSON.stringify(queryMutations);
is this the correct way to do it? Appreciate your suggestions.
To me it looks ok. I'd personally make a few layout style modifications and use backtick placeholder strings.
var queryMutations:any =
_.uniq(
_.map(
mutationData.result,
function(mutation:Mutation) {
if (mutation && mutation.gene) {
return {facet: "MUTATION",
term: `${mutation.gene} ${mutation.proteinChange}`
} else {
return {};
}
}
)
);
var jsonString = JSON.stringify(queryMutations);
I am using xml2js to convert xml to js object and add new nodes to the content
Ex1:
<abc>
<my-node>123</my-node>
<my-node>456</my-node>
</abc>
Ex2:
<abc>
<my-node>123</my-node>
</abc>
In the Ex1, the my-node property will be an array whereas in the Ex2, it will be non-array item.
How to add extra my-node to the same. I can do in below format but looking for better solution?
if(typeof abc.my-node == Array){
abc.my-node.push(xxx);
} else {
//create empty array
//add existing element
//add xxx
//set the array to json object
}
If you use
function addProp(obj, propName, value) {
if (propName in obj) {
if (obj[propName] instanceof Array) {
obj[propName].push(value);
}
else if (typeof obj[propName] !== 'object') {
obj[propName] = [obj[propName], value];
}
}
else {
obj[propName] = value;
}
}
var abc = {};
console.log(JSON.stringify(abc));
addProp(abc, 'my-node', 123);
console.log(JSON.stringify(abc));
addProp(abc, 'my-node', 456);
console.log(JSON.stringify(abc));
addProp(abc, 'my-node', 789);
console.log(JSON.stringify(abc));
then the result is
{}
{"my-node":123}
{"my-node":[123,456]}
{"my-node":[123,456,789]}
OK, we all know this works:
vm.myObject = {
required : "This field requires data",
.....
}
But how can I create that same object dynamically when the property 'keys' and 'values' come from a json file, eg:
json:
[
{ "key" :"required", "value": "This field requires data"},
.....
]
service:
var myObject = {}
DynamicObjSvc.get()
.success(function(data){
data.forEach(function(item){
// pass each key as an object property
// and pass its respective value
?????????
})
.....
UPDATE:
Kavemen was mostly correct, this turned out to be the solution:
var myObject = {};
DynamicObjSvc.all()
.success(function(data){
angular.forEach(data, function(msg) {
myObject[msg.key] = msg.value; <-- his answer was incorrect here
});
$fgConfigProviderRef.validation.message(myObject);
})
.error(function(err){
console.log(err.message);
})
You can use angular.forEach and the bracket notation for setting (and getting) object properties in Javascript
var myObject = {}
DynamicObjSvc.get().success(
function(data) {
angular.forEach(data, function(value, key) {
myObject[key] = value;
});
}
);
See also Working with Objects from MDN
EDIT
I see now that your data is really an array of objects, not just a single object, so yes, the code above could lead you astray.
In any case, the method of setting an object's properties dynamically using the bracket notation is sound; the loop could be reworked to handle your data array as such:
//we have an array of objects now
var myObjects = [];
DynamicObjSvc.get().success(
function(data) {
//for each object in the data array
for(var i = 0; i < data.length; i++) {
//create and populate a new object for the ith data element
var newObject = {};
angular.forEach(data[i], function(value, key) {
newObject[key] = value;
});
//and add it to the overall collection
myObjects.push(newObject);
}
}
);
Angular.js has a handy built-in filter, json, which displays JavaScript objects as nicely formatted JSON.
However, it seems to filter out object properties that begin with $ by default:
Template:
<pre>{{ {'name':'value', 'special':'yes', '$reallyspecial':'Er...'} | json }}</pre>
Displayed:
{
"name": "value",
"special": "yes"
}
http://plnkr.co/edit/oem4HJ9utZMYGVbPkT6N?p=preview
Can I make properties beginning with $ be displayed like other properties?
Basically you can't. It is "hard-coded" into the filter's behaviour.
Nonetheless, it is quite easy to build a custom JSON filter that behaves identically with the Angular's one but not filtering out properties starting with '$'.
(Scroll further down for sample code and a short demo.)
If you take a look at the 1.2.15 version source code, you will find out that the json filter is defined like this:
function jsonFilter() {
return function(object) {
return toJson(object, true);
};
}
So, it uses the toJson() function (the second parameter (true) means: format my JSON nicely).
So, our next stop is the toJson() function, that looks like this:
function toJson(obj, pretty) {
if (typeof obj === 'undefined') return undefined;
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
}
This function makes use of the "native" JSON.stringify() function, passing a custom replacer function (toJsonReplacer).
The toJsonReplacer() function handles some special cases: It checks if the key starts with $ and ignores it if it does (this is what we want to change) and it checks if the value is either a Window, a Document or a Scope object (in which case it converts it to a descriptive string in order to avoid "Converting circular structure to JSON" errors).
function toJsonReplacer(key, value) {
var val = value;
if (typeof key === 'string' && key.charAt(0) === '$') {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
} else if (value && document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
return val;
}
For the sake of completeness, the two functions that check for Window and Scope look like this:
function isWindow(obj) {
return obj && obj.document && obj.location && obj.alert && obj.setInterval;
}
function isScope(obj) {
return obj && obj.$evalAsync && obj.$watch;
}
Finally, all we need to do is to create a custom filter that uses the exact same code, with the sole difference that our toJsonReplacer() won't filter out properties starting with $.
app.filter('customJson', function () {
function isWindow(obj) {
return obj &&
obj.document &&
obj.location &&
obj.alert &&
obj.setInterval;
}
function isScope(obj) {
return obj &&
obj.$evalAsync &&
obj.$watch;
}
function toJsonReplacer(key, value) {
var val = value;
if (isWindow(value)) {
val = '$WINDOW';
} else if (value && (document === value)) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
return val;
}
function toJson(obj, pretty) {
if (typeof obj === 'undefined') { return undefined; }
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
}
return function(object) {
return toJson(object, true);
};
});
See, also, this short demo.
* The downside is that your custom JSON filter will not benefit from further improvement/enhancement of Angular's json filter, so you'll have to re-define your's to incorporate changes. Of course, for such a basic and simple filter like this, one should'nt expect frequent or extensive changes, but that doesn't mean there aren't going to be any.