I have the following code which calls two different API's, parses the JSON data and displays it on a webpage. Both JSON datasets have the same structure, one with 5 columns and the other one with 20 columns.
The JavaScript code I am using is shown below. How can I combine both JSON datasets into one, so there's a resulting dataset with 25 columns, enabling me to search/reference across all those 25 columns?
The Data Structure of both JSON datasets is as follows:
{
"datatable": {
"data": [
[
"TSLA",
"2019-02-22",
"2019-02-22",
58995.9,
-231.2
]
],
"columns": [
{
"name": "ticker",
"type": "String"
},
{
"name": "date",
"type": "Date"
},
{
"name": "lastupdated",
"type": "Date"
},
{
"name": "ev",
"type": "BigDecimal(15,3)"
},
{
"name": "evebit",
"type": "BigDecimal(15,3)"
}
]
},
"meta": {
"next_cursor_id": null
}
}
The JavaScript Code is as follows:
var apiurls = [
'api1.json',
'api2.json'
],
elroot = document.getElementById('root'),
index = 0;
function setup() {
loadJSON(apiurls[index], gotData);
}
function gotData(data) {
var daten = data.datatable.data[0],
spalten = data.datatable.columns,
output = '';
for (var i = 0; i < spalten.length; i++) {
output = '<p>' + spalten[i].name + ': ' + daten[i] + '</p>';
elroot.innerHTML += output;
}
if (++index < apiurls.length) {
setup();
}
}
something like that ?
var
Json_1 = {
"datatable": {
"data" : ['aa','bb','cc'],
"columns": ['x','y','z']
},
"meta": { 'meta1': 15, 'meta2':87 }
},
Json_2 = {
"datatable": {
"data" : ['ZZbb','cZc'],
"columns": ['xf','yf','zf','zgg']
},
"meta": { 'meta1': 879, 'meta2':4 }
},
Json_cumul_typ0 = { Json_1, Json_2 },
Json_cumul_typ1 = {
"data" : [].concat( Json_1.datatable.data, Json_2.datatable.data ),
"columns": [].concat( Json_1.datatable.columns, Json_2.datatable.columns ),
}
;
console.log( Json_cumul_typ0 );
console.log( Json_cumul_typ1 );
It would be easier to make all the API calls first, combining them into a single result object before doing any processing. Currently, you are making an API call, then processing the results before making the next API call.
I think the nature of async callbacks is making your task more difficult. I suggest using async/await to simplify the logic. Something like this:
var apiurls = [
'api1.json',
'api2.json'
],
elroot = document.getElementById('root');
// Combine all API responses into this object
allResults = {
data: [[]],
columns: []
};
// loadJSON() is probably not async, so here is an async version using fetch()
async function loadJSON(url) {
response = await fetch(url);
return response.json()
}
// Wrap logic in async function so await can be used
(async () => {
// First make all the API calls
for (url of apiurls) {
results = await loadJSON(url);
allResults.data[0] = allResults.data[0].concat(results.datatable.data[0]);
allResults.columns = allResults.columns.concat(results.datatable.columns);
}
// Then process combined allResults object here once.
var daten = allResults.data[0],
spalten = allResults.columns,
output = '';
for (var i = 0; i < spalten.length; i++) {
output = '<p>' + spalten[i].name + ': ' + daten[i] + '</p>';
elroot.innerHTML += output;
}
})();
The loadJSON() you are using probably isn't async. Here are some alternatives you can use:
fetch()
axios
var object1 = {
"datatable": {
"data": [],
"columns": [1,2,3,4]
},
"meta": {}
}
var object2 = {
"datatable": {
"data": [],
"columns": [6,7,8,9,0,11,12,123]
},
"meta": {}
}
Now you want to concatenate columns field. So what you can do is create a deep copy of one of the above. There are better ways to do this than the one mentioned below.
var object3 = JSON.parse(JSON.stringify(object1));
Now to concatenate columns do this,
object3.datatable.columns = object3.datatable.columns.concatenate(object2.datatable.columns);
If you want to concatenate multiple fields you can use a for loop on an object, check if the data type is an array and do the concatenation.
I hope this helps.
Related
I am trying to replace / change the values in an object, but I can't seem to work out how it's done or if it can even be done.
I'm trying to add https://SiteName.com to the start of each of the values so it will be like https://SiteName.com\/asset\/image\/map\/map-grass.svg
var assets = [{
"name": "\/asset\/image\/map\/map-grass.svg",
"url": "\/asset\/image\/map\/map-grass.svg"
}, {
"name": "\/asset\/image\/map\/map-stone.svg",
"url": "\/asset\/image\/map\/map-stone.svg"
}]
Object.keys(assets).forEach(key => {
const val = assets[key];
console.log(val)
});
Try this:
var assets = [{
"name": "\/asset\/image\/map\/map-grass.svg",
"url": "\/asset\/image\/map\/map-grass.svg"
}, {
"name": "\/asset\/image\/map\/map-stone.svg",
"url": "\/asset\/image\/map\/map-stone.svg"
}]
let url = "https://SiteName.com";
Object.keys(assets).forEach(key => {
const val = assets[key];
val.name = url + val.name;
val.url = url + val.url;
});
console.log(assets)
You need a nested loop (or forEach) here - one to go over the elements of the assets array, and then, for each object in there, go over all its properties:
var assets = [{
"name": "\/asset\/image\/map\/map-grass.svg",
"url": "\/asset\/image\/map\/map-grass.svg"
}, {
"name": "\/asset\/image\/map\/map-stone.svg",
"url": "\/asset\/image\/map\/map-stone.svg"
}]
assets.forEach(o => {
Object.keys(o).forEach(key => {
o[key] = 'https://SiteName.com' + o[key];
})
});
console.log(assets);
I have this JSON Response from API call
[
{
"id": 20599,
"name": "Deliver",
"options": [
{
"id": 63775,
"name": "Item",
"dataType": "SelectMultiOption",
"required": false,
"options": [
{
"id": 426,
"name": "Towels"
},
{
"id": 427,
"name": "Toothbrush"
},
{
"id": 428,
"name": "Pillow"
}
]
}
]
}
]
I am using this code to get the id of the service "Deliver"
var data = JSON.parse(responseBody);
var loop_count = 0
for (count = 0; count < data.length; count++)
{
if (data[count].name == "Deliver")
{
var job_id = data[count].id;
postman.setEnvironmentVariable("service_id", job_id);
}
}
The questions are:
How can I get value from array "options", I need to get the "id":
63775 and store as "item_id" and the "name":"Item" as "item_name" postman variables.
Then I need to select the "options" nested in record
"Item" and select the option "name": "Toothbrush" and store in postman
variable "svc_optn_optn_name" and it's "id" stored in
"svc_optn_optn_id"
Here I am giving my own suggestion for your problem with few lines of code. I am not sure, how are you going to use these values. I also don't know if the outer options array will always have 1 item or more. I have just tried to satisfy your questions.
Please ask/comment, if you have more doubts or I am wrong.
I have created a function getAllPostmanDataFrom(obj) which takes object as parameter which is the value of data[count], gathers necessary info in other object postmanObj and returns it to the caller.
function getAllPostmanDataFrom(obj) {
const item_id = obj.options[0].id;
const item_name = obj.options[0].name;
const svc_optn_optn_name = obj.options[0].options[1].name;
const svc_optn_optn_id = obj.options[0].options[1].id;
const postmanObj = {item_id, item_name, svc_optn_optn_id, svc_optn_optn_name}; // Return object
return postmanObj;
}
var data = [
{
"id": 20599,
"name": "Deliver",
"options": [
{
"id": 63775,
"name": "Item",
"dataType": "SelectMultiOption",
"required": false,
"options": [
{
"id": 426,
"name": "Towels"
},
{
"id": 427,
"name": "Toothbrush"
},
{
"id": 428,
"name": "Pillow"
}
]
}
]
}
]
var count = 0;
var obj = data[count];
var postmanObj = getAllPostmanDataFrom(obj);
//var {item_id, item_name, svc_optn_optn_id} = postmanObj;
console. log(postmanObj)
/*
console.log(item_id);
console.log(item_name);
console.log(svc_optn_optn_id);
console.log(svc_optn_optn_name);
*/
Finally, you can use values contained in postmanObj as follows:.
postman.setEnvironmentVariable("item_id", postmanObj.item_id);
postman.setEnvironmentVariable("item_name", postmanObj.item_name);
And so on.
This is the solution
var data = JSON.parse(responseBody);
variable named as data
var loop_count = 0
for (count = 0; count < data.length; count++)
{
if (data[count].name == "Deliver")
{
var job_id = data[count].id;
postman.setEnvironmentVariable("service_id", job_id);
var job1_name = data[count].options[0].name;
postman.setEnvironmentVariable("item_name", job1_name);
var job2_id = data[count].options[0].id;
postman.setEnvironmentVariable("item_id", job2_id);
var job3_id = data[count].options[0].options[1].id;
postman.setEnvironmentVariable("svc_optn_optn_id", job3_id);
var job4_name = data[count].options[0].options[1].name;
postman.setEnvironmentVariable("svc_optn_optn_name", job4_name);
}
const data = JSON.parse(responseBody);
data.forEach(item => {
console.log(item.id); // deliver object id.
item.options.forEach(option => {
console.log(`Option Id ${option.id}`); // option id
postman.setEnvironmentVariable("service_id", option.id);
option.options(optionItem => {
if(optionItem.name == 'Toothbrush'){
postman.setEnvironmentVariable("svc_optn_optn_name", optionItem.name);
postman.setEnvironmentVariable("svc_optn_optn_id", optionItem.id);
}
});
});
});
I am creating a Tableau Web Data Connector as described in their Getting Started guide HERE.
I have implemented a similar solution previous with data from a basic associative array, but I am having trouble with my current API call.
I make an API call to an external web service that returns a JSON similar to the one listed below (simplified version)(COMMENT simply added for clarity and not part of original code).
{
"status": true,
"employee": {
"id": 123
},
"company": {
"id": 123
},
"job": {
"id": 123,
"job_workflows": [{
"id": 1,
"name": "Start",
"action_value_entered": "Start"
}, {
"id": 2,
"name": "Date",
"action_value_entered": "2017-09-11"
}, {
"id": 3,
"name": "Crew",
"action_value_entered": "Crew 3"
},
**COMMENT** - 17 other unique fields - omitted for brevity
]
}
}
For my requirements, I need to create a new column for each of my JSON job_workflows to display the data in Tableau. I.e.
Column 1 Header = Start
Column 1 value = Start
Column 2 Header = Date Complete
Column 2 value = 2017-09-11
Etc.
My Tableau JavaScript file looks as below:
(function () {
var myConnector = tableau.makeConnector();
myConnector.init = function(initCallback) {
initCallback();
tableau.submit();
};
myConnector.getSchema = function (schemaCallback) {
var cols = [
{ id : "start", alias : "Start", dataType: tableau.dataTypeEnum.string },
{ id : "date", alias : "Date", dataType: tableau.dataTypeEnum.datetime },
{ id : "crew", alias : "Crew", dataType: tableau.dataTypeEnum.string }
];
var tableInfo = {
id : "myData",
alias : "My Data",
columns : cols
};
schemaCallback([tableInfo]);
};
myConnector.getData = function (table, doneCallback) {
$.getJSON("http://some/api/call/data.php", function(response) {
var resp = response; // Response is JSON as described above
var tableData = [];
// Iterate over the JSON object
for (var i = 0, len = feat.length; i < len; i++) {
// not working
tableData.push({
"start": resp.job.job_workflows[i].action_value_entered,
"date": resp.job.job_workflows[i].action_value_entered,
"crew": resp.job.job_workflows[i].action_value_entered
});
}
table.appendRows(tableData);
doneCallback();
});
};
tableau.registerConnector(myConnector);
})();
How do I iterate over the JSON job_workflow and assign each action_value_entered to be a id in my getSchema() function? My current version simply hangs and no values are returned. NO error or warnings thrown.
I am currently dealing with in issue in writing a recrusive function to order some json data. I have several nested arrays of objects which i need to order into single slides. The structure is similar to the following :
[
{
"title": "a",
"children": [
{
"title": "a-a",
"children": [
{
"title": "a-a-a"
},
{
"title": "a-a-b"
}
]
},
{
"title": "a-b",
"children": [
{
"title": "a-b-a"
},
{
"title": "a-b-b"
}
]
}
]
},
{
"title": "b",
"children": [
{
"title": "b-a",
"children": [
{
"title": "b-a-a"
},
{
"title": "b-a-b"
}
]
},
{
"title": "b-b",
"children": [
{
"title": "b-b-a"
},
{
"title": "b-b-b"
}
]
}
]
}
]
I have written a recursive function :
var catalog = {
init: function() {
var _this = this;
$.getJSON("catalog.json", function(data) {
_this.slides = [];
_this.parseCategories(data.catalog.category,-1,0);
});
},
parseCategories: function(array, depth, prevParent) {
++depth;
if (!this.slides[depth]) this.slides[depth] = [];
if (!this.slides[depth][prevParent]) this.slides[depth][prevParent] = [];
this.slides[depth][prevParent].push(array);
for (var i = 0; i < array.length; i++) {
if (array[i].category) {
this.parseCategories(array[i].category, depth, i);
}
}
}
}
catalog.init();
This outputs :
However instead of retrieving the data for my third slide under format :
a-a-a
a-b-a
a-c-a
I would like to get
a-a-[a,b,c]
I was wondering if that was possible since I'm not very good at handling recursive processes. I hope I was clear and thank you for reading this.
I basically need to keep my original data structure but remove the first depth level for each iteration (slide in a slider that represent increasing depths in my data structure).
I recently wrote a algorithm to recursively handle data like this. Here is a jsfiddle and the main function
console.log('starting');
// data in tree format.
var output = {};
// data in slide format ["a-a-a", "a-a-b", "b-b-a", "b-b-b"]
var outputStrs = [];
parseData(data, output);
console.log(output);
console.log(outputStrs);
function parseData(data, store) {
// go through each element
for (var i = 0; i < data.length; i++) {
var element = data[i];
// used to keep track of where we are in the tree.
var splitElement = element.title.split('-');
var titleStart = splitElement[0];
// console.log(element);
if (_.has(element, 'children') && _.isArray(element.children)) {
// if there is a children, then recursively handle it.
store[titleStart] = {};
parseData(element.children, store[titleStart]);
} else {
// if we are at the end, then add in the data differently.
var titleEnd = splitElement[splitElement.length-1];
store[titleEnd] = titleEnd;
// create the slides
var slide = [];
for (var j = 0; j < splitElement.length; j++) {
if (j !== splitElement.length - 1) {
slide.push(titleStart);
} else {
slide.push(titleEnd);
}
}
slide = slide.join('-');
if (!_.contains(outputStrs, slide)) outputStrs.push(slide);
}
}
}
With this data the output should resemble
a
a
a
b
b
b
a
b
And outputStrs will resemble a-a-[a,b,c]
Hope this helps!!!
I have data that's in this format:
{
"columns": [
{
"values": [
{
"data": [
"Project Name",
"Owner",
"Creation Date",
"Completed Tasks"
]
}
]
}
],
"rows": [
{
"values": [
{
"data": [
"My Project 1",
"Franklin",
"7/1/2015",
"387"
]
}
]
},
{
"values": [
{
"data": [
"My Project 2",
"Beth",
"7/12/2015",
"402"
]
}
]
}
]
}
Is there some super short/easy way I can format it like so:
{
"projects": [
{
"projectName": "My Project 1",
"owner": "Franklin",
"creationDate": "7/1/2015",
"completedTasks": "387"
},
{
"projectName": "My Project 2",
"owner": "Beth",
"creationDate": "7/12/2015",
"completedTasks": "402"
}
]
}
I've already got the column name translation code:
r = s.replace(/\%/g, 'Perc')
.replace(/^[0-9A-Z]/g, function (x) {
return x.toLowerCase();
}).replace(/[\(\)\s]/g, '');
Before I dive into this with a bunch of forEach loops, I was wondering if there was a super quick way to transform this. I'm open to using libraries such as Underscore.
function translate(str) {
return str.replace(/\%/g, 'Perc')
.replace(/^[0-9A-Z]/g, function (x) {
return x.toLowerCase();
})
.replace(/[\(\)\s]/g, '');
}
function newFormat(obj) {
// grab the column names
var colNames = obj.columns[0].values[0].data;
// create a new temporary array
var out = [];
var rows = obj.rows;
// loop over the rows
rows.forEach(function (row) {
var record = row.values[0].data;
// create a new object, loop over the existing array elements
// and add them to the object using the column names as keys
var newRec = {};
for (var i = 0, l = record.length; i < l; i++) {
newRec[translate(colNames[i])] = record[i];
}
// push the new object to the array
out.push(newRec);
});
// return the final object
return { projects: out };
}
DEMO
There is no easy way, and this is really not that complex of an operation, even using for loops. I don't know why you would want to use regex to do this.
I would start with reading out the column values into a numerically indexed array.
So something like:
var sourceData = JSON.parse(yourJSONstring);
var columns = sourceData.columns[0].values[0].data;
Now you have a convenient way to start building your desired object. You can use the columns array created above to provide property key labels in your final object.
var sourceRows = sourceData.rows;
var finalData = {
"projects": []
};
// iterate through rows and write to object
for (i = 0; i < sourceRows.length; i++) {
var sourceRow = sourceRows[i].values.data;
// load data from row in finalData object
for (j = 0; j < sourceRow.length; j++) {
finalData.projects[i][columns[j]] = sourceRow[j];
}
}
That should do the trick for you.