So I haven't been able to find an example. I'm loading this huge dataset from an external file with D3, and I'm making some graph with some part of the data. When a slider is moved, I wish make the same graph with some other part of the data.
I used d3.json() for loading the data. The trouble that I'm facing is: since the d3.json() is asynchronous, it loads the dataset for once, and then I can't access it from other functions. I can't have another update function that makes a new graph since I simply can't access the data anymore.
Other people's suggestions have been "only using the data in the d3.json() function: How to store a JSON object loaded from a file?
d3.json("temp.json", function(data){
//use data here
})
// do not use data anymore
I tried to use a variable to store the data (the second answer from the same question link above):
var DATASET; // global
d3.json("file.json", function(data) {
DATASET = data;
//any other functions that depend on data
});
d3.selectAll().data(DATASET).......
And I couldn't get it to work at all, since (I assume this is the reason) the data hasn't been loaded to DATASET when the last line of d3.data calls DATASET
Is there any way I could update external data in d3? Should I just load the data again in my update function? Will it affect performance since the dataset is huge?
Thank you so much for answering!
If you want to read all the data once, you should store it in your global DATASET variable, just as you show in your code fragment. But instead of placing the update logic in your main script flow, put it inside an update(...) function, and call that from the d3.json callback, too... something like this (untested):
var DATASET; // global
d3.json("file.json", function(data) {
// data is loaded, save the full set
DATASET = data;
// filter the initial subset
var subdata = data.filter(...);
// now update the graph
updateGraph(subdata);
});
function updateGraph(data) {
// when invoked without any data, use the full dataset
var newdata = (data == null ? DATASET : data);
// data rendering logic starts here...
d3.selectAll().data(newdata);
...
}
This saves the full dataset in a global variable, draws the inital graph with a filtered subset of the data, and also allows you to change which data is shown by using a different filter before calling the function again.
Related
I'm building this website: http://collections.design
The way it works is by reading all tools data from a JSON, using jQuery (I don't know much javascript). Then, you can click on an item and a side panels opens with further information. But there's a lot of repeated code, so I'm trying to optimise it a bit.
First I parse the JSON:
// The data source
var data_source = "../data/tools/tools.json";
// Parsing the JSON
$.getJSON(data_source, function(data) {
$.each(data, function(key,val) {
// And I'm storing all of its values in variables, to make them easier to read:
var name = val.availability.name;
var linux = val.os.linux;
// Then I'm using all that to render each item on screen
…
});
});
Each of the items has a button that calls another function to create and open the side panel. The side panel reuses that item's data from the JSON. This function to create the side panel is using the name variable as parameter, but then inside is parsing the JSON again to get the rest of the values it needs.
My question is:
How can I "encapsulate" all variables when I do the JSON parsing, then pass it as a parameter to the other function; and finally, individually read each of those values in the other function?
I tried working with arrays. But didn't manage it to work, also keeping in mind that I'm trying to simplify things, not repeat myself, and keep short names…
Maybe I'm asking too much, but any pointers or links to doc will be appreciated.
I see two ways of doing this.
1) Save the JSON data outside the scope so you can reuse it and pass the index of the data you want.
Something like this
// The data source
var data_source = "../data/tools/tools.json";
var all_data;
// Parsing the JSON
$.getJSON(data_source, function(data) {
all_data = data;
$.each(data, function(key,val) {
$('.button').on('click', function() { callToOtherFunction(key) })
});
});
function callToOtherFunction(key) {
console.log(all_data[key]);
}
2) As Sam Axe said, pass the data directly to the function
// The data source
var data_source = "../data/tools/tools.json";
// Parsing the JSON
$.getJSON(data_source, function(data) {
$.each(data, function(key,val) {
$('.button').on('click', function() { callToOtherFunction(key) })
});
});
function callToOtherFunction(val) {
console.log(val);
}
Here's a working fiddle.
The data is already "encapsulated" in the data object. Pass that object to the function that you want to use the data in.
You could always construct a new object - but what's the point - it's already in the data object.
I have an array that is being filled on page load by D3. I want to access that same array at another time but I still need to confirm that it has been loaded.
I don't know the propper way to do this so I just guessed a few time and I kept getting "not a function" errors
First Code (populate my svg, snapshot of current values)
d3.tsv("file.txt").then( function(data) { Loaded_Data[1]=data; document.getElementById("valve1").innerHTML = data[data.length-1]['OnOff']; //etc
});
much later, but still in a relevant timescale of async, I want to do some d3 graphing with this data, but I want to make sure that there is data in my array.
Later Code (populate graphs, weeks of data)
Loaded_Data[1].addEventListener('load', () => {console.log("success"); //d3.graphing; });
I basically want a:
while (array[1] is undefined){ Listen; }
when done => graph;
Or would it just be easier to load the entire set of documents again in another d3.tsv().then()? it seems like a waste of resources to reload the entire data. What makes this difficult is the number of sources I have to load in, and consolidating my data into one array will be (hopefully) more convenient.
just graph in the then call after you read the data:
d3.tsv("file.txt")
.then(data => graph(data)); //data is available to be used at this point
also, if I'm reading this correctly, you are listening for the array to be populated with the load event, but that is only fired when the whole page is loaded and isn't relevant to the populating of a data structure. If you need the data stored somewhere in addition to graphing, you could modify the above to:
d3.tsv("file.txt")
.then(data => {
graph(data);
myArray = data;
});
I'm looking for appropriate way to refresh bar chart which is constantly getting data from API.
$http({
method: 'GET',
url: '/data' //getting data from API
}).then(function ( json ) {
//chart
}
There is a similar situation on the plunker, but I'm affraid of memory leak with this method and chart is always blinking.
Do anyone has a suggestion what to use instead of interval? Or use it in some other way?
Thanks.
That Plunker is great, but the problem is that it calls c3.generate continuously, which is why it's leaking memory and flashing (You're essentially creating a new C3 chart instance every time you run generate; all it does is called new Chart(config);)
What you need to do is instantiate C3 in the controller and then call c3.load() in your $http call.
Updated Plunker
Key bit:
$interval(function() {
$http.get('chartData.json')
.then(function(json) {
$scope.countries = formatData(json.data); // format received data
$scope.chart.load({json: $scope.countries, keys: { value: ['Croatia', 'Belgium', 'Argentina'] }});
});
}, 1000);
The $interval+$http combo should work okay, provided that the JSON file you're loading is just on a CDN somewhere (if it's being generated on a per-request basis by a REST API, you'll either get throttled or rinse your server). You may want to take a look at $http's caching docs.
I am building a Ruby on Rails application where I am performing an Ajax request to retrieve some data, perform calculations on it, and place it on the page. The page is going to be performing these calculations every 30 seconds or so on new data from the database, so this means I need to keep the original data from the Ajax request intact. When I'm retrieving the original data, I'm placing it in a div using jQuery like so:
$('#info').data('original', data);
and then taking that data object and running said calculations on it. After the calculations are complete, I place it in a separate data attribute in the same div like so:
$('#info').data('modified', data);
The data in the 'modified' attribute goes to the page, and when the time has come, it pulls the new data from the database, the original data from the div, gets passed to a method for calculating, and then is placed back in the 'modified' attribute for being displayed on the page:
function calculate(data) {
// doing some stuff here
$('#info').data('modified', data);
}
var data = $('#info').data('original');
calculate(data);
All of this is working just fine, however when the very first calculation is being ran, it seems that the 'original' attribute in the div is being overwritten with the data that has been calculated, even though I'm not explicitly telling it to do so. Does anyone have any insight on why this could be happening?
Currently I'm loading data asynchronously via data.js as provided by the Grid app template. The problem exists where groupedItems.js (the "Hub" page) calls _initializeLayout in the ready handler before the Data in the global WinJS namespace is set due to the asynchronous nature of the StorageFile class.
In data.js:
fileNames.forEach(function (val, index, arr) {
var uri = new Windows.Foundation.Uri('ms-appx:///data/' + val + '.geojson');
Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) {
Windows.Storage.FileIO.readTextAsync(file).then(function (contents) {
// ... read, parse, and organize the data ...
// Put the data into the global namespace
WinJS.Namespace.define("Data", {
items: groupedItems,
groups: groupedItems.groups,
getItemReference: getItemReference,
getItemsFromGroup: getItemsFromGroup,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference
});
});
});
}
In groupedItems.js:
// ...
// This function updates the ListView with new layouts
_initializeLayout: function (listView, viewState) {
/// <param name="listView" value="WinJS.UI.ListView.prototype" />
if (viewState === appViewState.snapped) {
listView.itemDataSource = Data.groups.dataSource;
listView.groupDataSource = null;
listView.layout = new ui.ListLayout();
} else {
listView.itemDataSource = Data.items.dataSource;
listView.groupDataSource = Data.groups.dataSource;
listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
}
},
// ....
Seeing as I cannot move this code out of this file into the done() function of the Promise in data.js, how do I make the application wait until Data is initialized in the WinJS namespace prior to initializing the layout?
You have two asynchronous operations in progress (loading of the data and loading of the page) and one action (initializing the grid) that needs to happen only after both asynchronous operations are complete (page is loaded, data is available). There are a lot of approaches to solve this depending upon what architectural approach you want to take.
The brute force method is that you create a new function that checks to see if both the document is ready and the data is loaded and, if so, it calls _initializeLayout(). You then call that function in both places (where the doc is loaded and when the data is available) and it will execute only when both conditions are satisfied. It appears that you can tell if the data is loaded by checking for the existence of the global Data item and the its relevant properties.
There are more involved solutions that are architecturally a little cleaner. For example, in your doc ready handler, you can check to see if the data is available yet. If it is, you just initialize the layout. If, not you install a notification so that when the data is available, your callback will get called and you can then initialize the layout. If the data loading code doesn't currently have a notification scheme, then you create one that can be used by any client who wants to be called when the data has been loaded. This has the advantage over the first method in that the data loading code doesn't have to know anything about the grid. The grid does have to know about the data - which makes sense because the grid requires the data.
There are surely ways to use the promise/done system to do this too though I'm not personally familiar enough with it to suggest a good way to do it using that.