I am relatively new to the JSON notation, and have run into an issue when attempting to reformat. The current format contained in the database needs to be modified to a new format for importation into a project timeline graph.
Here is the current JSON format:
[
{
"name":"5-HP-N/A-N/A-F8",
"node":{
"name":"5",
"id":14
},
"timeline":{
"epc":null,
"m1":null,
"m2":null,
"m3":1554087600000,
"m4":1593572400000,
"m5":1625108400000,
"m6":1641006000000,
"m7":1656644400000
},
"fab":{
"name":"F8",
"id":1
}
},
However, in order to display in the graph, I need the following format:
{
'start': new Date(value from epc, or first non-null milestone),
'end': new Date(value from m1 or first non-null milestone), // end is optional
'content': 'label from start Date milestone'
'group' : ' value from name field above 5-HP'
'classname' : ' value from start Date milestone'
});
I am trying to write a function in order to accomplish this. Only epc, m1, or m2 may have the value of null, but the condition must be checked for to determine if an event range should be created and where it should end. What would be the best way to reformat this json data (preferrably from an external json sheet)?
Edit: Thanks for all the help I see how this is working now! I believe I didn't explain very well the first time though, I actually need multiple class items per "group".
The end result is that these will display inline on a timeline graph 'group' line, and so I am trying to figure out how to create multiple new objects per array element shown above.
So technically, the first one will have start date = m3, and end date = m4. Then, the next object would have the same group as the first (5-HP...), the start date = m4, end date = m5...etc. This would continue until m7 (always an end date but never a start date) is reached.
This is why the loop is not so simple, as many conditions to check.
see a working fiddle here: http://jsfiddle.net/K37Fa/
your input-data seems to be an array, so i build a loop around that. if not just see this fiddle where the input data is a simple object: http://jsfiddle.net/K37Fa/1/
var i
, result = [],
, current
, propCounter
, content = [ { "name":"5-HP-N/A-N/A-F8", "node":{ "name":"5", "id":14 }, "timeline":{ "epc":null, "m1":null, "m2":null, "m3":1554087600000, "m4":1593572400000, "m5":1625108400000, "m6":1641006000000, "m7":1656644400000 }, "fab":{ "name":"F8", "id":1 } }],
// get the milestone in a function
getMileStone = function(obj) {
propCounter = 1;
for(propCounter = 1; propCounter <= 7; propCounter++) {
// if m1, m2 and so on exists, return that value
if(obj.timeline["m" + propCounter]) {
return {key: "m" + propCounter, value: obj.timeline["m" + propCounter]};
}
}
};
// loop over content array (seems like you have an array of objects)
for(i=0;i< content.length;i++) {
current = content[i];
firstMileStone = getMileStone(current);
result.push({
'start': new Date(current.epc || firstMileStone.value),
'end': new Date(current.m1 || firstMileStone.value),
'content': firstMileStone.key,
'group' : current.name,
'classname' : firstMileStone.value
});
}
EDIT:
getMileStone is just a helper-function, so you could just call it with whatever you want. e.g. current[i+1]:
secondMileStone = getMileStone(current[i + 1])
you should just check, if you are not already at the last element of your array. if so current[i+1] is undefined, and the helperfunction should return undefined.
you could then use as fallback the firstMileStone:
secondMileStone = getMileStone(current[i + 1]) || firstMileStone;
see the updated fiddle (including check in the getMileStone-Helperfunction): http://jsfiddle.net/K37Fa/6/
Related
How could I rewrite this code to object javascript. Since Array usage is prohibed, I can only use objects here. Insted of pushing values to array, I would like to push this values into objects.
var container = [];
document.addEventListener("submit", function(e){
e.preventDefault();
});
window.addEventListener("load",function(){
var submit = document.getElementsByClassName("btn-primary");
submit[0].addEventListener("click",add,false);
document.getElementById("pobrisi").addEventListener("click",deleteAll,false);
var dateElement = document.getElementById('datum');
dateElement.valueAsDate = new Date();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth()+1;
var yyyy = today.getFullYear();
if(dd<10){
dd='0'+dd
}
if(mm<10){
mm='0'+mm
}
today = yyyy+'-'+mm+'-'+dd;
dateElement.setAttribute("min",today);
});
function add() {
var title = document.getElementById("title").value;
var type = document.getElementById("type").value;
var datum = document.getElementById("datum").value.split("-");
datum = datum[2]+". "+datum[1]+". "+datum[0];
var data = new Book(title,type,datum);
container.push(data.add());
display();
}
function display(data) {
var destination = document.getElementById("list");
var html = "";
for(var i =0;i <container.length; i++) {
html +="<li>"+container[i]+"</li>";
}
destination.innerHTML = html;
}
function deleteAll(){
container=[];
document.getElementById("list").innerHTML="";
}
Wondering if is possible to write this code whitout any array usage.
initial remarks
The problem here, in my estimation, is that you haven't learned the fundamentals of data abstraction yet. If you don't know how to implement an array, you probably shouldn't be depending on one quite yet. Objects and Arrays are so widespread because they're so commonly useful. However, if you don't know what a specific data type is affording you (ie, what convenience does it provide?), then it's probable you will be misusing the type
If you take the code here but techniques like this weren't covered in your class, it will be obvious that you received help from an outside source. Assuming the teacher has a curriculum organized in a sane fashion, you should be able to solve problems based on the material you've already covered.
Based on your code, it's evident you really have tried much, but why do you think that people here will come up with an answer that your teacher will accept? How are we supposed to know what you can use?
a fun exercise nonetheless
OK, so (we think) we need an Array, but let's pretend Arrays don't exist. If we could get this code working below, we might not exactly have an Array, but we'd have something that works like an array.
Most importantly, if we could get this code working below, we'd know what it takes to make a data type that can hold a dynamic number of values. Only then can we begin to truly appreciate what Array is doing for us.
// make a list
let l = list(1) // (1)
// push an item on the end
l = push(l, 2) // (1 2)
// push another item on the end
l = push(l, 3) // (1 2 3)
// display each item of the list
listeach(l, function (x) {
console.log(x)
})
// should output
// 1
// 2
// 3
runnable demo
All we have to do is make that bit of code (above) work without using any arrays. I'll restrict myself even further and only use functions, if/else, and equality test ===. I see these things in your code, so I'm assuming it's OK for me to use them too.
But am I supposed to believe your teacher would let you write code like this? It works, of course, but I don't think it brings you any closer to your answer
var empty = function () {}
function isEmpty (x) {
return x === empty
}
function pair (x,y) {
return function (p) {
return p(x,y)
}
}
function head (p) {
return p(function (x,y) {
return x
})
}
function tail (p) {
return p(function (x,y) {
return y
})
}
function push (l, x) {
if (isEmpty(l))
return list(x)
else
return pair(head(l), push(tail(l), x))
}
function list (x) {
return pair(x, empty)
}
function listeach (l, f) {
if (isEmpty(l))
return null
else
(f(head(l)), listeach(tail(l), f))
}
// make a list
let l = list(1) // (1)
// push an item on the end
l = push(l, 2) // (1 2)
// push another item on the end
l = push(l, 3) // (1 2 3)
// display each item of the list
listeach(l, function (x) {
console.log(x)
})
closing remarks
It appears as tho you can use an Object in lieu of an Array. The accepted answer (at this time) shows a very narrow understanding of how an object could be used to solve your problem. After this contrived demonstration, are you confident that you are using Objects properly and effectively?
Do you know how to implement an object? Could you fulfill this contract (below)? What I mean by that, is could you write the functions object, set, and get such that the following expressions evaluated to their expected result?
In case it's not obvious, you're not allowed to use Object to make it happen. The whole point of the exercise is to make a new data type that you don't already have access to
m = object() // m
set(m, key, x) // m
get(m, key) // x
set(m, key2, y) // m
get(m, key2) // y
set(m, key3, set(object(), key4, z)) // m
get(get(m, key3), key4) // z
I'll leave this as an exercise for you and I strongly encourage you to do it. I think you will learn a lot in the process and develop a deep understanding and appreciation for what higher-level data types like Array or Object give to you
Since this is a homework I feel like I shouldn't solve it for you, but rather help you in the right direction.
Like Slasher mentioned you can use objects
With JavaScript object one book would look something like
const book = {
title: 'my awesome title',
type: 'novel'
};
book is the object
title is a property with a value 'my awesome title'
type is a property with a value 'novel'
But objects can also have other objects as values. Something like
const BookShelf= {
Book1: {
Title: 'my awesome title',
Type: 'novel'
},
Book2: {
Title: 'my horrible title',
Type: 'sci-fi'
}
};
You can reference the books in the bookshelf in two ways
const book1 = BookShelf.Book1 // Returns the book1 object
const title1 = Book1.Title; // Get the title
const sametitle = BookShelf.Book1.Title // Returns title for book1, same as above.
You can also use brackets:
const book1 = BookShelf['Book1'];
const title1 = BookShelf['Book1']['Title];
You can even make new properties on a object like this:
const Book3 = {
Title: 'running out of ideas'
Type: 'memoir'
};
BookShelf['Book3'] = Book3;
Now the BookShelf has a Book3 property. So your BookShelf object looks like
const BookShelf= {
Book1: {
Title: 'my awesome title',
Type: 'novel'
},
Book2: {
Title: 'my horrible title',
Type: 'sci-fi'
},
Book3 = {
Title: 'running out of ideas'
Type: 'memoir'
};
};
That should get you started :)
JavaScript Objects is a good way to go
1- define a new object:
var myVar = {};
or
var myVar = new Object();
2- usage
// insert a new value, it doesn't matter if the value is a string or int or even another object
// set a new value
myVar.myFirstValue="this is my first value";
// get existing value and do what ever you want with it
var value = myVar.myFirstValue
I'm working on some realtime graphs built with NVD3.js. I currently refresh each chart with the following:
function reDraw(c) {
d3.json(c.qs, function(data) {
d3.select(c.svg)
.datum(data)
.transition().duration(500)
.call(c.chart);
});
}
c looks like:
function Chart(svg, qs, chart) {
this.qs = qs;
this.svg = svg;
this.ylabel;
this.chart;
}
This works fairly well, but with each refresh I am fetching the whole time series again. It would be more efficient to only grab recent elements and update each graph. There are examples of doing this by appending elements (This answer NVD3 line chart with realtime data and this tutorial for example) , but this isn't ideal for me since some recent elements might be updated that are not the most recent element.
So what I'm looking to do is grab say the most recent minute (by setting query string (.qs) to only get the most recent minute, then take that result and do the following:
Overwrite any existing elements that have the same x value for each series with the most recent data
Append and elements when there are new x values from the update in each series
Expire elements past a certain age
Update the NVD3.js script with the new data. Maybe still use datum with the new merged object?
Can anyone suggest an elegant way to perform the above Merge operation? The existing data object looks like the following:
> d3.select(perf.svg).data()[0]
[
Object
key: "TrAvg"
values: Array[181]
__proto__: Object
,
Object
key: "RedisDurationMsAvg"
values: Array[181]
__proto__: Object
,
Object
key: "SqlDurationMsAvg"
values: Array[181]
__proto__: Object
]
> d3.select(perf.svg).data()[0][0]['values'][0]
Object {x: 1373979220000, y: 22, series: 0}
> d3.select(perf.svg).data()[0][1]['values'][0]
Object {x: 1373979650000, y: 2, series: 1}
The object returned would look something like the following (Except will only be maybe 6 elements or so for each object):
> d3.json(perf.qs, function(data) { foo = data })
Object {header: function, mimeType: function, response: function, get: function, post: function…}
> foo
[
Object
,
Object
,
Object
]
> foo[0]
Object {key: "TrAvg", values: Array[181]}
> foo[0]['values'][0]
Object {x: 1373980220000, y: 49}
In this newer object the series value is missing - maybe that needs to get added or perhaps D3 can do it?
For the time being I used linq.js to perform this operation, and then use .datum() to bind a new dataset each time. The solution isn't very elegant but it does seem to function:
function reDraw2(c, partial) {
if (partial) {
qs = c.qs.replace(/minago=[0-9]+/, "minago=1")
} else {
qs = c.qs
}
d3.json(qs, function(new_data) {
chart = d3.select(c.svg)
if (c.ctype == "ts" && partial) {
thirtyminago = new Date().getTime() - (60*30*1000);
old_data = chart.data()[0]
var union = new Array();
for (var i = 0; i < old_data.length; i++) {
var existing = Enumerable.From(old_data[i]['values']);
var update = Enumerable.From(new_data[i]['values']);
oldfoo = existing
newfoo = update
var temp = {}
temp['key'] = old_data[i]['key']
temp['values'] = update.Union(existing, "$.x").Where("$.x >" + thirtyminago).OrderBy("$.x").Select().ToArray();
union[i] = temp
}
chart.datum(union)
} else {
chart.datum(new_data)
}
chart.call(c.chart)
});
Sorry if this is a really n00b question, but I am just getting into rails and can't figure this one out.
I am currently working with Analytics from a site. And want to put the data into a chart. However when I get the data back the stuff I want isn't in order. At the moment the data returned is:
{"legend_size":1,"data":{"series":["2013-06-20","2013-06-21","2013-06-22","2013-06-23","2013-06-24","2013-06-25","2013-06-26"],"values":{"undefined":{"2013-06-26":41,"2013-06-24":2,"2013-06-25":55,"2013-06-22":0,"2013-06-23":1,"2013-06-20":0,"2013-06-21":0}}}}
And what I need is the ["data"]["values"]["undefined"] bit to be ordered by date.
I know it's a simple question. But I have looked everywhere and can't figure it out.
Ok, as I said in the comment you can't really sort and object as it has an arbitrary order, so you must use an array.
My solution would be:
var objectFromAnalytics = {"2013-06-26":41,"2013-06-24":2,"2013-06-25":55,"2013-06-22":0,"2013-06-23":1,"2013-06-20":0,"2013-06-21":0},
toSort = [],
dateParts;
for (var date in objectFromAnalytics) {
dateParts = date.split("-");
toSort.push({ date: new Date(dateParts[0], (dateParts[1] - 1), dateParts[2]), value: objectFromAnalytics[date] });
}
var sorted = toSort.sort(function(a,b){
return a.date < b.date ? -1 : a.date > b.date ? 1 : 0; //you can change this to sort by value if you want
});
//sorted == [{ date: dateObject, value: value from analytics} , { ... }]
You can probably get away doing this in a few less lines, but I think is understandable this way :)
When i have the following global variable on my application start :
events = [];
Then if i go fetch something with ajax with the following simplified code snippet :
events[0] = [];
setTimeout(function fetchEventsThisWeek(){
$.ajax({
url: '/event/getEventsBetweenDates',
type: 'POST',
data : { from_date : currentweek.from_date.toString("yyyy-MM-d") , to_date : currentweek.to_date.toString("yyyy-MM-d"), limit : limit, offset : offset },
success: function(data) {
jQuery.each(data, function(index){
events[0].push(data[index]['attributes']);
});
offset = offset + limit;
entry_count = entry_count + data.length;
if(data.length < limit) { // We reached the end of the table so we don't need to call the function again
renderEvents(current, offset - limit, entry_count);
//Make sure the current next week button gets enabled because we are done with the results
} else {
renderEvents(current, offset - limit, offset);
setTimeout(fetchEventsThisWeek, 150); // There are more results, continue
}
}
})
}, 150);
This recursive function just fetches all events between two dates and keeps calling itself until there is no record in the db left.
My problem is:
With the variable:
events[0] = [];
I want to specify the index of the array as my week entry. So if i look for a specific week, i can get all the entries that already have been fetched from my array by the array index.
My problem is, when i want to fetch more weeks, so for example:
events[1] = [];// Index 1 would represent the next week
The array just expands in size and all gets appended to the end, so i have one big array and not a multidimensional one. Why is this? And how can i achieve this behaviour?
Edit:
Let me expand on my question.
I need several arrays of json objects in the events variable.
So..
events[0] = [ /*contains array of json objects */];
events[1] = [ /*contains array of json objects */];
events[2] = [ /*contains array of json objects */];
Each array index represent 1 week. So index 0 is the current week, index 1 is week 1, index 2 is week 2 and so forth. I even want to do the following but i don't know if this is even possible:
events[-1] = [ /*contains array of json objects */];
Where the index -1 would be 1 week in the past. Could anybody let me know if this is possible?
You're looking for Array.unshift:
events.unshift([]);
Documentation
I am using Ext.data.Store to call a PHP script which returns a JSON response with some metadata about fields that will be used in a query (unique name, table, field, and user-friendly title). I then loop through each of the Ext.data.Record objects, placing the data I need into an array (this_column), push that array onto the end of another array (columns), and eventually pass this to an Ext.grid.ColumnModel object.
The problem I am having is - no matter which query I am testing against (I have a number of them, varying in size and complexity), the columns array always works as expected up to columns[15]. At columns[16], all indexes from that point and previous are filled with the value of columns[15]. This behavior continues until the loop reaches the end of the Ext.data.Store object, when the entire arrays consists of the same value.
Here's some code:
columns = [];
this_column = [];
var MetaData = Ext.data.Record.create([
{name: 'id'},
{name: 'table'},
{name: 'field'},
{name: 'title'}
]);
// Query the server for metadata for the query we're about to run
metaDataStore = new Ext.data.Store({
autoLoad: true,
reader: new Ext.data.JsonReader({
totalProperty: 'results',
root: 'fields',
id: 'id'
}, MetaData),
proxy: new Ext.data.HttpProxy({
url: 'index.php/' + type + '/' + slug
}),
listeners: {
'load': function () {
metaDataStore.each(function(r) {
this_column['id'] = r.data['id'];
this_column['header'] = r.data['title'];
this_column['sortable'] = true;
this_column['dataIndex'] = r.data['table'] + '.' + r.data['field'];
// This display valid information, through the entire process
console.info(this_column['id'] + ' : ' + this_column['header'] + ' : ' + this_column['sortable'] + ' : ' + this_column['dataIndex']);
columns.push(this_column);
});
// This goes nuts at columns[15]
console.info(columns);
gridColModel = new Ext.grid.ColumnModel({
columns: columns
});
Okay, since the this_column array was responding correctly on each run, but the columns array was not, I figure it must be an issue with the push().
After a bit more toying with it, I moved altered the code to reset the this_column array on each iteration of the loop - seems to have fixed the issue...
metaDataStore.each(function(r) {
this_column = [];
this_column['id'] = r.data['id'];
this_column['header'] = r.data['title'];
this_column['sortable'] = true;
this_column['dataIndex'] = r.data['table'] + '.' + r.data['field'];
columns.push(this_column);
});
I see you've already found something that works, but just to offer some advice for the future: this is much easier if you use a json store and column model directly instead of performing intermediate steps by hand.
I'm not sure if you're using a grid or dataview, but the concept is pretty much the same for both of them. If you have to do a bit of data customization, but instead of doing it by hand here you can actually just do it in a prepareData callback function.
Because you first use the variable this_column in the global context (at the top of your example) it becomes a global variable. You should instead instantiate each column definition as an object literal (split into multiple lines for read-ability).
metaDataStore.each(function(r) {
columns.push({
id: r.data['id'],
header: r.data['title'],
sortable: true,
dataIndex: r.data['table'] + '.' + r.data['field']
});
});
Or if you really wanted to use a variable, you could just do this to make sure it's a local variable
metaDataStore.each(function(r) {
var this_column = {};
...