JavaScript cannot find method a second time? - javascript

I'm attempting to build a poker game. The method in question is very simple, and it works when it runs the first time.
This part isn't perfect convention because I'm just using it to test my methods:
var $ = function (id) { return document.getElementById(id); };
var test = function() {
var deck = new POKER.Deck();
var hand = new POKER.Hand();
for (var i = 0; i < 7; i++){
hand.addCard(deck.dealCard());
}
hand.sortByRank();
for (var j = 0; j < 7; j++){
var img = document.createElement("img");
var card = hand.getCardAtIndex(j); //** <------- WORKS HERE**
img.src = card.getImage();
$("images").appendChild(img);
}
var testHand = new POKER.Hand();
testHand = hand.removePairs();
for (var k = 0; k < testHand.length; k++) {
var img2 = document.createElement("img");
var card2 = testHand.getCardAtIndex(k); // **<------FAILS HERE**
img2.src = card2.getImage();
$("handImg").appendChild(img2);
}
};
window.onload = function() {
test();
};
The first and second loop work, and the hand is displayed and everything. When it gets to the last loop, the debugger tells me "TypeError: testHand.getCardAtIndex is not a function"
I was attempting to test the removePairs method (to test for straights more easily), and when watching the variables in the debugger, testHand clearly gets populated correctly. The method seems to work just fine.
getCardAtIndex:
POKER.Hand.prototype.getCardAtIndex = function(index) {
return this.cards[index];
};
removePairs:
POKER.Hand.prototype.removePairs = function(){
var allCards = this.cards;
var tempCards = [];
var uniqueRanks = [];
var unique;
for(var i = 0; i < allCards.length; i++){
unique = true;
for(var j = 0; j < uniqueRanks.length; j++){
if(allCards[i].getRank() == uniqueRanks[j]){
unique = false;
break;
}
}
if(unique){
uniqueRanks.push(allCards[i].getRank());
tempCards.push(allCards[i]);
}
}
return tempCards;
};
I'm completely perplexed.

var testHand = new POKER.Hand();
testHand = hand.removePairs();
hand.removePairs() returns an Array, not a Hand object.
That's why you don't have access to the getCardAtIndex method.
If cards is a public property you could do:
testHand.cards = hand.removePairs();
Or you can have a setter method:
testHand.setCards(hand.removePairs);

Related

Concat in a for loop google app scripts javascript

I'm trying to filter in an array by data in another array using concat in a for loop. The elements of the following code are logging correctly, but the final array is logging an empty array.
function Shipments (){
var app = SpreadsheetApp;
var movementSS = app.getActiveSpreadsheet();
var infoSheet = movementSS.getSheetByName("Update Info");
var orderInfoSheet = movementSS.getSheetByName("Order Info");
var targetSheet = movementSS.getSheetByName("Shipments");
var ShipLogSS = app.openByUrl(URL).getSheetByName("Shipping Details");
var ShipArr = ShipLogSS.getRange(3,1,ShipLogSS.getLastRow(),ShipLogSS.getLastColumn()).getValues().
filter(function(item){if(item[1]!=""){return true}}).
map(function(r){return [r[0],r[1],r[2],r[4],r[10],r[11],r[16],r[18],r[23]]});
var supplierData = orderInfoSheet.getRange(3,6,orderInfoSheet.getLastRow(),1).getValues().
filter(function(item){if(item[0]!=""){return true}});
var supplierList = [];
for (var i in supplierData) {
var row = supplierData[i];
var duplicate = false;
for (var j in supplierList) {
if (row.join() == supplierList[j].join()) {
duplicate = true;
}
}
if (!duplicate) {
supplierList.push(row);
}
}
var supplierFilter = [];
for(var i = 0; i < supplierList.length; i++){
var shipments = ShipArr.filter(function(item){if(item[4]===supplierList[i][0]){return true}});
supplierFilter.concat(shipments);
}
Logger.log(supplierFilter);
}
Any help would be greatly appreciated!
You need to assign the result of concat onto the supplierFilter in order to see the changes in later iterations and in the outer scope.
You can also return the comparison done inside the .filter callback immediately, instead of an if statement - it looks a bit cleaner.
var supplierFilter = [];
for (var i = 0; i < supplierList.length; i++) {
var shipments = ShipArr.filter(function (item) { return item[4] === supplierList[i][0]; });
supplierFilter = supplierFilter.concat(shipments);
}
Logger.log(supplierFilter);

How to set data from an array to another array to improve performance

