Basic Timesheet table - Using ajax requests and jquery - javascript

Im basically building a very basic timesheet page. I am having problems getting the output onto a basic HTML table. Problem is, I got 2 list of data, one being the parent and the other the child. Child contains the ID of the parent and im having issues finding an easy way to merge the parent/child data together and output them to look something like this:
ID / Description / Start / End / Hours / Night or Day?
------------------------------------------------------------------------------
1 Description1 2016-05-31 10:00 2016-05-31 12:00 2 Day
2016-06-02 14:00 2016-06-02 15:00 1 Day
2016-06-04 19:00 2016-06-04 20:00 1 Night
------------------------------------------------------------------------------
2 Description2 2016-04-06 14:00 2016-04-02 15:00 1 Day
2016-06-02 18:00 2016-06-02 22:00 4 Night
------------------------------------------------------------------------------
3 Description3 2016-05-30 23:00 2016-05-31 00:00 1 Night
------------------------------------------------------------------------------
Etc ...
All these fields are entered manually, and I just want to simply output them on a table to review the entered data.
I got started on an example page to show my progress so far:
https://jsfiddle.net/tj6bcjos/2/
Here is my code so far:
data_array = {};
$.ajax({
url:"Ajax/Path1",
dataType: "json",
cache: false,
success: function (data) {
data.d.results.forEach(function (data) {
data_array[data.ID] = {};
data_array[data.ID][data.description] = {};
});//foreach end
console.log(data_array);
}//sucess end
});
$.ajax({
url:"Ajax/Path2",
dataType: "json",
cache: false,
success: function (data) {
data.d.results.forEach(function (data) {
if (data_array[data.ID] === data.ID_of_parent) { data_array[data.data.ID_of_parent] = {}; }
});//foreach end
console.log(data_array);
}
});
Its with the second Ajax where I cant find a way to look trough the existing array, match the child's ID_of_parent to the parent's ID and merge the data.
Then I hoping to do something like
dara_array.forEach(function (data) {
$("#my_table tbody").append("<tr>"+
"<td>"+data.ID+"</td>"+
"<td>"+data.Description+"</td>"+
"<td>"+data.Start+"</td>"+
"<td>"+data.End+"</td>"+
"<td>"+data.Hours+"</td>"+
"<td>"+data.Night_or_Day+"</td>"+
"</tr>");
});
Would anyone know how to accomplish this?

Here is a complete solution.
var tableMaker = o => {var keys = Object.keys(o[0]),
rowMaker = (a,t) => a.reduce((p,c,i,a) => p + (i === a.length-1 ? "<" + t + ">" + c + "</" + t + "></tr>"
: "<" + t + ">" + c + "</" + t + ">"),"<tr>"),
rows = o.reduce((r,c) => r + rowMaker(keys.reduce((v,k) => v.concat(c[k]),[]),"td"),rowMaker(keys,"th"));
return "<table>" + rows + "</table>";
},
results1 = [{
ID: "17",
description: "Description 1"
}, {
ID: "22",
description: "Description 2"
}, {
ID: "34",
description: "Description 3"
}, {
ID: "54",
description: "Description 4"
}],
results2 = [{
ID_of_parent: "17",
Day_or_night: "day",
Start: "2016-06-01 08:00",
End: "2016-06-01 10:00",
Hours: "2"
}, {
ID_of_parent: "17",
Day_or_night: "day",
Start: "2016-06-01 13:00",
End: "2016-06-01 14:00",
Hours: "1"
}, {
ID_of_parent: "17",
Day_or_night: "night",
Start: "2016-06-01 21:00",
End: "2016-06-01 22:00",
Hours: "1"
}, {
ID_of_parent: "22",
Day_or_night: "day",
Start: "2016-06-01 09:00",
End: "2016-06-01 10:00",
Hours: "1"
}, {
ID_of_parent: "22",
Day_or_night: "day",
Start: "2016-06-01 14:00",
End: "2016-06-01 15:00",
Hours: "1"
}, {
ID_of_parent: "54",
Day_or_night: "day",
Start: "2016-06-01 13:30",
End: "2016-06-01 16:00",
Hours: "2.5"
}],
desclut = results1.reduce((p,c) => (p[c.ID] || (p[c.ID] = c.description),p),{}),
merged = results2.map(e => (e.Description = desclut[e.ID_of_parent], delete e.ID_of_parent,e)),
myTable = tableMaker(merged);
document.write(myTable);
The tableMaker function is generic and generates a table from array of objects. The object's properties must be the same and they are used for the table headers and each object constructs a row with its values.
desclut is a look up table constructed from results1. By doing this we prevent using array.find() type of expensive searches for each element of the results2 array.
merged is the array we get result1 and result2 merged in the form that we can use with our tableMaker function.
If you want to reorder the properties (table headers) you can use a merged.reduce((p,c) => {sort the properties accordingly here},{}) instruction.

