I'm new at this. Im trying to make a simple interactive chart using jaavascript. The idea is that when I change the values in the selector, the chart change the data is using.
However, I´m having troubles with the data switching. The data come´s frome different urls. Those urls are in a CSV file. My code extracts the urls from th CSV file and makes an array. Then it proceed to call the CSV file from the adequate element of the array. Simplifying the code:
functionThatReturnsArray(){
d3.csv("http//URL.COM", function(){
SomeMoreCode;
return ArrayOfStrings
};)
}
A = functionThatReturnsArray();
MoreCode;
//For example, the first value from A is selected
d3.csv(A[0], function(error, data) {
MoreCode;
})
The problem is that it seems that this isn´t a valid input to d3.csv, because it doesn´t work. I don´t know if i´m missing something or it just can´t be done this way.
I searched and it might be from the fact that d3.csv is an asynchronous method, but I´m not sure if that´s the problem.
Any suggestion will be apreciated
Your first CSV data is loading asynchronously, so this function will not return anything
functionThatReturnsArray(){
d3.csv("http//URL.COM", function(){
SomeMoreCode;
return ArrayOfStrings
};)
}
You can put following code in function
function processArray(A){
MoreCode;
//For example, the first value from A is selected
d3.csv(A[0], function(error, data) {
MoreCode;
})
}
and invoke this function in first csv loading callback
functionThatReturnsArray(){
d3.csv("http//URL.COM", function(){
SomeMoreCode;
processArray(ArrayOfStrings) //add this
};)
}
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;
});
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.
I'm trying to get data used in my table to be used in a div when I click on the table. The thing is, there are multiple tables in my script according to the data of my JSON. So my JSON consists of object that consists of object. For example:
My table(s) are rendered like this:
data.forEach(function(somedata){
return '<table><tr><td>'+somedata.something+'</td></tr></table>';
});
Now I've tried to get the onclick to work in this case but I cant seem to figure out how. I'd like to not use specific ID's rendered in the foreach like:
var i=0;
data.forEach(function(somedata){
i++;
return '<table id="'.id.'"><tr><td>'+somedata.something+'</td></tr></table>';
});
the variable somedata consists of an object so I cant just make an onclick in the html code of the table either and send the data in it.
So somedata would look something like this but json encoded:
somedata{
[0]=>array(
'something'=>'test',
'theobject'=>array(...)
),
[1]=>array(etc...)
}
Now what I want is to get the data from theobject in a seperate div as soon as I click on the table that belongs to the right somedata.
I've been thinking of making a jquery on click for this but then I would need specific ID's in the table(if that's the only possible solution then I'd take it). Cant I do something with this or something? Or send the data at the same time it's being rendered cause in my code I can at the moment of course reach to somedata.theobject
I think I'm thinking a bit too difficult about this. Am I?
You can pass this in onclick function like
return '<table onclick=makeObject(this)><tr><td>'+somedata.something+'</td></tr></table>';
And then use the function to get the data
function makeObject(that){
var tbl = $(that).find('tr').map(function() {
return $(this).find('td').map(function() {
return $(this).html();
}).get();
}).get();
}
There are a few ways to go about this. Rather than using the forEach function we can use the jQuery.map function, since you've indicated that you're open to using jQuery :-)
var $els = $.map(data, function(somedata, i){
var $el = $('<table><tr><td>'+somedata.something+'</td></tr></table>')
.click(function(e) {
populateDivWithData(somedata.theobject);
});
return $el;
});
The call to .click inside each will create a separate click handler for each item in data; each click handler then has access to the relevant theobject value.
EDIT: Thanks #Loko for the reminder about the .forEach built-in
I am currently trying to use the d3 framework for a university visualisation approach.
For testing purpose I want to read a csv-file and parse the rows to objects inside an array.
My csv looks like:
ID, Referred To, TimeStamp, Votes, Comment
So I want to read it with the following lines:
d3.csv("test_comments.csv", function(data) {
commentlist = data.map(function(d) {
return[+d["ID"],
+d["Referred To"],
+d["TimeStamp"],
+d["Votes"],
+d["Comment"]
]
});
});
But if I want to readout the values afterwards I am just getting "undefined"
I also tried the way mbostock described in this thread:
csv to array in d3.js
but working with a global variable is not working either.
var commentlist;
d3.csv("test_comments.csv", function(data) {
commentlist = data.map(function(d) {
return[+d["ID"],
+d["Referred To"],
+d["TimeStamp"],
+d["Votes"],
+d["Comment"]
]
});
});
console.log(commentlist);
Am I understanding something wrong?
Maybe you have a solution for me.
var commentlist=[];
d3.csv("test_comments.csv", function(data) {
commentlist=data;
});
console.log(commentlist);
What I know is, In the call back data object will contain array of JSON objects of csv file's all rows of data, that each row data is pushed as a JSON format into the data array.
As below
[{"ID": valueFromtheRow, "Referred To": value, "TimeStamp": value, "Votes":value, "Comment":value}]
The call back function is called by passing the array of JSONs.
So data object will look like
data=[{"ID": valueFromtheRow, "Referred To": value, "TimeStamp": value, "Votes":value, "Comment":value}];
Hope you understood...If not ask me.
I think the reason you got { console.log(commentlist) } undefined is that inside d3.csv the callback function is parsed and called last by browser, and { console.log(commentlist) } is called earlier even though it appears at the bottom of your code. So the moment when { console.log(commentlist) } is called, { commentlist } is actually undefined (only declared).
That being said, just try putting { console.log(commentlist) } inside the callback and it should do the job.