I'm currently developing a sheet that shows results from a set of data based on some filters but the data loads to slowly when getting the results, I've tried to follow the Best Practices from Google Documentacion with no luck, how can I set an array for the data to load faster?
Below is the code commented with what I've already tried
function realizarBusqueda() {
var inicio = SpreadsheetApp.getActive().getSheetByName("INICIO");
var aux_tags = SpreadsheetApp.getActive().getSheetByName("Aux_Tags");
var data = SpreadsheetApp.getActive().getSheetByName("Data");
var data_lc = data.getLastColumn();
var data_lr = data.getLastRow();
var searchRange = data.getRange(2,1, data_lr, data_lc);
var inicio_lc = inicio.getLastColumn();
inicio.getRange("A8:L1000").clearContent();
inicio.getRange("A8:L1000").clearFormat();
var countRows = inicio.getMaxRows();
inicio.deleteRows(20, (20-countRows)*-1);
if (inicio.getRange("B4").isBlank()) {
inicio.getRange("A8:L1000").clearContent();
inicio.getRange("A8:L1000").clearFormat();
var countRows = inicio.getMaxRows();
inicio.deleteRows(20, (20-countRows)*-1);
SpreadsheetApp.flush();
}
else if ((inicio.getRange("B4").getValue() != "" &&
inicio.getRange("C4").getValue() === "")) {
//filtrado 1
var arrayDatos = searchRange.getValues();
var inicio_fr = 8;
//var row = new Array(11);
for (var j = 2; j <= data_lr; j++) {
//row[j] = new Array(data_lr);
if (aux_tags.getRange("P2").getValue() === arrayDatos[j-2][4]) {
var inicio_fc = 1;
for (var i = 0; i < arrayDatos[j-2].length; i++) {
//row[j][i] = arrayDatos[j-2][i];
var row = arrayDatos[j-2][i];
inicio.getRange(inicio_fr, inicio_fc).setValue(row);
inicio_fc++;
}
inicio_fr++;
}
//inicio.getRange("A8").setValues(row);
}
}
I expect the output to load lots faster, currently what I've tried is commented, the code as-is is working but too slow
I just wanted to update this subject because I figured out myself, see attached the new code with the use of new 2D arrays
...
//filtrado 1
var arrayDatos = searchRange.getValues();
var inicio_fr = 8;
var rows = [];
var row = [];
for (var j = 2; j <= data_lr; j++) {
if (aux_tags.getRange("P2").getValue() === arrayDatos[j-2][4]) {
var inicio_fc = 1;
for (var i = 0; i < arrayDatos[j-2].length; i++) {
row.push(arrayDatos[j-2][i]);
if (i == 11) {
rows.push(row);
row = [];
}
}
}
}
inicio.getRange(8, 1, rows.length, rows[0].length).setValues(rows);
}
Now instead of writing on row at a time, I just write the whole array at once