https://jsfiddle.net/tj6bcjos/9/
tested. data1 is the parent, data2 is the children
for each (p in data1.d.results){
var pId = p.ID;
//find the child
var children = data2.d.results.filter(function(d){
return d.ID_of_parent == pId
})
var s=[], e=[], h=[], n=[] //start, end hours...
for each (c in children)
{
s.push(c.Start);
e.push(c.End);
h.push(c.Hours);
n.push(c.Day_or_night);
}
var rw = '<tr><td>'+p.ID+'</td><td>'+p.description+'</td><td>'+
s.join('<br>')+'</td><td>'+e.join('<br>')+'</td><td>'+h.join('<br>')
+'</td><td>'+n.join('<br>')+'</td></tr>'
console.log(rw)
$('#my_table tbody').append(rw);
}

Related

Converting strings to intergers in JSON using JavaScript

I have the following json
{
"Title": "Test",
"StartDate": {
"Month": "3",
"Year": "1973"
},
"EndDate": {
"Month": "4",
"Year": "1974"
}
}
I want Month and Year values from StartDate and EndDate to be without quotes, like this:
{
"Title": "Test",
"StartDate": {
"Month": 3,
"Year": 1973
},
"EndDate": {
"Month": 4,
"Year": 1974
}
}
EDIT
I'm not creating the json, with JSON.stringify(). My JSON is created by Form Builder module from Angular 2, despite the fact that I'm setting it's type to number, after I change the value, the value gets quotes.
Before saving your JSON, use the parseInt() functions to convert your values into integers. This will remove the quotes.
JSON.stringify({
Month: parseInt( value , 10);
})
See this answer
EDIT
If you made that JSON Object earlier in your JavaScript code, go for #Adrian Lambertz's answer. If you got that JSON as a String from somewhere else and want to convert it, read my answer :
My original answer
Say you got this JSON as a string in your JavaScript code, you could convert the desired values to integers like this :
var events = JSON.parse(JSONStringYouWantToConvert);
// if the JSON String is an array of events that all have a Title, a StartDate and an EndDate
for (var i = 0; i < events.length; i++) {
// else, forget about the loop and the [i] index, the concept remains the same
events[i].StartDate.Month = parseInt(events[i].StartDate.Month);
events[i].StartDate.Year = parseInt(events[i].StartDate.Year);
events[i].EndDate.Month = parseInt(events[i].EndDate.Month);
events[i].EndDate.Year = parseInt(events[i].EndDate.Year);
}
// make a JSON String that wont have the quotes around the Month and Year numbers
var JSONStringConverted = JSON.stringify(events);
Just convert the strings to numbers.
This code is just an example, you can adapt it to whatever your object structure is.
function normalize(target) {
for (const date of ['StartDate', 'EndDate']) {
for (const item of ['Month', 'Year']) {
target[date][item] = Number(target[date][item]);
}
}
}
I can not see the order from which data the result is expeceted, so here two versions.
Object -> JSON (-> Object)
This works with JSON.stringify
and a replacer function for creating numbers for certain keys, like Month and Year.
var dataObj = { "Title": "Test", "StartDate": { "Month": "3", "Year": "1973" }, "EndDate": { "Month": "4", "Year": "1974" } },
jsonStr = JSON.stringify(dataObj, function (k, v) {
return ['Month', 'Year'].indexOf(k) !== -1 ? +v : v;
}),
parsed = JSON.parse(jsonStr);
console.log(jsonStr);
console.log(parsed);
JSON -> Object
This works with JSON.parse
and a reviver function for creating numbers for certain keys, like Month and Year.
var jsonStr = '{ "Title": "Test", "StartDate": { "Month": "3", "Year": "1973" }, "EndDate": { "Month": "4", "Year": "1974" } }',
parsed = JSON.parse(jsonStr, function (k, v) {
return ['Month', 'Year'].indexOf(k) !== -1 ? +v : v;
});
console.log(parsed);

