I have an assignment in which I need to make two arrays (NAME and SALES). I need to populate the array of up to 100 components. Then, I need to calculate gross pay using a calcPay() function. I am having trouble figuring out how to get the function to work, it either prints the resulting table with the Pay column as 'undeclared', or it just stops working when it comes to that spot, no matter how many NAMES and SALES are entered into the array. I have this in the body script:
var i=0;
var NAME = new Array();
var SALES = new Array();
do
{
NAME[i]=getName();
SALES[i]=getSales();
i++;
var again=confirm("Would you like to enter another salesperson's stats?");
}while(again && i<=100);
var i=0;
for (var i=0;i<NAME.length;i++)
{
printRow();
}
And this is the header:
function getName()
{
do
{
var name=prompt("What is the salesperson's full name?");
}while (name==""||name==null);
return name;
}
function getSales()
{
do
{
var sales=prompt("Please enter salesperson's sales.");
}while(sales==""||isNaN(sales));
return parseFloat(sales);
}
calcPay(sales)
{
var pay=sales*.1+1000;
return pay;
}
function printRow()
{
document.write("<tr>");
document.write("<td>"+NAME[i]+"</td>");
document.write("<td>"+SALES[i]+"</td>");
var payment=calcPay(SALES[i]);
document.write("<td>"+payment+"</td>");
document.write("</tr>");
}
This is not the full extent of the assignment by any means, I just want to make sure that I have a handle on the feeding and manipulating of the arrays (which I don't, obviously).
Thanks for any tips.
Generally - your code works, find it here:
http://jsfiddle.net/osher/GhZSf/
However -
there is a missing "function" before calcPay
calcPay(sales)
{
var pay=sales*.1+1000;
return pay;
}
should be
function calcPay(sales)
{
var pay=sales*.1+1000;
return pay;
}
that's all
name and sales are out of scope this function will not do what you think it will and even if it does it is wrong.
Use an if statement .
function getName()
{
do
{
var name=prompt("What is the salesperson's full name?");
}while (name==""||name==null);
return name;
}
function getSales()
{
do
{
var sales=prompt("Please enter salesperson's sales.");
}while(sales==""||isNaN(sales));
return parseFloat(sales);
}
Related
I am very very new to vanilla JS and I am currently working on an assignment that allows us freedom to create whatever kind of website we want on the basis that we break all of the code down into functions and that we do not use global variables.
I have almost completed a very basic address book that takes a name and phone number as input, stores the values into an array and returns the list of contacts when the 'view all contacts' button is pressed. My only dilemma is that the array is a global variable and I am not sure how to get this to work other wise.
Update
I have added the entire code here. I am working in Brackets.
function init(){
document.getElementById("addButton").addEventListener("click", addContact)
document.getElementById("listButton").addEventListener("click", listContacts)
}
var contactList = [];
function getAttribute(id){
return document.getElementById(id).value;
}
function verifyNumber(number){
var regExp = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
var phone = number.match(regExp);
if (phone) {
outPut("alert","");
return true;
}else{
outPut("alert","invalid phone number");
return false;
}
}
function outPut(id,message){
var messageBox = document.getElementById(id);
messageBox.innerText = message;
}
function contact(name, number){
this.name = name;
this.number = number;
return ("Name: " + this.name + "Number: " + this.number);
}
function addContact(){
var name = getAttribute("name");
var number = getAttribute("number");
if(verifyNumber(number)){
var newContact = new contact(name,number);
contactList.push(newContact);
alert("Contact Successfuly Added!");
}
}
function listContacts(){
contactList.forEach(element => console.log(element));
contactList.forEach(Object => {var newElement = document.createElement('h1');
newElement.innerHTML = JSON.stringify(Object);
document.body.appendChild(newElement);
});
}```
Maybe use an object instead? like key: value pairs, and could you share a snippet of your code with us so we can help you better, thanks
I need help with my sort() method from inside the AddressBook class. I tried to figure it out on my own from examples on stackoverflow but I can't seem to get it to work since most of the examples don't involve working from a class instance. If you could please look at the sort() method and let me know where I am going wrong. I think i need to loop through somehow and then reposition the array order.
window.onload = init;
let abm;
function init() {
abm = new AddressBook();
}
class Contact {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
//DO NOT MODIFY ABOVE THIS LINE
function formSubmitted() {
event.preventDefault();
var user = document.getElementById("name").value;
var mail = document.getElementById("email").value;
var newContact = new Contact(user, mail);
abm.add(newContact);
abm.display();
}
function sortList() {
//CODE HERE ONLY
abm.sort();
abm.display();
}
class AddressBook {
constructor() {
this.contactList = [];
}
add(contact) {
//CODE HERE ONLY
this.contactList.push(contact);
}
display(htmlId) {
//CODE HERE ONLY
var html = "<table border='1|1'>";
for (var i = 0; i < this.contactList.length; i++){
html+="<tr>";
html+="<td>"+this.contactList[i].name+"</td>";
html+="<td>"+this.contactList[i].email+"</td>";
html+="</tr>";
}
html+="</table>";
document.getElementById("contacts").innerHTML = html;
}
sort() {
//CODE HERE ONLY
for (var i = 0; i < this.contactList.length; i++){
var tA = this.contactList[i].name.toUpperCase();
var tB = this.contactList[i].name.toUpperCase();
if (tA < tB) {
return -1;
}
if (tA > tB) {
return 1;
}
return 0;
}
}
}
this.contactList.sort((a, b) => a.name.toUpperCase() - b.name.toUpperCase());
You can learn more at Mozilla Developers
I assume you want to sort this.contactList in-place.
Note that you do not perform any assignment to this.contactList in your sort() code. This is the first bug.
The second bug is that you return a value from the function immediately, instead of sorting your data.
The third bug is, that you cannot sort in O(N) complexity (i.e. with a single pass on the data).
You need to decide which sorting algorithm you want to implement, or use the the native javascript implementation which is MergeSort.
In this case, you'd need to pass a function to express how and using which properties you want to sort your data, which is kind of what you tried to do, using -1, 1, and 0.
In this case, you can implement sort() in the following way:
sort() {
this.contactList = this.contactList.sort(function(a, b) {
var tA = this.contactList[i].name.toUpperCase();
var tB = this.contactList[i].name.toUpperCase();
if (tA < tB) {
return -1;
}
else if (tA > tB) {
return 1;
}
return 0;
}
}
Or in an equivalent way (make sure you understand why it's equivalent):
sort() {
this.contactList = this.contactList.sort(function(a, b) {
return a.name.toUpperCase() - b.name.toUpperCase();
}
}
Here is what I'm trying to do. I'm trying to pass an instance of order to bill, where it would be indexed. The thing is that it's not working.
Am I stretching JS too thin here?
Any example on how to do this, or some reading material?
EDIT: Maybe I should add that this is supposed to be the user interface for a POS (Point of Sale) system. It should accept the order of products (each one with variable quantity), and process in the client's side the subtotal, total and number of items in the bill.
EDIT2: Not native english speaker. Maybe the names that I choose did not best suited the problem.
function Bill (prefix,maxForms,minForms) {
this.prefix = prefix;
this.maxForms = maxForms;
this.minForms = minForms;
this.items = [];
this.total = 0;
this.addOrder = function(order) {
if (this.items.length == 0)
{
this.items.push(order);
}
for (i=0;i<this.items.length;i++){
if (this.items[i].name === order.name) {
this.items[i].quantity = order.quantity;
this.items[i].price = order.price;
}
else {
this.items.push(order);
}
this.total = this.total + order.getSubTotal();
}
}
}
function Order (name,price,quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
this.getSubtotal = function () {
return this.price*this.quantity;
}
this.changeQuantity = function (newQuantity) {
this.quantity = newQuantity;
}
this.incrementQuantity = function () {
this.quantity = this.quantity + 1;
}
}
Here's an issue:
for (i = 0;/*...*/)
I would suggest you spend a little more time in JS.
It does look a lot like C / Java / C# / PHP, etc...
The problem, however, is that JS does not have any notion of block scope*.
* until ES6, that is
It only deals with function scope.
That is, a variable has the same reference through the whole function where it's defined (via var).
If a variable is not defined via var, the function goes up to its parent to find the value of the variable, and up from there, and up from there, until it hits window.<varname>.
You might actually be modifying window.i in your class' instance.
function Bill ( ) {
var bill = this,
i = 0;
for (i=0; /* ... */) { /*...*/ }
}
That said, you might do to spend time getting to know JS.
Most of what you've written looks absolutely fine, in English, as well.
I might break it down a little further:
function Bill () {
var bill = this;
extend(bill, {
total : 0,
items : [],
addOrder : function (order) {
var match = bill.findOrder(order.name);
if (!match) { bill.items.push(order); }
else { bill.updateOrder(match, order); }
bill.updateTotal();
},
findOrder : function (name) {
var matches = bill.items.filter(function (order) {
return order.name === name;
});
return matches[0];
},
updateOrder : function (current, updated) {
/* I don't know if you want to replace the old order, or add to it... */
/* so I'm "replacing" it, instead of increasing quantity, like you did */
current.quantity = updated.quantity;
current.price = updated.price;
},
updateTotal : function () {
bill.total = bill.items
.map(function (order) { return order.getSubtotal(); })
.reduce(function (tally, price) { return tally + price; }, 0);
}
});
}
var bill = new Bill();
bill.addOrder(new Order(/*...*/));
I'm doing a few things differently, here.
First, extend isn't a "built-in" function; there are a lot of implementations, in all sorts of libraries, but basically, it just saves me from writing bill.x = x; bill.y = y; bill.z = z;..., and use an object, instead.
Next, I'm using var bill = this;
and bill.method = function () { bill.total = /*...*/; };
instead of this.method = function () { };, because once you go two levels down, in functions, this no longer means the object you think it does.
this.method = function () {
this.async(function (response) {
// unless you change it yourself, `this` probably means `window`
this.value = response; // oops
});
};
// instead, try
var thing = this;
thing.method = function () {
thing.async(function (response) {
thing.value = response;
});
};
Of course, you can always mix and match, as long as you know how far down you can go (one level)...
...but that means you really, really need to care about using this a whole lot.
var thing = this;
this.method = function () {
this.async(function (val) {
thing.value = val;
});
};
Much more confusing than just referring to the instance by a variable, rather than combining the two.
There are dozens of ways of doing this; some look very class-like, others might be 100% functional, and in ES6, you might just use classes altogether.
But there are some ideas, and some reasons behind doing them that way (especially if you don't know where the differences are in JS vs the other C-looking languages).
I don't think you're stretching JS too thin, at all.
Once all of the issues on line 80 are fixed. All you need to do is:
var order = new Order("My Order", 12, 2);
var bill = new Bill(blah, blah, blah);
bill.addOrder(order);
A few issues right off the bat:
this.total = this.total + order.subTotal();ยท
There is a garbage char at the end.
Order does not have a subtotal function. It should be getSubtotal.
The 2 assignments to this.items[i].quantity and this.items[i].price are superfluous, since you are assigning properties to themselves. Remember, this.items[i] === order. This is not a bug, but it is inefficient.
You should have something like this.total = 0; at the top of Bill.
I think you want:
this.items[i].quantity += order.quantity;
this.items[i].price += order.price;
This will update quantity with whatever quantity order has. Secondly, I see you have an order function. Not an order object. Was that intentional? Are you planning to add instances of this bill/order object to each other? I don't think that's where you were going. Make sure they are separate objects that you are nesting.
Are you getting anything except undefined? I don't think you are because you're not returning anything.
Put:
return this;
at the end of your functions. Make sure you save them to a var when you make them:
bill = Bill(v,v,v);
order = Order(v,v,v);
then you can:
bill.addOrder(order);
See if that helps.
Background
I have a plain JS array, initially empty. I later populate it with values. The values sent to it are numbers that are Knockout observables. Later, I want to compare those values to values in another, knockout observable array. My problem is that whenever I pass the index of the current item in my array loop, and pass that index value (a number!), the array returns a function. To get an idea, look at the JS that follows.
Note that my project and actual script is viewable on JSBin. Further, to view the problem in the console, you have to add assignments, then press 'sort'.
JSBin: http://jsbin.com/fehoq/177/edit]1
JS
//example script that follows actual script
var _this = this;
//initialize my array
this. lowest = [];
// I want to compare values in lowest to values in this array
this.scores = ko.observableArray();
// method that does comparison
this.myMethod = function(){
// initialize my helper, k
var k;
...
// loop through one array
ko.utils.arrayForEach(_this.scores(), function (score) {
// make sure my value is a number...
if (!isNaN(parseFloat(score()))) {
// this is important, I need to current index for comparison
k = _this.scores.indexOf(score);
console.log(k);
// this is where things break - it prints a function, not a value!
console.log(_this.lowest[k]);
// useless check, the value is a function, so they're always different
if (score()!=_this.lowest[k]){
// do stuff
}
}
}
}
Update
Putting the method I'm using, maybe someone will notice something I missed given that my syntax is correct(?).
this.mean = (function(scores,i) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
console.log(i);
console.log(_this.lowest[i]);
if (score() != _this.lowest[i]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
}
Update 2
Just in case someone else wanders over here since my problem isn't solve still. The following code is how I set the value of lowest:
this.dropLowestScores = function() {
ko.utils.arrayForEach(_this.students(), function(student){
var comparator = function(a,b){
if(a()<b()){
return 1;
} else if(a() > b()){
return -1;
} else {
return 0;
}
};
var tmp = student.scores().slice(0);
tmp.sort(comparator);
student.lowest = ko.observableArray(tmp.splice((tmp.length-2),tmp.length-1));
});
};
Outstanding Questions, 5/9/2014
Jeremy's script runs but without the desired effects. For example, console.log(_this.lowest[k]) prints undefined, just as mine does. Further, the matched scores aren't skipped, which they should be.
Jeremy's script specifies lowest as a ko.observable. My script also now has lowest as a ko.observable, but why shouldn't a plain JS array work for this? I only need lowest to update when the button it's bound to is clicked, and those bindings are already taken care of.
That is how observables work in Knockout.
When you create one, you are creating a function.
var myObservable1 = ko.observable(); // Create it.
var myObservable2 = ko.observable("Hola!"); // Create it with a value.
console.log(typeof myObservable2); // It is indeed a function
console.log(typeof myObservable2()); // That returns a string
console.log(myObservable2()); // And get the value.
EDIT BASED ON QUESTION IN COMMENTS
var koTest = ko.observableArray();
koTest.push("Line0");
koTest.push("Line1");
koTest.push("Line2");
koTest.push("Line3");
koTest.push("Line4");
var jsTest = [];
jsTest.push("Line0");
jsTest.push("Line1");
jsTest.push("Line2");
jsTest.push("Line3");
jsTest.push("Line4");
alert(koTest()[2]);
alert(jsTest[2]);
alert(koTest()[2] === jsTest[2]);
Test Code
I went ahead and make a runnable test of your code and everything was working just fine for me. I had to make some assumptions about the contents of _this -- in particular the declaration of lowest, which I made an observableArray based on how you were accessing it.
Anyways, this code runs:
var _this = {
scores: ko.observableArray(),
lowest: ko.observableArray()
};
var mean = (function(scores) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
k = _this.scores.indexOf(score);
console.log(k);
console.log(_this.lowest[k]);
if (score() != _this.lowest[k]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
for (var i = 0; i < 10; i++) {
_this.scores.push(ko.observable(i));
}
var m = mean();
alert(m);
I will give you a sample example of my problem to remove the logical complexity and let you be focus on the important part. Of course, this example will be a bit useless...
I have a tree structure where node are like that
{
path: "...",
childs : []
}
Now, I have to write all the full paths from root to each leaf in an array.
My design is very poor:
function listPaths(node) {
var result = [];
function listForNode(n, parentFullPath) {
var thisPath = parentFullPath + "/" + n.path;
result.push(thisPath);
n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
listForNode(node, "");
return result;
}
It could be nice but I can't write the test with Mocha without having an insane 600 line code test file. At this moment, you should be asking why. The reason is the complexity of the real purpose, that's not relevant for my question. My goal is to having something 'mockable' cause I'm used to. (Java dev). But I fail.
Do you have any pattern that I can use to resolve this one? I'm not really good at JS patterns. :/
Visitor? Making an Y Combinator? So many possibility...
Thank you for reading me
You need to remember that functions are first class citizens in javascript.
I see that essentially what you have is something like
function createVisitor(parentsAccumulatorInitialValue, parentsAccumulator){
var visitor = function myVisitor (node) {
var result;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
result.push(thisPath);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
result = [];
listForNode(node, parentsAccumulatorInitialValue());
return result;
}
return visitor;
}
var listPaths = createVisitor(
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
But that's not the only abstraction you could take care of:
function createVisitor2(
totalAccumulatorInitialValue,
totalAccumulator,
parentsAccumulatorInitialValue,
parentsAccumulator){
var visitor = function myVisitor (node) {
var total;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
total = totalAccumulator(total, thisPath, n);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
total = totalAccumulatorInitialValue();
listForNode(node, parentsAccumulatorInitialValue());
return total;
}
return visitor;
}
var listPaths2 = createVisitor2(
function totalInit() {
return [];
},
function totalAcc(total, thisPath, n){
total.push(thisPath);
return total;
},
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
Which might be pretty reasonable, but as you can see, I'm already beginning to have trouble finding appropriate names for these variables. In fact, I'd say the name of our function is bad, as doesn't create anything strictly like a visitor object I know of. However, it does work (BTW, I've slightly modified it to handle nulls as well as empty arrays):
> listPaths( { path:"foo",
childs: [{path:"bar", childs: null}, {path:"bob", childs: null}]})
["/foo", "/foo/bar", "/foo/bob"]
It can be modified even further so that your trees don't strictly even have the same structure... but we're already at 4 parameters, which isn't great. It'd be better if your visitor creator were passed a single extensible object with all the necessary methods or values. For instance, maybe (pseudocode):
function createVisitor3(opts) {
//assume we've defined GetDefaults() somewhere local to createVisitor3
// as well as assume that extend is defined somewhere that copies properties
// into a new object like various previously existing libraries do.
opts = extend({}, GetDefaults(), opts);
var totalAccumulatorInitialValue = opts.totalAccumulatorInitialValue;
var totalAccumulator = opts.totalAccumulator;
var parentsAccumulatorInitialValue = opts.parentsAccumulatorInitialValue;
var parentsAccumulator = opts.parentsAccumulator;
var childrenGetter = opts.childrenGetter;
/// etc.
...
}