JS missing ) in parenthetical (line #88)

I'm writing a program in JS for checking equal angles in GeoGebra.
This is my first JS code, I used c# formerly for game programming.
The code is:
var names = ggbApplet.getAllObjectNames();
var lines = new Set();
var angles = new Set();
var groups = new Set();
for(var i=0; i<names.length; i++)
{
if(getObjectType(names[i].e)==="line")
{
lines.add(names[i]);
}
}
for(var i=0;i<lines.size;i++)
{
for(var j=0;j<i;j++)
{
var angle = new Angle(i,j);
angles.add(angle);
}
}
for(var i=0;i<angles.size;i++)
{
var thisVal = angles.get(i).value;
var placed = false;
for(var j=0;j<groups.size;j++)
{
if(groups.get(j).get(0).value===thisVal)
{
groups.get(j).add(angles.get(i));
placed = true;
}
}
if(!placed)
{
var newGroup = new Set();
newGroup.add(angles.get(i));
groups.add(newGroup);
}
}
for(var i=0;i<groups.size;i++)
{
var list="";
for(var j=0;j<groups.get(i).size;j++)
{
list = list+groups.get(i).get(j).name;
if(j != groups.get(i).size-1)
{
list = list+",";
}
}
var comm1 = "Checkbox[angle_{"+groups.get(i).get(0).value+"},{"+list+"}]";
ggbApplet.evalCommand(comm1);
var comm2 = "SetValue[angle_{"+groups.get(i).get(0).value+"}+,0]";
ggbApplet.evalCommand(comm2);
}
(function Angle (i, j)
{
this.lineA = lines.get(i);
this.lineB = lines.get(j);
this.name = "angleA_"+i+"B_"+j;
var comm3 = "angleA_"+i+"B_"+j+" = Angle["+this.lineA+","+this.lineB+"]";
ggbApplet.evalCommand(comm3);
var val = ggbApplet.getValue(this.name);
if(val>180)
{val = val-180}
this.value = val;
ggbApplet.setVisible(name,false)
});
function Set {
var elm;
this.elements=elm;
this.size=0;
}
Set.prototype.get = new function(index)
{
return this.elements[index];
}
Set.prototype.add = new function(object)
{
this.elements[this.size]=object;
this.size = this.size+1;
}
It turned out that GeoGebra does not recognize Sets so I tried to make a Set function.
Basically it collects all lines into a set, calculates the angles between them, groups them and makes checkboxes to trigger visuals.
the GeoGebra functions can be called via ggbApplet and the original Workspace commands via ggbApplet.evalCommand(String) and the Workspace commands I used are the basic Checkbox, SetValue and Angle commands.
The syntax for GeoGebra commands are:
Checkbox[ <Caption>, <List> ]
SetValue[ <Boolean|Checkbox>, <0|1> ]
Angle[ <Line>, <Line> ]
Thank you for your help!
In short, the syntax error you're running to is because of these lines of code:
function Set {
and after fixing this, new function(index) / new function(object) will also cause problems.
This isn't valid JS, you're likely looking for this:
function Set() {
this.elements = [];
this.size = 0;
}
Set.prototype.get = function(index) {
return this.elements[index];
};
Set.prototype.add = function(object) {
this.elements[this.size] = object;
this.size = this.size + 1;
};
Notice no new before each function as well.
I'm not sure what you're trying to accomplish by creating this Set object though - it looks like a wrapper for holding an array and its size, similar to how something might be implemented in C. In JavaScript, arrays can be mutated freely without worrying about memory.
Here's an untested refactor that removes the use of Set in favour of native JavaScript capabilities (mostly mutable arrays):
var names = ggbApplet.getAllObjectNames();
var lines = [];
var angles = [];
var groups = [];
for (var i = 0; i < names.length; i++) {
if (getObjectType(names[i].e) === "line") {
lines.push(names[i]);
}
}
for (var i = 0; i < lines.length; i++) {
for (var j = 0; j < i; j++) {
angles.push(new Angle(i, j));
}
}
for (var i = 0; i < angles.length; i++) {
var thisVal = angles[i].value;
var placed = false;
for (var j = 0; j < groups.length; j++) {
if (groups[j][0].value === thisVal) {
groups[j].push(angles[i]);
placed = true;
}
}
if (!placed) {
groups.push([angles[i]]);
}
}
for (var i = 0; i < groups.length; i++) {
var list = "";
for (var j = 0; j < groups[i].length; j++) {
list += groups[i][j].name;
if (j != groups[i].length - 1) {
list += ",";
}
}
var comm1 = "Checkbox[angle_{" + groups[i][0].value + "},{" + list + "}]";
ggbApplet.evalCommand(comm1);
var comm2 = "SetValue[angle_{" + groups[i][0].value + "}+,0]";
ggbApplet.evalCommand(comm2);
}
function Angle(i, j) {
this.name = "angleA_" + i + "B_" + j;
var comm3 = "angleA_" + i + "B_" + j + " = Angle[" + lines[i] + "," + lines[j] + "]";
ggbApplet.evalCommand(comm3);
var val = ggbApplet.getValue(this.name);
if (val > 180) {
val -= 180;
}
this.value = val;
ggbApplet.setVisible(name, false);
}
Hopefully this helps!
Your function definition is missing the parameter list after the function name.
Also, you're initializing the elements property to an undefined value. You need to initialize it to an empty array, so that the add method can set elements of it.
function Set() {
this.elements=[];
this.size=0;
}

For loop in Adobe ExtendScript

my for-loop in the "setEase" function won't increase "i"
function storeKeyframes(){
var properties = app.project.activeItem.selectedProperties;
var activeProperty = null;
var keySelection = null;
var curKey = null;
var curKeyTime = null;
var curKeyIndex = null;
var theEase = new KeyframeEase(0 , slider_1_slider.value);
for (var i = 0; i < properties.length; i++){
activeProperty = properties[i];
setEase();
}
function setEase(){
for (var i = 0; i < activeProperty.selectedKeys.length ; i++){
keySelection = activeProperty.selectedKeys;
curKey = keySelection[i];
curKeyTime = activeProperty.keyTime(curKey);
curKeyIndex = activeProperty.nearestKeyIndex(curKeyTime);
activeProperty.setInterpolationTypeAtKey(curKeyIndex, KeyframeInterpolationType.BEZIER, KeyframeInterpolationType.BEZIER);
activeProperty.setTemporalEaseAtKey(curKeyIndex,theEase, theEase);
}
}
}
I just can't figure out why. Am I missing something?
I tried your code and indeed "i" is not increasing, but for me the reason was that there was an error.
The main reason for error is that the 2nd and 3rd arguments to setTemporalEaseAtKey() should be arrays of KeyframeEase, not just KeyframeEase (see the scripting guide).
Another reason is that activeProperty needs not be an actual Property, hence querying activeProperty.selectedKeys.length will throw an error.
On a side note, what you call curKeyIndex is actually the same as curKey, so you dont need the nearestKeyIndex stuff. The following code works for me:
function storeKeyframes(){
var comp = app.project.activeItem;
if (!comp || comp.typeName !== "Composition") return;
var properties = comp.selectedProperties;
var i, I=properties.length;
var ease1 = new KeyframeEase(0,100);
for (i=0; i<I; i++){
if (properties[i] instanceof Property) setEase(properties[i], ease1);
};
};
function setEase(property, ease1){
var ease = property.propertyValueType===PropertyValueType.Two_D ? [ease1, ease1] : (property.propertyValueType===PropertyValueType.Three_D ? [ease1, ease1, ease1] : [ease1]);
var keySelection = property.selectedKeys;
var i, I=keySelection.length;
for (i=0; i<I; i++){
property.setInterpolationTypeAtKey(keySelection[i], KeyframeInterpolationType.BEZIER, KeyframeInterpolationType.BEZIER);
property.setTemporalEaseAtKey(keySelection[i], ease, ease);
};
};
storeKeyframes();
This works for me. Take a look into the console of the ESTK. I would suggest passing your activeProperty to your setEase function. So you keep your scopes clean.
Also it is better to not use the same iterator.
function main() {
storeKeyframes();
}
function storeKeyframes() {
var properties = app.project.activeItem.selectedProperties;
for (var i = 0; i < properties.length; i++) {
activeProperty = properties[i];
$.writeln(i + " in storeKeyframes");
setEase(activeProperty);
}
function setEase(ap) {
for (var j = 0; j < ap.selectedKeys.length; j++) {
$.writeln(j + " in setEase");
}
}
}
main();

Adding a running sum to a javascript object

I have a javascript object as below
var mydata=[
{"Club":"Blackburn","EventNo":1,"Pnts":3,"CumPnts":0},
{"Club":"Blackburn","EventNo":2,"Pnts":1,"CumPnts":0},
{"Club":"Blackburn","EventNo":3,"Pnts":4,"CumPnts":0},
{"Club":"Preston","EventNo":1,"Pnts":2,"CumPnts":0},
{"Club":"Preston","EventNo":2,"Pnts":4,"CumPnts":0},
{"Club":"Preston","EventNo":3,"Pnts":2,"CumPnts":0},]
I want to update the object so that CumPnts contains a running points total for each Club as below
{"Club":"Blackburn","EventNo":1,"Pnts":3,"CumPnts":3},
{"Club":"Blackburn","EventNo":2,"Pnts":1,"CumPnts":4},
{"Club":"Blackburn","EventNo":3,"Pnts":4,"CumPnts":8},
{"Club":"Preston","EventNo":1,"Pnts":2,"CumPnts":2},
{"Club":"Preston","EventNo":2,"Pnts":4,"CumPnts":6},
{"Club":"Preston","EventNo":3,"Pnts":1,"CumPnts":7},]
Any help would be much appreciated
Here is a function that loops through the list and updates it after it's been added. But I suspect that the events come in one at a time so there could be another function that can look the cumPtns obj and take from that. Here is for the current list.
var cumData = {};
var mydata=[
{"Club":"Blackburn","EventNo":1,"Pnts":3,"CumPnts":0},
{"Club":"Blackburn","EventNo":2,"Pnts":1,"CumPnts":0},
{"Club":"Blackburn","EventNo":3,"Pnts":4,"CumPnts":0},
{"Club":"Preston","EventNo":1,"Pnts":2,"CumPnts":0},
{"Club":"Preston","EventNo":2,"Pnts":4,"CumPnts":0},
{"Club":"Preston","EventNo":3,"Pnts":2,"CumPnts":0}];
function updateMyData() {
for (var i = 0; i < mydata.length; i++) {
var item = mydata[i];
if(cumData[item.Club] == undefined) {
cumData[item.Club] = {};
cumData[item.Club] = item.Pnts;
} else {
cumData[item.Club] = cumData[item.Club] + item.Pnts;
}
mydata[i].CumPnts = cumData[item.Club];
};
console.log(mydata);
//if you want to return it you can have this line below. Otherwise the object is updated so you'll probably want to do something with it once it's updated. Call back maybe?
return mydata;
}
updateMyData();
The first time it encounters a team it adds it to an array and so does with the corresponding cumPnts, so we can keep track of whether we checked a team earlier or not.
var tmArr = [];
var cumArr = [];
for(var i = 0; i < mydata.length; i++) {
var elm = mydata[i];
var club = elm.Club;
var points = elm.Pnts;
var idx = tmArr.indexOf(club);
if(idx > -1) {
cumArr[idx] += points;
elm.CumPnts = cumArr[idx];
}
else {
elm.CumPnts = points;
tmArr[tmArr.length] = club;
cumArr[cumArr.length] = points;
}
}
jsfiddle DEMO

Categories