Im writing a simple function in Google Spreadsheets.
I want to input two ranges in the argument something like this:
=EctsPartial(C3:C4, E3:E4)
For the following function I wrote:
function EctsPartial(rangeA, rangeB) {
Logger.log(rangeA+" "+rangeB);
var noten = SpreadsheetApp.getActiveSheet().getRange(rangeA).getValues();
var ects = SpreadsheetApp.getActiveSheet().getRange(rangeB).getValues();
for(var i=0; i < SpreadsheetApp.getActiveSheet().getRange(rangeB).getHeight(); i++){
if(noten[i] != "" && noten[i] != 5) {
summe = summe - 0;
ects[i] = ects[i] - 0;
summe = summe + ects[i];
}
Logger.log(i+":");
Logger.log(summe);
}
return summe;
};
But the program keeps telling me that the argument of getRange() is not correct. If I manually type "C3:C4" (including the ") it works but otherwise it doesn't.
What am I doing wrong?
I think this is what you are trying to do. This is for custom spreadsheet functions.
In spreadsheet, the following code allows you to type =EctsPartial(C1) instead of =EctsPartial("C1"). If you put return noten on the script, it will get the value of C1
function EctsPartial(rangeA, rangeB) {
if (rangeA.map) {
return rangeA.map(EctsPartial);
} else {
var noten = rangeA;
}
}
https://developers.google.com/apps-script/guides/sheets/functions#optimization
A couple of options include:
1.
=EctsPartial("C3:C4"; "E3:E4")
.gs:
function EctsPartial(rangeA, rangeB) {
var noten = SpreadsheetApp.getActiveSheet().getRange(rangeA).getValues();
var ects = SpreadsheetApp.getActiveSheet().getRange(rangeB).getValues();
var sum = 0;
noten.forEach(function(value) {
sum += value[0];
});
ects.forEach(function(value) {
sum += value[0];
});
return sum;
}
2.
=EctsPartial(C3:C4; E3:E4)
.gs:
function EctsPartial(rangeA, rangeB) {
var sum = 0;
rangeA.forEach(function(value) {
sum += value[0];
});
rangeB.forEach(function(value) {
sum += value[0];
});
return sum;
}
Related
I set an array
let A1 = []
then create a function to populate the array
function popA1() {
let x = new Array(v1, v2, v3, vn);
return A1 = x;
}
function basicTableGenerator(myArray) {
let result = "<table><thead><th>Table</th></thead>";
for(let i=0; i<myArray.length; i++) {
result += "<tr><td>" + myArray[i] + "</td></tr>";
}
result += </table>";
return result;
}
and that function is called by a button click
button.addEventListener("click", () => {
if (A1.length == 0) {
popA1();
}
generateTableFromA1(A1);
}
It works well and all, except for the tiny fact that generateTableFromA1() won't work until the second time I click that button, how do I get it to work the first time?
function popA1() {
let x = new Array(v1, v2, v3, vn);
A1 = x;
}
try to remove return
I'm going through Codecademy's lesson on building a Blackjack game with Javascript.
I'm having trouble coming up with code to put in the for-loop. I'm supposed to write a "score" method in the Hand constructor. It should loop over all of the cards in the Hand, summing up the result of the "getValue" call to each and return that sum.
Can someone help me out please? Thank You.
Here's my attempt, the relevant code is inside the for-loop at the bottom:
// Card Constructor
function Card(s, n) {
var suit = s;
var number = n;
this.getSuit = function() {
return suit;
};
this.getNumber = function() {
return number;
};
this.getValue = function() {
if (number >= 10) {
return 10;
} else if (number === 1) {
return 11;
} else {
return number;
}
};
};
//deal function
var deal = function() {
var randNum = Math.floor(Math.random() * 13) + 1;
var randSuit = Math.floor(Math.random() * 4) + 1;
console.log(randNum, randSuit);
return new Card(randSuit, randNum);
};
function Hand() {
var handArray = [];
handArray[0] = deal();
handArray[1] = deal();
this.getHand = function() {
return handArray;
};
this.score = function() {
var sum;
for (var i = 0; i < handArray; i++) {
sum += handArray[i].getValue;
return sum;
}
};
};
Well something like this should work :
this.score = function() {
return handArray.reduce( function( memo, val){
return memo + val.getValue();
});
};
I think you need to return the score, outside of the loop, like so:
this.score = function() {
var sum;
for (var i = 0; i < handArray; i++) {
sum += handArray[i].getValue();
}
return sum;
};
This fixed it. Thanks for your help!
this.score = function(){
var sum =0;
for(var i =0; i<handArray.length; i++){
sum += handArray[i].getValue();
};
return sum;
};
I can easily understand how it works in C#, but in Javascript I'm a little bit confused. Here is a little test code I wrote:
function Lunch(name,price)
{
var priceChanging = [], priceChanged = [];
this.name = function(val)
{
return name;
}
this.price = function(val)
{
if(val !== price && val !== undefined )
{
for(var i = 0; i < priceChanging.length; i++)
{
if(!priceChanging[i](this,val))
{
return price;
}
}
price = val;
for(var i = 0; i < priceChanged.length; i++)
{
priceChanged[i](this);
}
}
return price;
}
this.OnPriceChanging = function(val)
{
priceChanging.push(val);
}
this.OnPriceChanged = function(val)
{
priceChanged.push(val);
}
}
var order = new Lunch("Fish and Chips",20);
console.log(order.name());
console.log(order.price());
order.OnPriceChanging(function(name,price)
{
if(price > 30)
{
console.log("Price too high");
return false;
}
return true;
});
order.OnPriceChanged(function(name)
{
console.log("Price changed to: $" + name.price());
});
It runs fine, the thing is I want to be able to explain it to myself. I'm not in front of a debugger and just used Notepad at the moment. I just thought of it like .NET where subscribers are put in a container, I'm just curious how it works in Javascript.
Does the OnPriceChanging and OnPriceChanged function call themselves automatically anytime you add/change the price? I guess I'm just uncomfortable with how Javascript is loosely typed and all.
As always, I'm very thankful for all the imparted knowledge.
It's really quite simple. You have two arrays that store functions:
var priceChanging = [], priceChanged = [];
You have two methods that push functions into the arrays:
this.OnPriceChanging = function(val)
{
priceChanging.push(val);
}
this.OnPriceChanged = function(val)
{
priceChanged.push(val);
}
You then push functions into the arrays:
order.OnPriceChanging(function(name,price)
{
if(price > 30)
{
console.log("Price too high");
return false;
}
return true;
});
order.OnPriceChanged(function(name)
{
console.log("Price changed to: $" + name.price());
});
Note that the code above may be confusing if you're not used to seeing anonymous functions. They are exactly equivalent to this:
function priceChangingCallback (name,price)
{
if(price > 30)
{
console.log("Price too high");
return false;
}
return true;
}
function priceChangedCallback (name)
{
console.log("Price changed to: $" + name.price());
})
order.OnPriceChanging(priceChangingCallback);
order.OnPriceChanged(priceChangedCallback);
So you see, the arrays priceChanging and priceChanged should now both contain a single function each.
Does the OnPriceChanging and OnPriceChanged function call themselves automatically anytime you add/change the price?
No, they do not. In fact to be precise, it's not OnPriceChanging and OnPriceChanged that are called. It's functions inside the arrays priceChanging and priceChanged. And they don't call themselves. You called them:
this.price = function(val)
{
if(val !== price && val !== undefined )
{
for(var i = 0; i < priceChanging.length; i++)
{
if(!priceChanging[i](this,val)) // <--- you're calling it here!!
{
return price;
}
}
price = val;
for(var i = 0; i < priceChanged.length; i++)
{
priceChanged[i](this); // <-- you're calling it here!!
}
}
return price;
}
You're calling all functions inside priceChanging and priceChanged arrays inside for loops. Functions that you added to the arrays using the OnPriceChanging and OnPriceChanged methods.
I am working on a project that needs an excel like calculation engine in the browser. But, it doesn't need the grid UI.
Currently, I am able to do it by hiding the 'div' element of Handsontable. But, it isn't elegant. It is also a bit slow.
Is there a client side spreadsheet calculation library in javascript that does something like this?
x = [ [1, 2, "=A1+B1"],
[2, "=SUM(A1,A2"),3] ];
y = CalculateJS(x);
##############
y: [[1, 2, 3],
[2,3,3]]
I'm not aware of any (although I haven't really looked), but if you wish to implement your own, you could do something along these lines (heavily unoptimized, no error checking):
functions = {
SUM: function(args) {
var result = 0;
for (var i = 0; i < args.length; i++) {
result += parseInt(args[i]);
}
return result;
}
};
function get_cell(position) {
// This function returns the value of a cell at `position`
}
function parse_cell(position) {
cell = get_cell(position);
if (cell.length < 1 || cell[0] !== '=')
return cell;
return parse_token(cell.slice(1));
}
function parse_token(tok) {
tok = tok.trim();
if (tok.indexOf("(") < 0)
return parse_cell(tok);
var name = tok.slice(0, tok.indexOf("("));
if (!(name in functions)) {
return 0; // something better than this?
}
var arguments_tok = tok.slice(tok.indexOf("(") + 1);
var arguments = [];
while (true) {
var arg_end = arguments_tok.indexOf(",");
if (arg_end < 0) {
arg_end = arguments_tok.lastIndexOf(")");
if (arg_end < 0)
break;
}
if (arguments_tok.indexOf("(") >= 0 && (arguments_tok.indexOf("(") < arg_end)) {
var paren_amt = 1;
arg_end = arguments_tok.indexOf("(") + 1;
var end_tok = arguments_tok.slice(arguments_tok.indexOf("(") + 1);
while (true) {
if (paren_amt < 1) {
var last_index = end_tok.indexOf(",");
if (last_index < 0)
last_index = end_tok.indexOf(")");
arg_end += last_index;
end_tok = end_tok.slice(last_index);
break;
}
if (end_tok.indexOf("(") > 0 && (end_tok.indexOf("(") < end_tok.indexOf(")"))) {
paren_amt++;
arg_end += end_tok.indexOf("(") + 1;
end_tok = end_tok.slice(end_tok.indexOf("(") + 1);
} else {
arg_end += end_tok.indexOf(")") + 1;
end_tok = end_tok.slice(end_tok.indexOf(")") + 1);
paren_amt--;
}
}
}
arguments.push(parse_token(arguments_tok.slice(0, arg_end)));
arguments_tok = arguments_tok.slice(arg_end + 1);
}
return functions[name](arguments);
}
Hopefully this will give you a starting point!
To test in your browser, set get_cell to function get_cell(x) {return x;}, and then run parse_cell("=SUM(5,SUM(1,7,SUM(8,111)),7,8)"). It should result in 147 :)
I managed to do this using bacon.js. It accounts for cell interdependencies. As of now, it calculates values for javascript formula instead of excel formula by using an eval function. To make it work for excel formulae, all one has to do is replace eval with Handsontable's ruleJS library. I couldn't find a URI for that library... hence eval.
https://jsfiddle.net/sandeep_muthangi/3src81n3/56/
var mx = [[1, 2, "A1+A2"],
[2, "A2", "A3"]];
var output_reference_bus = {};
var re = /\$?[A-N]{1,2}\$?[1-9]{1,4}/ig
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('');
function convertToCellRef(rows, cols) {
var alphabet_index = rows+1,
abet = "";
while (alphabet_index>0) {
abet = alphabet[alphabet_index%alphabet.length-1]+abet;
alphabet_index = Math.floor(alphabet_index/alphabet.length);
}
return abet+(cols+1).toString();
}
function getAllReferences(value) {
if (typeof value != "string")
return null;
var references = value.match(re)
if (references.length == 0)
return null;
return references;
}
function replaceReferences(equation, args) {
var index = 0;
return equation.replace(re, function(match, x, string) {
return args[index++];
});
}
//Assign an output bus to each cell
mx.forEach(function(row, row_index) {
row.forEach(function(cell, cell_index) {
output_reference_bus[convertToCellRef(row_index, cell_index)] = Bacon.Bus();
})
})
//assign input buses based on cell references... and calculate the result when there is a value on all input buses
mx.forEach(function(row, row_index) {
row.forEach(function(cell, cell_index) {
if ((all_refs = getAllReferences(cell)) != null) {
var result = Bacon.combineAsArray(output_reference_bus[all_refs[0]]);
for (i=1; i<all_refs.length; i++) {
result = Bacon.combineAsArray(result, output_reference_bus[all_refs[i]]);
}
result = result.map(function(data) {
return eval(replaceReferences(cell, data));
})
result.onValue(function(data) {
console.log(convertToCellRef(row_index, cell_index), data);
output_reference_bus[convertToCellRef(row_index, cell_index)].push(data);
});
}
else {
if (typeof cell != "string")
output_reference_bus[convertToCellRef(row_index, cell_index)].push(cell);
else
output_reference_bus[convertToCellRef(row_index, cell_index)].push(eval(cell));
}
})
})
output_reference_bus["A2"].push(20);
output_reference_bus["A1"].push(1);
output_reference_bus["A1"].push(50);
I have a function that performs a Luhn check on a card entry when a form is posted.
<script language="javascript">
function Calculate(Luhn)
{
var sum = 0;
for (i=0; i<Luhn.length; i++ )
{
sum += parseInt(Luhn.substring(i,i+1));
}
var delta = new Array (0,1,2,3,4,-4,-3,-2,-1,0);
for (i=Luhn.length-1; i>=0; i-=2 )
{
var deltaIndex = parseInt(Luhn.substring(i,i+1));
var deltaValue = delta[deltaIndex];
sum += deltaValue;
}
var mod10 = sum % 10;
mod10 = 10 - mod10;
if (mod10==10)
{
mod10=0;
}
return mod10;
}
function Validate(Luhn)
{
var LuhnDigit = parseInt(Luhn.substring(Luhn.length-1,Luhn.length));
var LuhnLess = Luhn.substring(0,Luhn.length-1);
if (Calculate(LuhnLess)==parseInt(LuhnDigit))
{
return true;
}
alert("\n\nYou have mis-typed your card number! \nPlease check and correct.\n\n")
return false;
}
I also have a function that removes any spaces that may have been entered in the card number onblur.
function stripChar(sValue, sChar) {
var i, tempChar, buildString;
buildString = ""
for (var i=0; i<sValue.length; i++) {
tempChar = sValue.charAt(i);
if (tempChar != sChar) {
buildString = buildString + tempChar;
}
}
return buildString;
How do I combine the functions so that the spaces are removed and the card number checked onblur.
In your onblur function you could use:
Validate(stripChar(sValue, sChar));