Hey everyone, so I am working on creating a small class to help me work with the Google visualization API. You can see how it works here...
http://code.google.com/apis/visualization/documentation/gallery/annotatedtimeline.html
Here is google's implementation.
google.load('visualization', '1', {'packages':['annotatedtimeline']});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'Sold Pencils');
data.addColumn('string', 'title1');
data.addColumn('string', 'text1');
data.addColumn('number', 'Sold Pens');
data.addColumn('string', 'title2');
data.addColumn('string', 'text2');
data.addRows([
[new Date(2008, 1 ,1), 30000, undefined, undefined, 40645, undefined, undefined],
[new Date(2008, 1 ,2), 14045, undefined, undefined, 20374, undefined, undefined],
[new Date(2008, 1 ,3), 55022, undefined, undefined, 50766, undefined, undefined],
[new Date(2008, 1 ,4), 75284, undefined, undefined, 14334, 'Out of Stock','Ran out of stock on pens at 4pm'],
[new Date(2008, 1 ,5), 41476, 'Bought Pens','Bought 200k pens', 66467, undefined, undefined],
[new Date(2008, 1 ,6), 33322, undefined, undefined, 39463, undefined, undefined]
]);
var chart = new google.visualization.AnnotatedTimeLine(document.getElementById('chart_div'));
chart.draw(data, {displayAnnotations: true});
Here is the class I made that I am having issues with.
The class makes adding data to the graph a little easier and better for what I am trying to do. Basically, instead of making columns with a bunch of undefined values, the class does it for you, and you don't have to keep track of the location/value of each column.
function GraphManager(dataTable)
{
this.graphData = new Array();
this.dataTable = dataTable;
this.titleFinder = new Object(); // used to keep track of the indices
this.dataTable.addColumn('date', 'Date');
this.addSet = function(title)
{
var setCount = (this.dataTable.getNumberOfColumns() -1) / 3; //used for the column name
var place = this.dataTable.getNumberOfColumns();
this.titleFinder[title] = place; //the title of the column and its location
this.dataTable.addColumn('number', title);
this.dataTable.addColumn('string', 'title' + setCount);
this.dataTable.addColumn('string', 'text' + setCount);
}
this.addPoint = function(title, date, number)
{
//this function finds the location of the category
//and inserts a column with data at the location
var place = titleFinder[title]; //get the location
var column = new Array(dataTable.getNumberOfColumns());
column[0] = date;
column[place] = number;
for (var i = 0;i<place; i++)
{
column[i] = undefined;
}
for (var i = place + 1; i<dataTable.getNumberOfColumns(); i++)
{
column[i] = undefined;
}
var next = this.graphData.length;
this.graphData[next] = column;
data.addRows(graphData);
}
}
And then this code can be used to do the same thing with a fewer amount of code.
function printGraph()
{
var data = new google.visualization.DataTable();
var gm = new GraphManager(data);
var title = "testcategory";
gm.addSet(title);
gm.addPoint(title, new Date[2010, 5, 10], 100);
gm.addPoint(title, new Date[2010, 6, 10], 200);
var chart = new google.visualization.AnnotatedTimeLine(document.getElementById('chart_div'));
chart.draw(gm.dataTable, {displayAnnotations: true});
}
With this HTML body
<input type="button" onclick="printGraph()" value="Draw Graph">
<div id='chart_div' style='width: 800px; height: 350px;'></div>
The issue: I am getting an "Object expected" error on the line gm.addSet(title). Basically, I am not able to use the class GraphManager. What am I doing wrong here?
Isn't supposed to be "dataTable" instead of "tableData"?
for (var i = place + 1; i<tableData.count; i++)
{
column[i] = undefined;
}
I don't know, as per:
http://code.google.com/apis/visualization/documentation/reference.html#DataTable
count is not an attribute, but I see you referring to it many places in your code:
var column = new Array(dataTable.count)
There is dataTable.getNumberOfColumns() however
Ok, I figured out the problem. Basically I had left out a bunch of "this" statements, and when I created a new date I used a bracket instead of a parentheses. I also realized that when I added a new set of data, I needed to go through the old data to add the empty columns. Here is the finished code if anyone is interested... It's pretty useful if you are adding data at different dates or if you don't know how many data sets you will have.
function GraphManager(adataTable)
{
this.graphData = new Array();
this.dataTable = adataTable;
this.titleFinder = new Object(); // used to keep track of the indices
this.dataTable.addColumn('date', 'Date');
this.addSet = function(title)
{
var setCount = (this.dataTable.getNumberOfColumns() -1) / 3; //used for the column name
var pointsCount = this.graphData.length;
var place = this.dataTable.getNumberOfColumns();
this.titleFinder[title] = place; //the title of the column and its location
this.dataTable.addColumn('number', title);
this.dataTable.addColumn('string', 'title' + setCount);
this.dataTable.addColumn('string', 'text' + setCount);
var newCount = this.dataTable.getNumberOfColumns();
for (var i = 0; i<pointsCount; i++)
{
for (var j=place; j<newCount; j++)
{
this.graphData[i][j] = undefined;
}
}
}
this.addPoint = function(title, date, number)
{
//this function finds the location of the category
//and inserts a column with data at the location
var place = this.titleFinder[title]; //get the location
var column = new Array(this.dataTable.getNumberOfColumns());
column[0] = date;
column[place] = number;
for (var i = 1;i<place; i++)
{
column[i] = undefined;
}
for (var i = place + 1; i<this.dataTable.getNumberOfColumns(); i++)
{
column[i] = undefined;
}
var next = this.graphData.length;
this.graphData[next] = column;
this.dataTable.addRows(this.graphData);
}
}
And its as easy to use as this:
var data = new google.visualization.DataTable();
var gm = new GraphManager(data);
var title = "testcategory";
var title2 = "cat";
gm.addSet(title);
gm.addPoint(title, new Date(2010, 5, 10), 100);
gm.addPoint(title, new Date(2010, 6, 10), 200);
gm.addPoint(title, new Date(2010, 2, 10), 300);
gm.addSet(title2);
gm.addPoint(title2, new Date(2010, 6, 10), 100);
gm.addPoint(title2, new Date(2010, 2, 10), 500);
var chart = newgoogle.visualization.AnnotatedTimeLine(document.getElementById('chart_div'));
chart.draw(gm.dataTable, {displayAnnotations: true});
Related
I'm trying to add a constraint to one of my columns which would prevent entering values in the column C that are lesser than those in the column B.
For example, if the value in B1 is 10, inputting 7 in the column C1 shouldn't be allowed.
If the value in B2 is 4, inputting anything less than 4 in the column C1 shouldn't be allowed, etc.
So far I managed to produce a simple file where each cell in the third column is a sum of first and second column's cells - C1=A1+B1, C2=A2+B2... (Notice that column A is used only for the sake of this example). downloadSheet() function is where the the logic is implemented.
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.0/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>
<button onclick="downloadSheet()">Download excel</button>
<script>
function uigrid_to_sheet(data, columns) {
var o = [],
oo = [],
i = 0,
j = 0;
/* column headers */
for (j = 0; j < columns.length; ++j) oo.push((columns[j]));
o.push(oo);
/* table data */
for (i = 0; i < data.length; ++i) {
oo = [];
for (j = 0; j < data[i].length; ++j) oo.push((data[i][j]));
o.push(oo);
}
/* aoa_to_sheet converts an array of arrays into a worksheet object */
return XLSX.utils.aoa_to_sheet(o);
}
var s2ab = function (s) {
var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
var view = new Uint8Array(buf); //create uint8array as viewer
for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
return buf;
}
function downloadSheet() {
var sheetName = 'first_sheet';
var wopts = { bookType: 'xlsx', bookSST: true, type: 'binary' };
var fileName = "the_excel_file.xlsx";
var columns = ['first', 'second', 'result'];
var data = [
[1, 20],
[2, 32],
[3, 18],
[4, 11]
];
var wb = XLSX.utils.book_new();
var ws = uigrid_to_sheet(data, columns);
ws['!ref'] = XLSX.utils.encode_range({
s: { c: 0, r: 0 },
e: { c: 2, r: 0 + data.length }
});
data.forEach((element, i) => {
ws['C'+(i+2)] = { f: 'A'+(i+2) + '+' + 'B'+(i+2) };
});
XLSX.utils.book_append_sheet(wb, ws, sheetName);
var wbout = XLSX.write(wb, wopts);
saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), fileName);
}
</script>
Logically and functionally what I require is different, but I can't seem to figure what formula (or other method) should I use. I tried simply using formula
ws['C1'] = { f: 'C1>=B1' };
but this simpleton attempt unsurprisingly failed with a message that it caused a circular reference.
This requirement was made because our systems allow updating our web shop's prices through an excel file which can be manually edited and sent to server. The problem is that guys in that department can sometimes accidentally omit or add a cypher and then we get ourselves incorrect prices on the web shop. Column B in my example is the purchase price, column C is the Web Shop price, and values in column C should never be allowed to get under that purchase price in column B.
Edited to include entire HTML so someone can just copy paste it into a new HTML file and it should work.
I am trying to add the values of multiple arrays (one starts out empty, but all the ones I am adding to it are the same length, though it be great if we could come up with something that adds them even if they are all different lengths) - not the sum of all values in each array, instead, sum of the values in the same index. For example:
array1 = [1, 2, 3]
array2 = [2, 3, 4]
desiredArray = [3, 5, 7]
The number of the arrays I will be adding is arbitrary, as they are created based on the users selection. (Based on the length of the array created from the selection). I want to sum the arrays by index to create a new array, and from the new array, I will create a decline curve. When I attempt to add them using "indexSum" I get an array below back full of NaaNs...though they are the correct legth:
requestedOil
requestedGas
requestedWater
These are the temporary arrays created by the length of the var "values" - these are the ones I am trying to add by respective index to eventually get the ones mentioned above:
Oil[well]
Gas[well]
Water[well]
THIS IS THE FUNCTON I CURRENTLY HAVE TO ADD ARRAYS AND CALLED WHEN USER MAKES SELECTION FROM multiple-site-selection
function updateCurves(){
var dropdownMenu = document.getElementById("multiple-site-selection").selectedOptions;
var values = Array.from(dropdownMenu).map(({ value }) => value);
console.log(values);
d3.json('./static/wellNames.json').then((data) => { //read in the wellNames.json file, which contains the array "names" with all the well names
wellOptions = data.names;
forSelection = wellOptions.map((x) => ({id:x}))
console.log(forSelection);
d3.json("./static/all_production.json").then((data) =>{
var requestedOil = [];
var requestedGas = [];
var requestedWater = [];
var site_date = [];
var Oil = [];
var Gas = [];
var Water = [];
values.forEach((well) => {
forSelection.forEach((pair) => {
if(well == Object.values(pair)){
Oil[well] = new Array();
Gas[well] = new Array();
Water[well] = new Array();
new Promise ((resolve) => data.forEach((site) => {
if(values.length == 1 && well == site[0]){
requestedOil.push(site[2]);
requestedGas.push(site[3]);
requestedWater.push(site[4]);
site_date.push(site[8])}
else if(values.length > 1 && well == site[0]){
indexSum = (a1, a2) => a1.map((v, i) => i + a2[v]);
Oil[well].push(site[2])
requestedOil = indexSum(Oil[well], requestedOil);
Gas[well].push(site[3])
requestedGas = indexSum(Gas[well], requestedGas);
Water[well].push(site[4])
requestedWater = indexSum(Water[well], requestedWater);
site_date.push(site[8])}
else{}
resolve()}))//PROMISE CLOSED
} //IF CLOSED
})//forSelection (dic containing names of well selected) closed
console.log(Oil[well]);
}); //values.forEach closed
THIS CODE CURRENTLY WORKS AS I AM NOT ADDING ANY ARRAYS AND IT IS CALLED AS SOON AS THE WEBPAGE LOADS
//FUNCTION TO CREATE DROP DOWN VALUES
function createDropdownOptions() {
var selector = d3.select("#multiple-site-selection"); //select dropdown <select> in well.html with id:"siteSelection"
d3.json('./static/wellNames.json').then((data) => { //read in the wellNames.json file, which contains the array "names" with all the well names
var wellOptions = data.names;
wellOptions.forEach((well) => {
selector
.append('option')
.text(well)
.property('Value', well);
})
})
};
createDropdownOptions(); //CALL FUNCTION TO CREATE DROPDOWN MENU VALUES
// //FUNCTION TO CREATE HOME/SUMMARY CURVES
function curvesHome() {
d3.json("./static/all_production.json").then((data) =>{ //THIS WORKS!!!
var site_oil = [];
var site_gas = [];
var site_water = [];
summarySiteDate = [];
new Promise ((resolve) => data.forEach(site => {if (site[0]==="Summary") {
site_oil.push(site[2]);
site_gas.push(site[3]);
site_water.push(site[4]);
summarySiteDate.push(site[8]);
} resolve()}));
//CALL FUNCTION TO CREATE DROPDOWN MENU VALUES
var mostRecentEntry = summarySiteDate[0]; //MOST RECENT DATE WITHOUT HOUR AS VARIABLE
var addingHours = "T00:00"; //HOURS TO ADD TO MOST RECENT DATE - NEEDED TO NORMALIZE FROM ORIGINAL 19 HOUR FORMAT
var nextYear = mostRecentEntry.concat(addingHours); //DATE AND HOUR AS SINGLE VARIABLE TO MAKE INTO DATE
var mostRecentDate = new Date(nextYear); //MAKE VARIABLE INTO DATE
var nextYearsDate = new Date(mostRecentDate.setFullYear(mostRecentDate.getFullYear() + 1)); //GET YEAR FROM MOST RECENT DATE AND ADD A YEAR
var nextYear= nextYearsDate.getFullYear() //GET NEXT YEARS DATE
var nextMonth= nextYearsDate.getMonth() + 1 // GET NEXTS YEARS MONTH, ADD ONE BECAUSE MONTHS ARE INDEXED AT 0
var nextDate= nextYearsDate.getDate() //GET NEXT YEARS DATE
nextYearGraph = `${nextYear}-${nextMonth}-${nextDate}`; // CREATE FULL DATE FOR NEXT YEAR IN RIGHT FORMAT FOR AXIS
console.log(`${nextYearGraph} is a year from the most recent production date. This is from curvesHome()`);
var dataOil = [{
x: summarySiteDate,
y: site_oil,
type: "line",
line:
{color: "green"}
}];
var layoutOil = {
title: "Oil BBL",
yaxis: {
type: 'log',
autorange: true
},
xaxis: {
autorange: false,
range: [summarySiteDate[summarySiteDate.length-1], nextYearGraph]
}
};
Plotly.newPlot("oilDeclineCurve", dataOil, layoutOil);
// gas decline curve data
var dataGas = [{
x: summarySiteDate,
y: site_gas,
type: "line",
line: {color: "red"}
}];
var layoutGas = {
title: "Gas BBL",
yaxis: {
type: 'log',
autorange: true
},
xaxis: {
autorange: false,
range: [summarySiteDate[summarySiteDate.length-1], nextYearGraph]
}
};
Plotly.newPlot("gasDeclineCurve", dataGas, layoutGas);
// water decline curve data
var dataWater = [{
x: summarySiteDate,
y: site_water,
type: "line" }
];
var layoutWater = {
title: "Water BBL",
yaxis: {
type: 'log',
autorange: true
},
xaxis: {
autorange: false,
range: [summarySiteDate[summarySiteDate.length-1], nextYearGraph]
}
};
Plotly.newPlot("waterDeclineCurve", dataWater, layoutWater);
})};
I have both HTML sand JS in my code, so it is probably best if you have the whole thing to better understand what I am doing and trying to do. Here are my links:
My repo:
My GitPage
Thank you in advanced!
Here is a function that bases length off of first array length and doesn't care about the number of input arrays
function stackSum(arr) {
return arr[0].map((a, i) => {
let r = 0;
arr.forEach(b, j) => r+=arr[j][i]);
return r;
})
}
console.log(stackSum([
[1,1,1],
[1,2,2],
[2,2,3],
[3,7,7]
]).join(','));
console.log(stackSum([
[1,2,3],
[2,3,4]
]).join(','));
Since the arrays are of equal lengths, you can simply use Array#map.
const
indexSum = (a1, a2) => a1.map((v, i) => v + a2[i]),
desiredArray = indexSum([1, 2, 3], [4, 5, 6])
console.log(desiredArray)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am trying to get my nested loops to perform a check in / check out simulation. I have my functions set up and my loop at the end but it somehow always gives me the same output. Every patron has the same amount of books and a humongous fine instead of being totally randomized like the checkOut and isOverdue functions make them.
How do I make it so that the loops uses the randomly generated date from the functions and give each patron different amounts of fines and different books checked out? Instructions for the project.
My code:
var Book = function(title, Available, publicationDate, checkoutDate, callNumber, Authors) {
this.title = title;
this.Available = Available;
this.publicationDate = publicationDate;
this.checkoutDate = checkoutDate;
this.callNumber = callNumber;
this.Authors = Authors;
};
var Author = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};
var Patron = function(firstName, lastName, libCardNum, booksOut, fine) {
this.firstName = firstName;
this.lastName = lastName;
this.libCardNum = libCardNum;
this.booksOut = booksOut;
this.fine = fine;
};
function randInRange(start, end) {
return Math.floor(Math.random() * (end - start + 1)) + start;
}
Book.prototype.checkOut = function() {
this.Available = false;
var dateChk = new Date();
var randDay = randInRange(1, 25);
dateChk.setDate(dateChk.getDate() - randDay);
this.checkoutDate = dateChk;
};
Book.prototype.checkIn = function() {
this.Available = true;
};
Book.prototype.isOverdue = function() {
var singleDay = 1000 * 60 * 60 * 24;
var todayDate = new Date().getTime();
var difference = todayDate - this.checkoutDate.getTime();
if (Math.round(difference / singleDay) >= 14) {
return true;
}
return false;
};
Patron.prototype.read = function(book) {
this.booksOut.add(book);
}
Patron.prototype.return = function(book) {
this.booksOut.remove(this.booksOut.length);
}
var authors = [];
authors[0] = new Author("Auth", "One");
authors[1] = new Author("AutL", "Two");
var catalog = [];
catalog[0] = new Book('Bk1', true, new Date(2001, 1, 21), new Date(), 123456, authors);
catalog[1] = new Book('Bk2', true, new Date(2002, 2, 22), new Date(), 987656, authors);
catalog[2] = new Book('Bk3', true, new Date(2003, 3, 23), new Date(), 092673, authors);
catalog[3] = new Book('Bk4', true, new Date(2004, 4, 24), new Date(), 658342, authors);
catalog[4] = new Book('Bk5', true, new Date(2005, 5, 25), new Date(), 345678, authors);
var patrons = [];
patrons[0] = new Patron('Pat1', 'Wat', 1, catalog, 0.00);
patrons[1] = new Patron('Pat2', 'Wot', 1, catalog, 0.00);
patrons[2] = new Patron('Pat3', 'Wit', 1, catalog, 0.00);
patrons[3] = new Patron('Pat4', 'Wet', 1, catalog, 0.00);
patrons[4] = new Patron('Pat5', 'Wut', 1, catalog, 0.00);
//while loop or for loop for 90 days
//For loop over catalog
//forloop over patrons
//Check if available , if so check book out
//If not available check book back in
//check checking back in check to see if book is overdue and if so add a fine
//When down loop over patrons to see their fees
for (var i = 0; i < 90; i++) {
for (var j = 0; j < catalog.length; j++) {
for (var k = 0; k < patrons.length; k++) {
var fine = patrons[k].fine;
if (catalog[k].Available) {
catalog[k].checkOut;
} else {
catalog[k].checkIn;
patrons[k].read;
}
if (catalog[k].isOverdue) {
fine = fine + 5.00;
}
patrons[k].fine = fine;
}
}
}
for (i = 0; i < patrons.length; i++) {
console.log(patrons[i].firstName + " has checked out the following books:");
for (j = 0; j < patrons[i].booksOut.length; j++) {
console.log(patrons[i].booksOut[j].title);
}
console.log(patrons[i].firstName + " has fine amount: $" + patrons[i].fine);
}
I had to make a number of changes to your example so it actually works.
One very important thing to know is that when you call a method of an object you have to put parenthesis behind the call: not catalog[j].checkOut but catalog[j].checkOut().
You can examine my changes and how the script works now. With this in hand you can formulate follow up questions.
Hope this helps.
var Book = function(title, Available, publicationDate, checkoutDate, callNumber, Authors) {
this.title = title;
this.Available = Available;
this.publicationDate = publicationDate;
this.checkoutDate = checkoutDate;
this.callNumber = callNumber;
this.Authors = Authors;
};
var Author = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};
var Patron = function(firstName, lastName, libCardNum, booksOut, fine) {
this.firstName = firstName;
this.lastName = lastName;
this.libCardNum = libCardNum;
this.booksOut = booksOut;
this.fine = fine;
};
function randInRange(start, end) {
return Math.floor(Math.random() * (end - start + 1)) + start;
}
Book.prototype.checkOut = function() {
this.Available = false;
var dateChk = new Date();
var randDay = randInRange(1, 25);
dateChk.setDate(dateChk.getDate() - randDay);
this.checkoutDate = dateChk;
};
Book.prototype.checkIn = function() {
this.Available = true;
};
Book.prototype.isOverdue = function() {
var singleDay = 1000 * 60 * 60 * 24;
var todayDate = new Date().getTime();
var difference = todayDate - this.checkoutDate.getTime();
if (Math.round(difference / singleDay) >= 14) {
return true;
}
return false;
};
// Changed the read method
Patron.prototype.read = function(book) {
this.booksOut[book.callNumber] = book;
}
// Changed the return method
Patron.prototype.return = function(book) {
delete(this.booksOut[book.callNumber]);
}
var authors = [];
authors[0] = new Author("Auth", "One");
authors[1] = new Author("AutL", "Two");
var catalog = [];
catalog[0] = new Book('Bk1', true, new Date(2001, 1, 21), new Date(), 123456, authors);
catalog[1] = new Book('Bk2', true, new Date(2002, 2, 22), new Date(), 987656, authors);
catalog[2] = new Book('Bk3', true, new Date(2003, 3, 23), new Date(), 092673, authors);
catalog[3] = new Book('Bk4', true, new Date(2004, 4, 24), new Date(), 658342, authors);
catalog[4] = new Book('Bk5', true, new Date(2005, 5, 25), new Date(), 345678, authors);
// Changed how Patrons are initialised. Instead of passing the full
// catalog for booksRead an empty object is passed.
var patrons = [];
patrons[0] = new Patron('Pat1', 'Wat', 1, {}, 0.00);
patrons[1] = new Patron('Pat2', 'Wot', 1, {}, 0.00);
patrons[2] = new Patron('Pat3', 'Wit', 1, {}, 0.00);
patrons[3] = new Patron('Pat4', 'Wet', 1, {}, 0.00);
patrons[4] = new Patron('Pat5', 'Wut', 1, {}, 0.00);
//while loop or for loop for 90 days
//For loop over catalog
//forloop over patrons
//Check if available , if so check book out
//If not available check book back in
//check checking back in check to see if book is overdue and if so add a fine
//When down loop over patrons to see their fees
for (var i = 0; i < 3; i++) {
// changed the for loop
for (var j in catalog) {
// changed the for loop
for (var k in patrons) {
var fine = patrons[k].fine;
// Changed catalog[k] to catalog[j]
// moved and changed patrons[k].read() call
// added the patrons[k].return() call
if (catalog[j].Available) {
catalog[j].checkOut();
patrons[k].read(catalog[j]);
} else if (patrons[k].booksOut[catalog[j].callNumber]) {
catalog[j].checkIn();
patrons[k].return(catalog[j]);
}
if (catalog[j].isOverdue()) {
fine = fine + 5.00;
}
patrons[k].fine = fine;
}
}
}
for (i = 0; i < patrons.length; i++) {
console.log(patrons[i].firstName + " has checked out the following books:");
for (j in patrons[i].booksOut) {
console.log(patrons[i].booksOut[j].title);
}
console.log(patrons[i].firstName + " has fine amount: $" + patrons[i].fine);
}
Edit
Answer to the comment.
That is a result of how you set up your for loops. On the first day all the books go to the first patron, on the second day they go to the second partron and so on.
Day 1, book 1, patron 1: Book is available and goes to patron 1.
Day 1, book 1, patron 2: Book is not available.
...
Day 1, book 2, patron 1: Book is available and goes to patron 1.
Day 1, book 2, patron 2: Book is not available.
...
Day 2, book 1, patron 1: Book is not available, book is returned.
Day 2, book 1, patron 2: Book is available and goes to patron 2.
Day 2, book 1, patron 3: Book is not available.
...
and so on
The instructions did not say to shuffle the books or patrons, during the simulation so your results would not be as random as your hoping for. Also looping over the books and patrons we can use a standard forEach vs a regular for loop making the code much easier to follow. I'm using lodash _.shuffle to randomize the books and patrons during each loop for better results. We can also hide the fine logic in our patrons return prototype ending up with a simple simulation.
for (var i = 0; i < 90; i++) {
_.shuffle(books).forEach(function(book) {
_.shuffle(patrons).forEach(function(patron) {
if (patron.hasBookCheckedOut(book)) {
return patron.return(book);
}
if (book.available) {
patron.read(book);
}
});
});
};
Working example http://jsbin.com/teyipak/1/edit?js,console
I am trying to run a simulation of a library check in/out system with prototypes, objects and nested loops. I am having trouble properly integrating the pseudo code into the loop itself and would appreciate any help.
//while loop or for loop for 90 days
//For loop over catalog
//forloop over patrons
//Check if available , if so check book out
//If not available check book back in
//check checking back in check to see if book is overdue and if so add a fine
//When down loop over patrons to see their fees
Loop
for (var i = 0; i < 90; i++) {
for (var j = 0; j < catalog.length; j++) {
for (var k = 0; k < patrons.length; i++) {
var fine = patrons[k].fine;
if (catalog[k].Available) {
catalog[k].checkOut;
} else {
catalog[k].checkIn;
patrons[k].read;
}
if (catalog[k].isOverdue) {
fine = fine + 5.00;
}
}
}
patrons[i].fine = fine;
}
All the code
var Book = function(title, Available, publicationDate, checkoutDate, callNumber, Authors) {
this.title = title;
this.Available = Available;
this.publicationDate = publicationDate;
this.checkoutDate = checkoutDate;
this.callNumber = callNumber;
this.Authors = Authors;
};
var Author = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};
var Patron = function(firstName, lastName, libCardNum, booksOut, fine) {
this.firstName = firstName;
this.lastName = lastName;
this.libCardNum = libCardNum;
this.booksOut = booksOut;
this.fine = fine;
};
Book.prototype.checkOut = function() {
this.Available = false;
var temp = new Date(1000000000);
var date = new Date() - temp;
var res = new Date(date);
this.checkoutDate = res;
};
Book.prototype.checkIn = function() {
this.Available = true;
};
Book.prototype.isOverdue = function() {
var singleDay = 1000 * 60 * 60 * 24;
var todayDate = new Date().getTime();
var difference = todayDate - this.checkoutDate.getTime();
if (Math.round(difference / singleDay) >= 14) {
return true;
}
return false;
};
Patron.prototype.read = function(book) {
this.booksOut.add(book);
}
Patron.prototype.return = function(book) {
this.booksOut.remove(this.booksOut.length);
}
var authors = [];
authors[0] = new Author("Auth", "One");
authors[1] = new Author("AutL", "Two");
var catalog = [];
catalog[0] = new Book('Bk1', true, new Date(2001, 1, 21), new Date(), 123456, authors);
catalog[1] = new Book('Bk2', true, new Date(2002, 2, 22), new Date(), 987656, authors);
catalog[2] = new Book('Bk3', true, new Date(2003, 3, 23), new Date(), 092673, authors);
catalog[3] = new Book('Bk4', true, new Date(2004, 4, 24), new Date(), 658342, authors);
catalog[4] = new Book('Bk5', true, new Date(2005, 5, 25), new Date(), 345678, authors);
var patrons = [];
patrons[0] = new Patron('Pat1', 'Wat', 1, catalog, 0.00);
patrons[1] = new Patron('Pat2', 'Wot', 1, catalog, 0.00);
patrons[2] = new Patron('Pat3', 'Wit', 1, catalog, 0.00);
patrons[3] = new Patron('Pat4', 'Wet', 1, catalog, 0.00);
patrons[4] = new Patron('Pat5', 'Wut', 1, catalog, 0.00);
//while loop or for loop for 90 days
//For loop over catalog
//forloop over patrons
//Check if available , if so check book out
//If not available check book back in
//check checking back in check to see if book is overdue and if so add a fine
//When down loop over patrons to see their fees
for (var i = 0; i < 90; i++) {
for (var j = 0; j < catalog.length; j++) {
for (var k = 0; k < patrons.length; i++) {
var fine = patrons[k].fine;
if (catalog[k].Available) {
catalog[k].checkOut;
} else {
catalog[k].checkIn;
patrons[k].read;
}
if (catalog[k].isOverdue) {
fine = fine + 5.00;
}
}
}
patrons[i].fine = fine;
}
for (i = 0; i < patrons.length; i++) {
console.log(patrons[i].firstName + " has checked out the following books:");
for (j = 0; j < patrons[i].booksOut.length; j++) {
console.log(patrons[i].booksOut[j].title);
}
console.log(patrons[i].firstName + " has fine amount: $" + patrons[i].fine);
}
I am trying to write a loop that simulates checkouts and checkins for a 3 month period. Every day I iterate over the catalog, and every person in the patrons array. If the patron currently has the book checked out then I check it in. If it is not checked out then I add it to the patrons list of books via the patrons read method. If the book is overdue then I add a fine of $5.00 to the patron returning it. At the end of the 3 month period, I have to display each patron, the books they have currently checked out and any fine they may have.
you have typo error in your loops (for var k ... i++). See the snippet
var Book = function(title, Available, publicationDate, checkoutDate, callNumber, Authors) {
this.title = title;
this.Available = Available;
this.publicationDate = publicationDate;
this.checkoutDate = checkoutDate;
this.callNumber = callNumber;
this.Authors = Authors;
};
var Author = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};
var Patron = function(firstName, lastName, libCardNum, booksOut, fine) {
this.firstName = firstName;
this.lastName = lastName;
this.libCardNum = libCardNum;
this.booksOut = booksOut;
this.fine = fine;
};
Book.prototype.checkOut = function() {
this.Available = false;
var temp = new Date(1000000000);
var date = new Date() - temp;
var res = new Date(date);
this.checkoutDate = res;
};
Book.prototype.checkIn = function() {
this.Available = true;
};
Book.prototype.isOverdue = function() {
var singleDay = 1000 * 60 * 60 * 24;
var todayDate = new Date().getTime();
var difference = todayDate - this.checkoutDate.getTime();
if (Math.round(difference / singleDay) >= 14) {
return true;
}
return false;
};
Patron.prototype.read = function(book) {
this.booksOut.add(book);
}
Patron.prototype.return = function(book) {
this.booksOut.remove(this.booksOut.length);
}
var authors = [];
authors[0] = new Author("Auth", "One");
authors[1] = new Author("AutL", "Two");
var catalog = [];
catalog[0] = new Book('Bk1', true, new Date(2001, 1, 21), new Date(), 123456, authors);
catalog[1] = new Book('Bk2', true, new Date(2002, 2, 22), new Date(), 987656, authors);
catalog[2] = new Book('Bk3', true, new Date(2003, 3, 23), new Date(), 092673, authors);
catalog[3] = new Book('Bk4', true, new Date(2004, 4, 24), new Date(), 658342, authors);
catalog[4] = new Book('Bk5', true, new Date(2005, 5, 25), new Date(), 345678, authors);
var patrons = [];
patrons[0] = new Patron('Pat1', 'Wat', 1, catalog, 0.00);
patrons[1] = new Patron('Pat2', 'Wot', 1, catalog, 0.00);
patrons[2] = new Patron('Pat3', 'Wit', 1, catalog, 0.00);
patrons[3] = new Patron('Pat4', 'Wet', 1, catalog, 0.00);
patrons[4] = new Patron('Pat5', 'Wut', 1, catalog, 0.00);
//while loop or for loop for 90 days
//For loop over catalog
//forloop over patrons
//Check if available , if so check book out
//If not available check book back in
//check checking back in check to see if book is overdue and if so add a fine
//When down loop over patrons to see their fees
for (var i = 0; i < 90; i++) {
for (var j = 0; j < catalog.length; j++) {
for (var k = 0; k < patrons.length; k++) {
var fine = patrons[k].fine;
if (catalog[k].Available) {
catalog[k].checkOut;
} else {
catalog[k].checkIn;
patrons[k].read;
}
if (catalog[k].isOverdue) {
fine = fine + 5.00;
}
patrons[k].fine = fine;
}
}
}
for (i = 0; i < patrons.length; i++) {
console.log(patrons[i].firstName + " has checked out the following books:");
for (j = 0; j < patrons[i].booksOut.length; j++) {
console.log(patrons[i].booksOut[j].title);
}
console.log(patrons[i].firstName + " has fine amount: $" + patrons[i].fine);
}
May be this is gonna help
Keep in mind that they are asking for 3 months period or 90 days period. we can't just iterate 90 times. Eventually you will be ended up in the same month. For an example: if today is 10/19/2017 that means this program should calculate until 01/19/2018 (3 months period)
//***************** library.js *****************//
/**
Create a constructor function for a Book object. The Book object should have the following properties:
Title: string
Available: Boolean representing whether the book is checked out or not. The initial value should be false.
Publication Date: Use a date object
Checkout Date: Use a date object
Call Number: Make one up
Authors: Should be an array of Author objects
**/
var Book = function(title, available, publicationDate, checkOutDate, callNumber, authors) {
this.title = title;
this.available = available;
this.publicationDate = publicationDate;
this.checkOutDate = checkOutDate;
this.callNumber = callNumber;
this.authors = authors;
}
/**
Create a constructor function for an object called Author. It should have a property for the
first name: string
last name: string
**/
var Author = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
/**
Create a constructor function for an object called Patron. This represents a person who is allowed to check out books from the library. Give it the following properties:
Firstname: string
Lastname: string
Library Card Number (Make one up): string
Books Out (make it an array): []
fine (Starts a 0.00): parseFloat(0.00)
**/
var Patron = function(firstName, lastName, libraryCardNumber, booksOut, fine) {
this.firstName = firstName;
this.lastName = lastName;
this.libraryCardNumber = libraryCardNumber;
this.booksOut = booksOut;
this.fine = parseFloat(fine) || 0.00;
}
// Methods
/**
Add a function to the Book prototype called "checkOut". The function will change the available property of the book from true to false and set the checkout date. The checkout date should be set to the current date minus some random number of days between 1 and 5. This will allow us to simulate books being overdue.
**/
Book.prototype.checkOut = function(date) {
this.available = false;
this.checkOutDate = new Date(date.setDate(date.getDate() - Math.floor((Math.random() * 5) + 1)));
}
/**
Add a function to the Book prototype called "checkIn". The function will change the available property of the book from false to true.
**/
Book.prototype.checkIn = function() {
this.available = true;
}
/**
Add a function called isOverdue that checks the current date and the checked out date and if it's greater than 5 days it returns true
**/
Book.prototype.isOverdue = function(date) {
var time = date.getTime();
var checkOutDate = (this.checkOutDate).getTime();
var timeDiff = Math.abs(time - checkOutDate);
var dayDiff = Math.ceil(timeDiff/ (1000 * 3600 * 24));
if (dayDiff > 5) {
return true;
} else {
return false;
}
}
/**
Add a function called isCheckedOut that returns true if the book is checked out to someone and false if not.
**/
Book.prototype.isCheckedOut = function() {
if (this.available === true) {
return false;
} else {
return true;
}
}
/**
Add a function to the Patron prototype called "read" that adds a book to it's books out property.
**/
Patron.prototype.read = function(book) {
this.booksOut.push(book);
}
/**
Add a function to the Patron prototype called "return" that removes a book from it's books out property.
**/
Patron.prototype.return = function(book) {
var booksOut = this.booksOut;
booksOut.forEach(function(existingBook, index) {
if (existingBook.title === book.title) {
booksOut.splice(index, 1);
}
});
}
/**
Create 5 different books from the Book Class and store them in an array called catalog.
**/
var catalog = [];
catalog[0] = new Book('Another Brooklyn', false, new Date(2017, 5, 30), new Date(), '201-360-9955', [new Author('Jacqueline', 'Woodson')]);
catalog[1] = new Book('Sunshine State', false, new Date(2017, 4, 11), new Date(), '888-888-888', [new Author('Sarah', 'Gerard')]);
catalog[2] = new Book('Homegoing', false, new Date(2017, 5, 2), new Date(), '877-990-3321', [new Author('Yaa', 'Gyasi')]);
catalog[3] = new Book('Round Midnight', false, new Date(2015, 11, 22), new Date(), '321-221-2100', [new Author('Laura', 'McBride')]);
catalog[4] = new Book('The Sport of Kings', false, new Date('08/22/2016'), new Date(), '813-289-0007', [new Author('C.E.', 'Morgan')]);
/**
Create 5 different patrons from the Patron Class and store them in an array called patrons.
**/
var patrons = [];
patrons[0] = new Patron('Bhaumik', 'Mehta', 'Lib-229781', [catalog[0]], 0.00);
patrons[1] = new Patron('Jeff', 'Reddings', 'Lib-337895', [catalog[1]], 0.00);
patrons[2] = new Patron('Andrew', 'Hansing', 'Lib-227896', [catalog[2]], 0.00);
patrons[3] = new Patron('Dylan', 'Marks', 'Lib-672563', [catalog[3]], 0.00);
patrons[4] = new Patron('Roop', 'Kapur', 'Lib-008936', [catalog[4]], 0.00);
/**
Write a loop that simulates checkouts and checkins for a 3 month period. Every day iterate over each book in the catalog, and every person in the patrons array(Nested Loops). If the current book is not checked out and the current patron does not have a book already, then add it to the patrons list of books via the patrons read method and check it out by calling the books checkout method. If the book is checked out to the current person, then check if it is overdue, and if so, check it in using the books check in method, and return it using the patrons return method. When checking in, if the book is overdue then add a fine of $5.00 to the patron returning it. At the end of the 3 month period, display each patron, the books they have currently checked out and any fine they may have.
**/
var now = new Date();
var threeMonthsFromNow = new Date(now.setMonth(now.getMonth() + 3));
var timeDiff = Math.abs(threeMonthsFromNow.getTime() - new Date().getTime());
var days = Math.ceil(timeDiff/ (1000 * 3600 * 24));
for (var i = 0; i < days; i ++) {
var date = new Date(new Date().setDate(new Date().getDate() + i));
catalog.forEach(function(item, index) {
patrons.forEach(function(patron, index) {
if (item.isCheckedOut() === false) {
var booksOut = (patron.booksOut).filter(function(book) {
return book.title === item.title;
});
if (booksOut.length === 0) {
patron.read(item);
item.checkOut(date);
}
} else if (item.isCheckedOut() === true) {
var booksOut = (patron.booksOut).filter(function(book) {
return book.title === item.title;
});
if (booksOut.length > 0) {
var isOverdue = item.isOverdue(date);
if (isOverdue === true) {
item.checkIn();
patron.return(item);
patron.fine += parseFloat(5.00);
}
}
}
});
});
}
/**
Object with the method to execute the record list for patrons
**/
var records = {
patronInfo: function() {
patrons.forEach(function(patron, index) {
console.log('Patron name: ' + patron.firstName + ' ' + patron.lastName);
console.log('Patron library card number: ' + patron.libraryCardNumber);
console.log('List of book(s) checked out currently by the patron:');
if ((patron.booksOut).length === 0) {
console.log('NO BOOKS ARE CHECKED OUT CURRENTLY');
} else {
(patron.booksOut).forEach(function(book, index) {
console.log((index + 1) + '. | ' + book.title + ' | Checked out on: ' + (book.checkOutDate).toDateString());
});
}
console.log('Patron ' + patron.firstName + ' ' + patron.lastName + ' has fine amout(till date): $' + (patron.fine).toFixed(2));
console.log('-----------------------------------------------------');
});
}
};
/**
Method execution
**/
records.patronInfo();
I am working on a jquery form file that uses an array to populate a dropdown of sizes based on the selection of the material. This works fine but now we are adding an additional dropdown and we need to have different values in the array based on that selection.
this is the part of the current working code:
var Material = new Array();
Material['UNC'] = new Array('40', '32');
Material['UNF'] = new Array('10', '24');
This is basically what I am trying to, not sure how:
if($("#style").val() == "long") { var Material = new Array();
Material['UNC'] = new Array('45', '35');
Material['UNF'] = new Array('15', '29');} else {
var Material = new Array();
Material['UNC'] = new Array('40', '32');
Material['UNF'] = new Array('10', '24');}
I'm not having any luck, I'm not super familiar with Javascript functions. Thanks
One way:
var isLong = $('#style').val() === 'long';
var material = {};
material.UNC = (isLong) ? [45, 35] : [40, 32];
material.UNF = (isLong) ? [15, 29] : [10, 24];
Another way:
var isLong = $('#style').val() === 'long';
var material = {};
if (isLong) {
material.UNC = [45, 35];
material.UNF = [15, 29];
}
else {
material.UNC = [40, 32];
material.UNF = [10, 24];
}
As Felix Kling points out, it is better to use an object over an array for material. I've also used JavaScript convention of a lowercase variable name. Instead of using new Array use [] and instead of new Object, you can use {}.
You just need to move the declaration of the Material variable outside the blocks:
var Material = new Array();
if($("#style").val() == "long") {
Material['UNC'] = new Array('45', '35');
Material['UNF'] = new Array('15', '29');
} else {
Material['UNC'] = new Array('40', '32');
Material['UNF'] = new Array('10', '24');
}
However, as others have pointed out, you should be using an object rather than an array for these kinds of non-numeric indexes. You should also use object and array notation:
var Material = {};
if($("#style").val() == "long") {
Material['UNC'] = ['45', '35'];
Material['UNF'] = ['15', '29'];
} else {
Material['UNC'] = ['40', '32'];
Material['UNF'] = ['10', '24'];
}