Loop dates and add object if not excist in JSON data

I need to create calendar view with fullcalendar.io. To use calendar I need to create a JSON like this:
var db_data = [
{
"id": 5,
"user_id": 1,
"article_id": 5,
"title": "",
"start": "2016-03-25 15:18:46"
},
{
"id": 4,
"user_id": 1,
"article_id": 5,
"price": 55,
"title": "",
"start": "2016-03-15 15:18:46"
} etc.
I need to put price at calendar from every date from period_start to period_end, but for some prices I have values in database and for other I dont have so I need to use standar price (etc.$50)...
SO I have period_start and period_end and I get some data from database but now I need to create an JSON with objects for dates which are not in database so I create code:
function fulljson (){
var db_data;
$.ajax({
url: "http://localhost:8888/diving/{{$article->id}}",
type: "GET",
async: true,
dataType: "json",
success: function(data) {
db_data = data;
console.log(db_data);
// declare variables
var period_start = new Date('{{ date('Y-m-d', strtotime($article->from)) }}'),
period_end = new Date('{{ date('Y-m-d', strtotime($article->to)) }}'),
current_date = period_start,
array_of_all_dates = [];
// Create a populated array of dates
while (current_date.getTime() <= period_end.getTime()) {
array_of_all_dates.push(current_date);
current_date = new Date(+current_date);
current_date.setDate(current_date.getDate() + 1);
}
// Now loop over the array of populated dates and mutate, so something like
array_of_all_dates = array_of_all_dates.map(function (date) {
var found_in_db = db_data.filter(function (db_data) {
return new Date(db_data.start.replace(" ", "T")).getTime() === date.getTime(); // I need to do this comparison better!
});
if (found_in_db.length > 0) {
return found_in_db[0];
}
var new_object = {
title: '',
start: date,
price: '{{$article->price}}'
};
console.log(new_object);
return new_object;
});
console.log('result'+array_of_all_dates);
drawCalendar(array_of_all_dates);
},
error: function (data) {
console.log(data);
console.log('ERROR');
}
});
};
$(document).ready(function() {
fulljson();
});
function drawCalendar(data) {
$('#calendar').fullCalendar({
header: {
left: 'prev today',
center: 'title',
right: 'next'
},
defaultDate: Date.now(),
eventRender: function(event, element) {
element.find("fc-event-container").remove();
element.find(".fc-content").remove();
element.find(".fc-event").remove();
var new_description =
'<div style="display:inline-flex; float:right;"><div class="col-md-6"style="margin-top:5px";><p class="price">' + event.price + 'e</p></div>';
element.append(new_description);
} ,// allow "more" link when too many events
events: data,
loading: function(bool) {
$('#loading').toggle(bool);
}
}
});
};
And now this code work in Chrome:
But in FireFox I get this:
Please look at 1.2.3. February where I have data from database. You will see the changes.
SO why this code work in Chrome and dont work in Firefox? What is a problem? Is there a better solution to solve this problem?
SO why this code work in Chrome and dont work in Firefox?
Because the date strings provided are not valid and Firefox will transpose those to invalid date when passed to new Date(). Chrome however does parse them but this is not expected behavior cross browser
Send valid ISO strings from server or any format recommended in plugin docs
From fullcalender docs:
When specifying Event Objects for events or eventSources, you may
specify a string in IETF format (ex: "Wed, 18 Oct 2009 13:00:00 EST"),
a string in ISO8601 format (ex: "2009-11-05T13:15:30Z") or a UNIX
timestamp.

