String is :
AVG(Disk Usage,CPU USAGE,NETWORK USAGE,SUM(Shared Memory Usage,System Memory Usage))
Output Required :
{
"operation": "AVG",
"rules": [
{
"field": "Disk Usage"
},
{
"field": "CPU Usage"
},
{
"field": "Network Usage"
},
{
"operation": "SUM",
"rules": [
{
"field": "Shared Memory Usage"
},
{
"field": "System Memory Usage"
}
]
}
]
}
I've wrote the solution for your case using some regexp pattern, RegExp.exec, String.split and Array.map functions. I also added one more nested operator to process complex example.Hope it will help ...
var str = "AVG(Disk Usage,CPU USAGE,NETWORK USAGE,SUM(Shared Memory Usage,System Memory Usage),COUNT(Processes,Services))",
re = /(\w+?)\(([^()]+)(?=,\w+?\()|(?:,\))?(\w+?)\(([^)]+)/g, m, idx,
obj = {}, result = {};
while ((m = re.exec(str)) !== null) {
idx = m.index; // position of the current matched item in the initial input string
m = m.filter((v) => v); // to get consecutive filled matched items
obj = {'operation': m[1], 'rules' : m[2].split(",").map((v) => ({'field':v}))};
if (idx === 0) { // the first function(operator) i.e. "AVG"
result = obj;
} else {
result['rules'].push(obj);
}
}
console.log(JSON.stringify(result, 0, 4));
The output:
{
"operation": "AVG",
"rules": [
{
"field": "Disk Usage"
},
{
"field": "CPU USAGE"
},
{
"field": "NETWORK USAGE"
},
{
"operation": "SUM",
"rules": [
{
"field": "Shared Memory Usage"
},
{
"field": "System Memory Usage"
}
]
},
{
"operation": "COUNT",
"rules": [
{
"field": "Processes"
},
{
"field": "Services"
}
]
}
]
}
Try it:
JSON.parse(jsonObj); //javascript
You should do something like this:
var jsonObj = '{"TeamList" : [{"teamid" : "1","teamname" : "Barcelona"}]}';
var obj = $.parseJSON(jsonObj);
Hope it helps;)
Related
I have a need to get data out of a NiFi flow file with somewhat complex JSON content. I'm planning to use a NiFi ExecuteScript processor because I don't think it can be done with EvaluateJSONPath. The content looks like this (snippet)
...
"segments": [
{
"INS01": "Y",
"INS03": "001",
"INS02": "18",
"INS05": "A",
"id": "INS",
"INS04": "AI",
"INS08": "FT"
},
{
"REF02": "1041558xxxxx",
"REF01": "0F",
"id": "REF"
},
{
"REF02": "ABD",
"REF01": "1L",
"id": "REF"
},
{
"REF02": "106835xxxxx",
"REF01": "23",
"id": "REF"
}
],
...
I want to extract the REF02 property value from the segments array element that has REF01 === '0F'. The array element does not necessarily have a REF02 property. So in the above case, I should get 1041558xxxxx.
Here's my current script:
var flowFile = session.get()
if (flowFile != null) {
var InputStreamCallback = Java.type('org.apache.nifi.processor.io.InputStreamCallback')
var IOUtils = Java.type('org.apache.commons.io.IOUtils')
var StandardCharsets = Java.type('java.nio.charset.StandardCharsets')
try {
var subscriber = null
session.read(flowFile,
new InputStreamCallback(function (inputStream) {
var data = JSON.parse(IOUtils.toString(inputStream, StandardCharsets.UTF_8))
var segment = data.segments.find(function (s) { return s.hasOwnProperty('REF01') && s.REF01 === '0F' })
subscriber = segment ? segment.REF02 : null
}));
session.putAttribute(flowFile, 'subscriber', subscriber ? subscriber : '')
session.transfer(flowFile, REL_SUCCESS)
} catch (e) {
log.error('script failed', e)
session.transfer(flowFile, REL_FAILURE)
}
}
When I execute the above, I get a java.lang.NoSuchMethodException. Also, are anonymous 'arrow' functions allow?
I've tried using an old-school for loop to no avail.
Thanks for your help.
You can add a JoltTransformJSON processor with specification
[
{
"operation": "shift",
"spec": {
"segments": {
"*": {
"REF01": {
"0F": {// conditional to match "REF01" with "0F"
"#2,REF02": ""// go two levels up the three to reach the level of the attributes REF01 or REF02
}
}
}
}
}
}
]
in order to return the result
"1041558xxxxx"
You can use below JSONPath with EvaluateJSONPath processor:
$.segments[?(#.REF01<="0F")]#.REF02
Note: Returned result is in the array, So you can use SplitJSON after that to get your string.
Groovy script:
import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets
import groovy.json.JsonSlurper
flowFile = session.get()
if(!flowFile) return
def jsonSlurper = new JsonSlurper()
def subscriber = ""
flowFile = session.write(flowFile, {inputStream, outputStream ->
input = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
json = jsonSlurper.parseText(input)
segment = json.segments.find{ segment ->
if (segment.keySet().contains('REF01')) {
if (segment.REF01 == '0F') {
return true
} else {
return false
}
} else {
return false
}
}
if (segment) {
subscriber = segment.REF02
}
outputStream.write(input.getBytes(StandardCharsets.UTF_8))
} as StreamCallback)
session.putAttribute(flowFile, 'subscriber', subscriber)
session.transfer(flowFile, REL_SUCCESS)
input:
{
"test": "best",
"segments": [
{
"INS01": "Y",
"INS03": "001",
"INS02": "18",
"INS05": "A",
"id": "INS",
"INS04": "AI",
"INS08": "FT"
},
{
"REF02": "1041558xxxxx",
"REF01": "0F",
"id": "REF"
},
{
"REF02": "ABD",
"REF01": "1L",
"id": "REF"
},
{
"REF02": "106835xxxxx",
"REF01": "23",
"id": "REF"
}
]
}
output (with attribute subscriber: 1041558xxxxx):
{
"test": "best",
"segments": [
{
"INS01": "Y",
"INS03": "001",
"INS02": "18",
"INS05": "A",
"id": "INS",
"INS04": "AI",
"INS08": "FT"
},
{
"REF02": "1041558xxxxx",
"REF01": "0F",
"id": "REF"
},
{
"REF02": "ABD",
"REF01": "1L",
"id": "REF"
},
{
"REF02": "106835xxxxx",
"REF01": "23",
"id": "REF"
}
]
}
I need to change the structure of the payload response from the REST call on the frontend. Right now the api returns the data in the following format:
[
{
"row": [
{
"name": "Desc",
"value": "7777 - Florida Hurricane"
},
{
"name": "DSTR_NR",
"value": "7777"
}
]
},
{
"row": [
{
"name": "Desc",
"value": "7172 - Virginia Severe Storm(s)"
},
{
"name": "DSTR_NR",
"value": "7172"
}
]
}
]
This is what I have so far but it does not give me the data in the proper format:
let result = payload.reduce(function(res, obj) {
let temp = obj.row.map(function(o) {
return Object.assign({}, o);
});
return res.concat(temp);
},[])
return result;
// instead, this returns the data in the following way:
{name: "Desc", value: "7777 - Florida Hurricane"}
{name: "DSTR_NR", value: "7777"}
How can I make the payload response structure into the following desired format?
{name: "7777 - Florida Hurricane", value: "7777"}
{name: "7172 - Virginia Severe Storm(s)", value: "7172"}
for name, I only want the value for Desc and
for value, I only want the value for DSTR_NR
I think that's what you're looking for:
const getDesiredFormat = response => response.map(data => {
const row = data['row']
return {
name: row[0].value,
value: row[1].value
}
}, [])
This should be easy.
Here you go..
var data = [
{
"row": [
{
"name": "Desc",
"value": "7777 - Florida Hurricane"
},
{
"name": "DSTR_NR",
"value": "7777"
}
]
},
{
"row": [
{
"name": "Desc",
"value": "7172 - Virginia Severe Storm(s)"
},
{
"name": "DSTR_NR",
"value": "7172"
}
]
}
].reduce((arr, value)=>{
var row = value.row;
arr.push({name:row.find(x=> x.name =="Desc").value, value:row.find(x=> x.name =="DSTR_NR").value})
return arr
},[])
console.log(data)
you can use map
var data = [{
"row": [{
"name": "Desc",
"value": "7777 - Florida Hurricane"
},
{
"name": "DSTR_NR",
"value": "7777"
}
]
},
{
"row": [{
"name": "Desc",
"value": "7172 - Virginia Severe Storm(s)"
},
{
"name": "DSTR_NR",
"value": "7172"
}
]
}
];
console.log(data.map(i => {
if (i.row.length) {
var val1 = i.row.find(f => f.name.toLowerCase() === "desc");
var val2 = i.row.find(f => f.name.toLowerCase() === "dstr_nr");
if (val1 && val2) {
return {
name: val1.value,
value: val2.value
};
}
}
}))
This should handle your problem
result = data.reduce(function(res, obj) {
let temp = obj.row.reduce(function(tempObj, element) {
if(element.name === "Desc"){tempObj.name = element.value}
else tempObj.value = element.value
return tempObj
}, {});
return res.concat(temp);
},[])
I have an object with a triple nested objects. I want to flatten the object to clearly review the values and place in an HTML.
{
"templateName": "Test template",
"assignmentGroup": "Test template",
"fields": [{
"type": "Multi Select dropdown",
"entry": [{
"checked": false,
"value": "govtBusinses",
"displayValue": "Government Business Division"
}, {
"checked": true,
"value": "privateSector",
"displayValue": "Private Sector"
}, {
"checked": true,
"value": "publicSector",
"displayValue": "Public Sector"
}]
}, {
"type": "Text Field",
"entry": null
}]
}
I tried flatting it, but I want it to be in desired format.
flattenObject = function(ob) {
let toReturn = {};
for (let i in ob) {
if (!ob.hasOwnProperty(i)) {
continue;
}
if ((typeof ob[i]) === "object") {
let flatObject = this.flattenObject(ob[i]);
for (let x in flatObject) {
if (!flatObject.hasOwnProperty(x)) {
continue;
}
toReturn[i + "." + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
console.log(toReturn);
return toReturn;
};
Expected:
TemplateName : "Test template"
Assignment Group : "sample"
Field 0 :
Type : Multiselect dropdown
Entry 0 :
checked : false
value :
buisness:
Entry 1:
checked : false
value :
buisness:
Field 1:
Type:
.......
How can i achieve this?
You can't have different values with same Key in object. I guess better way to achieve this would be to return array of objects instead.
try this:
let obj = {
"templateName": "Test template",
"assignmentGroup": "Test template",
"fields": [{
"type": "Multi Select dropdown",
"entry": [{
"checked": false,
"value": "govtBusinses",
"displayValue": "Government Business Division"
}, {
"checked": true,
"value": "privateSector",
"displayValue": "Private Sector"
}, {
"checked": true,
"value": "publicSector",
"displayValue": "Public Sector"
}]
}, {
"type": "Text Field",
"entry": null
}]
}
let fieldsCount = []
flattenObject = function(ob, toReturnArr) {
for (let i in ob) {
if (!ob.hasOwnProperty(i)) {
continue;
}
if ((typeof ob[i]) === "object") {
if (isNaN(i)) {
fieldsCount[i] = fieldsCount[i] === undefined ? 0 : fieldsCount[i] + 1
ob[i] && ob[i].length ? console.log(`${i}: ${fieldsCount[i]}`) : null;
}
toReturnArr = this.flattenObject(ob[i], toReturnArr);
} else {
console.log(`${i}: ${ob[i]}`);
toReturnArr.push({
[i]: ob[i]
})
}
}
return toReturnArr;
};
flattenObject(obj, [])
Result:
templateName: Test template
assignmentGroup: Test template
fields: 0
type: Multi Select dropdown
entry: 0
checked: false
value: govtBusinses
displayValue: Government Business Division
checked: true
value: privateSector
displayValue: Private Sector
checked: true
value: publicSector
displayValue: Public Sector
type: Text Field
PS: {[key]: object[key]} using variable for key value like [key] is the ES6 feature.
I have a data set of the following form
let data = [
{
"id": {
"primary": "A1"
},
"msg": 1
}, {
"id": {
"primary": "A1"
},
"msg": 2
}, {
"id": {
"primary": "B2"
},
"msg": 3
}
]
I would like to transform it to
newData = [
{
"id": {
"primary": "A1"
},
"items": [
{ "msg": 1 },
{ "msg": 2 }
]
},
{
"id": {
"primary": "B2"
},
"items": [
{ "msg": 3 }
]
}
]
I think the method is something like the following, but am not sure how to check against undefined values in this case.
let newData = [];
for (let i = 0; i < data.length; i++) {
if (newData[i]['id']['primary'] === data[i]['id']) newData.push(data[i]['id'])
else newData[i]['items'].push(data[i]['msg'])
}
How can I transform the original data set to merge entries with a matching primary id?
One option would be to use .reduce() to create a new array from the existing.
I've added comments to clarify.
let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ];
let result = data.reduce((out,item) => {
let {id, ...items} = item; //Separate the "id" and "everything else"
let existing = out.find(({id}) => id.primary == item.id.primary);
existing //have we seen this ID already?
? existing.items.push(items) //yes - add the items to it
: out.push({ id: {...id}, items: [items]}); //no - create it
return out;
}, []);
console.log(result);
A couple notes:
You may notice that I've set the ID using id: {...id}, despite the id already being an object. This is because using the existing id object would create a reference, whereas {...id} creates a shallow copy.
I haven't specified the msg property anywhere. Instead, any properties that aren't id will be added to the items list (example below).
let data = [ { "id": { "primary": "A1" }, "msg": 1, "otherStuff": "Hello World!" }, { "id": { "primary": "A1" }, "msg": 2, "AnotherThing": true }, { "id": { "primary": "B2" }, "msg": 3, "someOtherProperty": false } ];
let result = data.reduce((out,item) => {
let {id, ...items} = item;
let existing = out.find(({id}) => id.primary == item.id.primary);
existing
? existing.items.push(items)
: out.push({ id: {...id}, items: [items]});
return out;
}, []);
console.log(result);
That said, if you start to nest objects (other than ID), they will likely be included as references; ...items is only a shallow copy.
If such a case, consider something like JSON.parse(JSON.stringify(...)) for a deep copy. Be sure to read the link though; there are caveats.
You could also solve this in a concise way via the Array.reduce and ES6 destructuring:
let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ]
let result = data.reduce((r, {id, msg}) =>
((r[id.primary] = r[id.primary] || { id, items: [] }).items.push({msg}), r), {})
console.log(Object.values(result))
In more readable format it is:
let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ]
let result = data.reduce((r, {id, msg}) => {
r[id.primary] = (r[id.primary] || { id, items: [] })
r[id.primary].items.push({msg})
return r
}, {})
console.log(Object.values(result))
The idea is to group by the id.primary and then once the grouping is done simply get the values via Object.values
Notice that this is one pass solution where you do not have to per each iteration do an Array.find against the current accumulator.
I am trying to find a child object in JSON by one of its properties and add more properties to that object. I am not sure how to do this using JQuery (or regular javascript). For example: From the following JSON, I would like to find a category with id 123-1 and then add another category object as a child object. Thanks for your help.
JSON:
{
"result": {
"category":
{
"id": 123,
"name": "cat1",
"rules": [
{
"rulename": "r1",
"regex": ""
},
{
"rulename": "r2",
"regex": ""
}
],
"category":
{
"id": "123-1",
"name": "cat1-1",
"rules": [
{
"rulename": "r1-1",
"regex": ""
}
]
}
}
}
}
Javascript:
function addSubCategory(catId, anotherCatObj) {
//Step1: Find category object with catID in the existing json
//Step3: add the supplied object as a child.
}
function appendCategoryTo(categories, destinationCategoryId, newCategoryToAdd){
var success = false;
for (var i = 0; i < categories.length && !success; i++){
var category = categories[i];
if (category.id == destinationCategoryId){
category.category = category.category || [];
success = !!category.category.push(newCategoryToAdd);
} else if (category.category) {
success = appendCategoryTo(category.category, destinationCategoryId, newCategoryToAdd);
}
}
return success;
}
you have to start at the obj.result.category node in order to take advantage of the recursive ability, but you can easily wrap that method in another that makes it more polite.
but, as-is, here's an example usage:
appendCategoryTo(o.result.category, '123-1', {
id: '123-1-1',
name: 'cat-1-1-1',
rules: []
});
console.log(JSON.stringify(o));
Which adds a new category property to the nested category as an array (i assume this follows the nomenclature) then adds the element to that new array--thus giving you:
{
"result": {
"category": [
{
"id": 123,
"name": "cat1",
"rules": [
{
"rulename": "r1",
"regex": ""
},
{
"rulename": "r2",
"regex": ""
}
],
"category": [
{
"id": "123-1",
"name": "cat1-1",
"rules": [
{
"rulename": "r1-1",
"regex": ""
}
],
"category": [ // BEGIN new addition
{
"id": "123-1-1",
"name": "cat-1-1-1",
"rules": [
]
}
] // END new addition
}
]
}
]
}
}
Example to play with on jsfiddle, btw: http://jsfiddle.net/cqRzX/