I have a JSON file; I want to remove all of the fields or objects, whose names are a specific word (lets say "test") and then return the stripped JSON file back; how can I do it in Node.JS?
Here is an example of my JSON file:
{
"name": "name1",
"version": "0.0.1",
"storage": {
"db": {
"test": "STRING",
"tets2": "STRING",
},
"test": {
"test11": "STRING",
"test2": {
"test3": "0",
"test4": "0"
},
"test5": {
"test6": "0",
"test7": "0"
}
},
"test8": {
"test9": "STRING",
"test10": "STRING"
}
}
}
The desired output:
{
"name": "name1",
"version": "0.0.1",
"storage": {
"db": {
"tets2": "STRING",
},
"test8": {
"test9": "STRING",
"test10": "STRING"
}
}
}
I tried the folloiwng, but I dont know how to use typeof() and check if it is an objectgo deeper in the tree! could you please help me in this regard
var new_json = config;
async.each(Object.keys(config), function(key) {
if (key == "test") {
delete new_json[key];
}
while (typeof (new_json[key]) == "object") {
// How can I handle it here
}
});
console.log("done!");
This function should do it:
function clean(obj,target) {
var tmpobj = obj;
for (var key in tmpobj) {
if (key === target) {
delete obj[key];
}
else if (typeof obj[key] === "object") {
obj[key] = clean(obj[key],target);
}
}
return obj;
}
called this way:
json_struct = clean(json_struct,"test")
Below Recursion code will work. But you need to list of acceptable fields or not acceptable fields and based on that you need to change the below condition IF you know not acceptable fields then use below conditions.
unAcceptableFields.indexOf(key) > 0
var acceptableFields = ["name","version","storage","db", "test9", "test10","tets2", "test8", "test9", "test10" ];
console.log(removeUnwantedFields(testObject, acceptableFields));
function removeUnwantedFields(jsData,acceptableFields) {
var key;
if (jsData) {
for (key in jsData) {
if (acceptableFields.indexOf(key) == -1) {
delete jsData[key];
}
else if(typeof jsData[key] === "object"){
jsData[key] = removeUnwantedFields(jsData[key],acceptableFields);
}
}
}
return jsData;
}
Refer this URL http://jsfiddle.net/55x2V/
Related
I have valid JSON with this structure
const myJSONExample = {
"SubItems": [
{
"SubItems": [
{
"ItemNo": "000001"
}
],
"ItemNo": null,
"Number": null,
"price": 114.46
},
{
"SubItems": [
{
"Group": "0.4.004"
}
],
"type": null
},
{
"SubItems": [
{
"ItemNo": "000005"
},
{
"Quantity": 2
}
],
"Material": "Steel"
},
{
"Description": null
}
]
}
and just simply trying to format all number types in it, using recursive iteration.
const iterate = (obj) => {
Object.keys(obj).forEach(key => {
if(typeof(item[key]) == "number"){
item[key] = new Intl.NumberFormat("de-DE").format(item[key]) //format number for german lang.
}
if (typeof obj[key] === 'object') {
iterate(obj[key])
}
})
}
iterate(myJSONExample);
I used this functions on other JSONs, and been trying to understand for some time, why this throws TypeError: Cannot convert undefined or null to object
null is an "object" hence your issue. So add a truthy check
const iterate = (obj) => {
Object.keys(obj).forEach(key => {
const value = obj[key]
const valueType = typeof value
if (valueType === "number") {
obj[key] = new Intl.NumberFormat("de-DE").format(value)
} else if (valueType === 'object' && value) {
iterate(value)
}
})
}
I am converting JSON keys to the list with dot-notation. If any dot is there represent nested jsonobject and if any [](array notation) is there resents jsonarray.
var keyify = (obj, prefix = '') =>
Object.keys(obj).reduce((res, el) => {
if (Array.isArray(obj[el])) {
return [...res, ...keyify(obj[el][0], prefix + el + '[].')];
} else if (typeof obj[el] === 'object' && obj[el] !== null) {
return [...res, ...keyify(obj[el], prefix + el + '.')];
} else {
return [...res, prefix + el];
}
}, []);
Above is the sample code that I am using for the converion. If input is
{
"input": {
"test": {
"phone": [
{
"phone1": "123"
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
Output will be:
[ 'input.test.phone[].phone1',
'customer[].lastname',
'customer[].firstname' ]
But the above code searches for only first JSONObject's keys in the JSONArray. But if the input is like this:
{
"input": {
"test": {
"phone": [
{
"phone1": "123"
},
{
"a": "456"
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
Then in the above JSON case the code will give output :
[ 'input.test.phone[].phone1',
'customer[].lastname',
'customer[].firstname' ]
So, the key a is missing only phone1 is coming in the list.So, how to get if multiple json keys are there then get keys with index of first occurence.
Expected output
[ 'input.test.phone[0].phone1',
'input.test.phone[1].a',
'customer[0].lastname',
'customer[0].firstname' ]
And if the JSONarray is value then it should be replaced by empty string.
For input:
const data = {
"input": {
"test": {
"phone": [
{
"phone1": ["123456"]
},
{
"a": ["1","2","3","4"]
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
In this case "phone1": ["123456"] and "a": ["1","2","3","4"] are Json array as values this case lis will be like:
Expected Output:
[ 'input.test.phone[0].phone1',//instead of 'input.test.phone[0].phone1[0]'
'input.test.phone[1].a',//instead of 'input.test.phone[1].a[0]','input.test.phone[1].a[1]','input.test.phone[1].a[2]','input.test.phone[1].a[3]',
'customer[0].lastname',
'customer[0].firstname' ]
In the above case jsonarray should be considered as value not key.
You could use for...in loop to create recursive function for this and check if the current data input is an array or not to add dot or square brackets.
const data = { "input": { "test": { "phone": [ { "phone1": ["123456"] }, { "a": ["1","2","3","4"] } ] } }, "customer": [ { "lastname": "def", "firstname": "abc" } ] }
function parse(data, prev = '') {
const result = []
const check = data => {
if (typeof data !== 'object') {
return false
}
if (Array.isArray(data)) {
if (data.some(e => (typeof e != 'object'))) {
return false
}
}
return true;
}
for (let i in data) {
let dot = prev ? '.' : ''
let str = Array.isArray(data) ? `[${i}]` : dot + i
let key = prev + str;
if (check(data[i])) {
result.push(...parse(data[i], key))
} else {
result.push(key)
}
}
return result
}
const result = parse(data);
console.log(result)
You can traverse through the scope of the object and capture any paths that have a non-object value.
This is an extremely uncoupled and generic soulution.
const traverse = (obj, visitorFn, scope = []) => {
for (let key in obj) {
visitorFn.apply(this, [key, obj[key], scope]);
if (obj[key] !== null && typeof obj[key] === 'object') {
traverse(obj[key], visitorFn, scope.concat(key));
}
}
}
const scopeToPath = (obj) => obj.reduce((path, key) =>
path + (!isNaN(key) ? `[${key}]` : `.${key}`), '').substring(1);
const findObjectPaths = (obj) => {
let paths = [];
traverse(obj, (key, value, scope) => {
if (typeof value !== 'object') {
paths.push(scopeToPath(scope.concat(key)));
}
});
return paths;
};
console.log(findObjectPaths(getData()));
function getData() {
return {
"input": {
"test": {
"phone": [{ "phone1": "123" }, { "a": "456" }]
}
},
"customer": [{ "lastname": "def", "firstname": "abc" }]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
You could take a nested approach by having a look to the types of the object.
function flat(object, keys = '') {
if (!object || typeof object !== 'object') return [keys];
if (Array.isArray(object))
return object.every(o => !o|| typeof o!== 'object')
? [keys]
: object.flatMap((o, i, { length }) =>
flat(o, `${keys}[${length === 1 ? '' : i}]`));
return Object
.entries(object)
.flatMap(([k, v]) => flat(v, `${keys}${keys && '.'}${k}`));
}
var data = { input: { test: { phone: [{ phone1: ["123456"] }, { a: ["1", "2", "3", "4"] }] } }, customer: [{ lastname: "def", firstname: "abc" }] },
result = flat(data);
console.log(result);
I have to make a network map in js in which there are components included in component (etc..). I use canvas to do this with the Fabric.js library.
I get a json from an api like this :
{
"name": "BAY_01",
"type": "Bay",
"_links": [{
"name": "SERVER_01",
"type": "Server",
"_links": [{
"name": "CPU"
}, {
etc...
}],
}]
}
I think the best way to implement the draw of these components is to make it recursive but I don't know how to do this.
Can anyone help me to solve my issue?
Here is an example of using recursion by name. Basically you need to determine if a property of the JSON object is an array then call recursion function again, else check if property name is "name" then call function to draw shape by name:
var obj = {
"name": "BAY_01",
"type": "Bay",
"_links": [{
"name": "SERVER_01",
"type": "Server",
"_links": [{
"name": "CPU"
}, {
"name": "SERVER_02",
"type": "Server",
"_links": [{
"name": "CPU2"
}]
}]
}]
};
function goThroughtObject(obj, name) {
var key;
if (obj instanceof Array) {
return obj.map(function(value) {
if (typeof value === "object") {
goThroughtObject(value, name)
}
return value;
})
} else {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (key === name){
drawByName(obj[key]);
}
if (obj[key] instanceof Array || (obj[key] !== null && obj[key].constructor === Object)) {
goThroughtObject(obj[key], name);
}
}
}
}
};
//implement fabricjs logic in this function
function drawByName (name) {
console.log("Fabricjs will draw a: " + name);
}
goThroughtObject(obj, 'name');
Please remember to use canvas.renderAll(); function after the recursion function as long as it will be better performance.
I have a json that is similar to the code below.
var jsonData = {
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"CpuShares": 0,
"Cpuset": "",
"Domainname": "",
"Entrypoint": null,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOME=/root"
]
"Hostname": "git",
"WorkingDir": ""
},
"Created": "2015-03-03T08:59:05.735601013Z",
"Name": "/git",
"NetworkSettings": {
"Ports": {
"22/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "2008"
}
],
"80/tcp": null,
"8006/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "9008"
}
]
}
},
"ResolvConfPath": "/etc/resolv.conf",
"State": {
"Pid": 6146,
"Running": true,
"StartedAt": "2015-03-03T08:59:05.829535361Z"
}
}
As you can see any properties has it's own unique name. My question is: How can I access the properties like Env in this way getValue(jsonData, 'Env'); ?
My json is much bigger and more complex than what I put above.
Here's a function that allows you to recurse through an object to find the information you need if you don't know the structure of the object. Note that this will break out of the function when the first instance of that key has been found. This means that if you have more than one key called HostIp, it will only find the first one.
function getValue(obj, key) {
var found = null;
var recurse = function (obj, key) {
for (var p in obj) {
if (p === key) {
found = obj[p];
break;
}
if (obj[p] !== null && typeof obj[p] === 'object') recurse(obj[p], key);
}
}
recurse(obj, key);
return found;
}
getValue(jsonData, 'Env'); // [ "PATH=/usr/local/sbin:/usr/local/bin…", "HOME=/root" ]
If you want to find all instances of a particular key name use the following code instead. It will add all matches to an array and return that array once the object has been trawled. This isn't particularly useful tho because it doesn't provide the context in which it found the key, but it might give you a few ideas.
function getValue(obj, key) {
var found = [];
var recurse = function (obj, key) {
for (var p in obj) {
if (p === key) {
found.push(obj[p]);
}
if (obj[p] !== null && typeof obj[p] === 'object') recurse(obj[p], key);
}
}
recurse(obj, key);
return found;
}
getValue(jsonData, 'HostPort'); // [ "2008", "9008" ]
DEMO
I have an object which at some points is four levels deep, however I want a function that will cope should more levels be introduced. I'm trying to write a function that will replaced elements such that <span class="ajax-parent1-parent2-parent3-value"></span> will be replaced with parent1.parent2.parent3.value.
The issue is that the depth is variable, so I could have something like <span class="ajax-parent1-value"></span> to be replaced with parent1.value.
Finally, it's not always the text to be replaced. Optionally, data-attr can specify an attribute to be used instead (through element.attr(<data-attr>, <value>)).
Currently, I'm iterating manually, however it isn't very clean so I was wondering if there is a better way to do it. This also doesn't work for greater than two levels deep.
function render(data) {
$.each(data, function(parent, value) {
$.each(value, function(element, value) {
$el = $('.ajax-' + parent + '-' + element);
$.each($el, function(key, el) {
if ($(el).data('attr')) {
$(el).attr($(el).data('attr'), value);
} else {
$(el).text(value);
}
}
});
});
}
Example object:
{
"profile": {
"username": "johnd",
"bio": "Some example data about John",
"website": "http://john.com",
"profile_picture": "http://john.com/me.jpg",
"full_name": "John Doe",
"counts": {
"media": 13,
"followed_by": 26,
"follows": 49
},
"id": "16"
},
"dashboard": {
"script": {
"tags": ["media"],
"stats": {
"liked": 0,
"lastrun": "never",
"duration": 0
},
"status": {
"code": 0,
"slug": "disabled",
"human": "Disabled",
"message": "Not running."
}
},
"account": {
"plan": "free",
"created": 1419261005373,
"updated": 1419261005373
}
},
"serverInformation": {
"serverName": "Johns API",
"apiVersion": "0.0.1",
"requestDuration": 22,
"currentTime": 1419262805646
},
"requesterInformation": {
"id": "redacted",
"fingerprint": "redacted",
"remoteIP": "redacted",
"receivedParams": {
"action": "getDashboard",
"apiVersion": 1
}
}
}
Here is the solution I wrote:
function iterate(obj, stack) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], stack + '-' + property);
} else {
$group = $('.ajax' + stack + '-' + property);
$.each($group, function(key, element) {
if ($(element).data('attr')) {
$(element).attr($(element).data('attr'), obj[property]);
} else {
$(element).text(obj[property]);
}
});
}
}
}
}
Why don't you start from the HTML, so you only access the properties you actually want to render?
That way you can keep it quite simple (also note that this removes the need to nest HTML spans in the same order/depth as the data object, you can just place any HTML node anywhere. Just make sure you don't use class/node names more then once.
function parseData(data) {
var $container = $('.ajax');
$container.find("[class^='ajax-']").each(function(i, el) {
var $el = $(el);
if ($el.children().length === 0)
{
var nodes = $el.attr('class').split('-');
nodes.shift();
var node = data;
for (var i = 0; i < nodes.length; i++) {
node = node[nodes[i]];
if (typeof(node) == "undefined") {
break;
}
}
if ($el.data('attr'))
{
$el.attr($el.data('attr'), node);
}
else
{
$el.text(node);
}
}
});
}
Fiddle here:
http://jsfiddle.net/ckcduLhn/5/