I'd like to push non-existing items inside an already declared object if they don't already exist using lodash in angular

I'm new with lodash but as the title states 'I'd like to push non-existing items inside an already declared object if they don't already exist' that is if I have
var lessdata = {
"id": 1004,
"name": "some event",
"bookmarked": false //not in moredata and I'd like to keep the var as is
};
var moredata = {
"id": 1004,
"name": "some event",
"time": { //from here
"hours": 2,
"minutes": 00,
"currency": "USD"
},
"place": "some place" //to here is new without '"bookmarked": false'
};
I'd like to have my result loaded back into the lessdata variable and have my result look like so
var lessdata = {
"id": 1004,
"name": "some event",
"time": {
"hours": 2,
"minutes": 00,
"currency": "USD"
},
"place": "some place",
"bookmarked": false
};
I stuck knowing know to use lodash apprpriatly in angular and wasnt sure if I need to use angualar's forEach or not.
I've dabbled with two approaches.
version 1
lessdata= _.uniq(lessdata, function(moredata) {
return moredata;
});
version 2
angular.forEach(lessdata, function(lkey, lvalue) {
console.log("[-]lessdata---lkey: " + lkey + ", lvalue: " + lvalue)
angular.forEach(moredata, function(mkey, mvalue) {
console.log("[+]moredata---mkey: " + mkey + ", mvalue: " + mvalue)
lessdata=_.uniq(lessdata, function(moredata) {
return moredata;
});
})
})
$scope.event = lessdata
Im assuming using _.uniq is the best approach? any help would be appreciated and I created a codepen here.
TLDR: just read the title
That's what lodash.defaults does:
Assigns own and inherited enumerable properties of source objects to the destination object for all destination properties that resolve to undefined.
lodash.defaults(lessdata, moredata);

JavaScript - Sum/merge multiple objects in an array

I would like to sum up two objects into one total object and get a percent change of the two values (add then divide by # of values). I'm having some issues applying this logic. The data is dynamic so there could be more than two objects in the same array.
My preferred method is JavaScript but jQuery is perfectly fine as well if it's simpler! Below is an example and here is a jsfiddle - http://jsfiddle.net/4z63cwhz/4/
Response Data:
$scope.data = [
{
"July":{
"params":{
"frequency":"Monthly",
"category":"Overview",
"year":"2015",
"month":"July"
},
"subcategory":{
"us total":{
"interaction rate":0.51,
"digital interaction rate":8.33,
"impressions":500,
"interactions":256
},
"discover me":{
"digital u.s. discover site sessions":50
},
"me com":{
"total me.com site sessions":50
}
},
"action":"new"
},
"August":{
"params":{
"frequency":"Monthly",
"category":"Overview",
"year":"2015",
"month":"August"
},
"subcategory":{
"us total":{
"interaction rate":0.51,
"digital interaction rate":8.33,
"impressions":500,
"interactions":256
},
"discover me":{
"digital u.s. discover site sessions":50
},
"me com":{
"total me.com site sessions":50
}
},
"action":"new"
}
}
]
JS:
$scope.calculate = function(){
var sumObj = {};
var local = $scope.data;
for(var i in local){
var obj = local[i];
for(var i2 in obj){
for(var i3 in obj[i2].subcategory){
//do the calculation here.
console.log(i3);
}
}
}
};
The sumObj I was hoping to get would be similar to below:
{
"us total":{
"interaction rate":{
total: total#,
'%change': %change
},
"digital interaction rate":{
total: total#,
'%change': %change
},
"impressions":{
total: total#,
'%change': %change
},
"interactions":{
total: total#,
'%change': %change
}
},
"discover me":{
"digital u.s. discover site sessions":{
total: total#,
'%change': %change
}
},
"me com":{
"total me.com site sessions":{
total: total#,
'%change': %change
}
}
}
Thanks in advance!
Well if the json objects all repeat the same format then can't you just do:
var text= {};
local[current].attributeString (some calculation) local[next].attributeString += text;
where current = i and next = i+1.
Then you can use:
var obj = JSON.parse(text);
to convert your String into a json object. I'd probably use some type of key/value pair instead of a string but this may help reduce some of your loops.

How to access specific object from data array in JSON?

data: [
{
cid: "211211",
parentCid: "212121",
level: 3,
number: "2.1.1",
subject: "Title 2.1.1"
},
{
cid: "111111",
parentCid: "",
lv: 1,
number: "1",
subject: "Title1"
},
{
cid: "222222",
parentCid: "",
lv: 1,
number: "2",
subject: "Title2"
},
{
cid: "212121",
parentCid: "222222",
lv: 2,
number: "2.1",
subject: "Title2.1"
},
{
cid: "333333",
parentCid: "",
lv: 1,
number: "3",
subject: "Title3"
}
]
I have a json data like above and I can get subject by data[i].subject.
For example, I can get "Title3" by data[i].subject.(i = 4)
then I can get "Title3" prev item by data[i-1].subject and I will get "Title2.1".
My questionis , is there any way that I can get prev item in same "lv"(level)?
in english is something like "data[ previtem in same lv] ?
Thanks and sorry for bad english.
I've seen your previous question yesterday... I suppose that if you need to do it (search for spec element by level) for many times, you should sort the array. Or you just need to do it for few times, you just need a loop to find the previous element you want.
A simple method like this:
function findPrev(i) {
var level = data[i--].lv;
while (i >= 0) {
if (data[i].lv === level) return i;
i--;
}
return -1; // not found
}
See the complete demo:
var data = [
{
cid: "211211",
parentCid: "212121",
lv: 3,
number: "2.1.1",
subject: "Title 2.1.1"
},
{
cid: "111111",
parentCid: "",
lv: 1,
number: "1",
subject: "Title1"
},
{
cid: "222222",
parentCid: "",
lv: 1,
number: "2",
subject: "Title2"
},
{
cid: "212121",
parentCid: "222222",
lv: 2,
number: "2.1",
subject: "Title2.1"
},
{
cid: "333333",
parentCid: "",
lv: 1,
number: "3",
subject: "Title3"
}
];
function findPrev(i) {
var level = data[i--].lv;
while (i >= 0) {
if (data[i].lv === level) return i;
i--;
}
return -1; // not found
}
console.log(data[findPrev(4)]); // data[4] go find data[2]
I'm assuming you already have an index i of the "current item", or know how to get it. In that case, a naive method would be to move backwards from the current item in a loop, comparing lv:
var previousItemIndex = i - 1;
while(previousItemIndex >= 0 && data[i].lv !== data[previousItemIndex].lv)
previousItemIndex--;
This will traverse the array backwards from i, finding the first element with a matching lv property, or returning -1 if it does not exist.
This also assumes that "previous" means in the same order as presented in the JSON array (i.e. the index has semantic meaning). If you meant to order by something else, for example number, you might consider sorting the entire thing first.
LateBloomer, The answer to your question is simple. To find the previous element with same level, all you need to do is decrement "i" and check whether the level is same or not. If not then decrement again.
Exactly what iplus26 did.
I think you want something like this:
function prevItemSameLv(data, i) {
for (var idx=i-1; idx>=0; idx--) {
if (data[idx].lv == data[i].lv) return data[idx];
}
}
You can use library like _lodash, it has function named "findIndex", read more here: https://lodash.com/docs#findIndex But it will return only one value.
Also, you can check this question, about filtering data: lodash Filter collection using array of values

Categories

Resources