Cannot read properties of an object - javascript

I have spent many hours on this irritating problem in my code. I define a class to hold several properties. I then populate those properties into a 2d array. From there, I then attempt to modify random elements in the 2d array. I keep getting the message
"Cannot read property of 'attribute' of undefined"
where attribute is any one of the three attributes in my object. Here is my code:
var table = new Array();
var adjacencyList = new Array();
var cell = function (prop) {
var atrbs = {
x: prop.x,
y: prop.y,
isVisited: prop.isVisited
}
return atrbs;
};
var createTable = function(size){
for(var row = 0; row < size; row++){
table[row] = new Array();
for(var column = 0; column < size; column++){
table[row][column] = new cell({x: row, y: column, isVisited: false});
}
}
};
function print(){
for(var row = 0; row < table.length; row++){
for(var column = 0; column < table[row].length; column++){
console.log(table[row][column]);
}
}
};
var randomizeMaze = function(){
var randomX = Math.floor((Math.random() * table.length));
var randomY = Math.floor((Math.random() * table.length));
var currentCell = table[randomX][randomY];
currentCell.isVisited = true;
adjacencyList.push(currentCell);
while( adjacencyList.length > 0 ) {
currentCell.isVisited = true;
var adjacentNodes = getAdjacentNodes(currentCell);
//randomly select a node to add to path
var nextInPath = adjacentNodes[Math.floor((Math.random() * adjacentNodes.length))];
//add to path to not visit it again
adjacencyList.push.apply(adjacencyList, adjacentNodes);
var removeNode = adjacencyList.indexOf(currentCell);
adjacencyList.splice(removeNode, 1);
//reset currentCell to random cell from resized list
currentCell = adjacencyList[Math.floor((Math.random() * adjacencyList.lenth))];
}
};
function getAdjacentNodes(pCell){
var adjacentNodes = new Array();
//check left
if(pCell.x - 1 >= 0){
var node = table[pCell.x-1][pCell.y];
adjacentNodes.push(node);
adjacencyList.push(node);
}
//check right
if(pCell.x + 1 < table.length){
var node = table[pCell.x+1][pCell.y];
adjacentNodes.push(node);
adjacencyList.push(node);
}
//check top
if(pCell.y - 1 >= 0){
var node = table[pCell.x][pCell.y - 1];
adjacentNodes.push(node);
adjacencyList.push(node);
}
//check bottom
if(pCell.y + 1 < table.length){
var node = table[pCell.x][pCell.y + 1];
adjacentNodes.push(node);
adjacencyList.push(node);
}
return adjacentNodes;
};
createTable(3);
//print();
randomizeMaze();
Whenever I try and access/change any property inside of a cell, namely in the functions 'randomeMaze' and 'getAdjacentNodes', it will throw the error I mentioned. I can print the objects to console, I see the objects as being populated when I debug. Maybe I'm missing something and am going crazy.
Any help would be greatly appreciated.

Related

How to create automatic catalog in InDesign with scripting in JavaScript

I have a following code which works great for importing excel to InDesign and creating automatic grid catalog.
var doc = app.activeDocument;
// get a text from the XLSX file
var inputFile = File("~/Desktop/test.xlsx");
var temp_frame = doc.textFrames.add();
temp_frame.place(inputFile);
var text = temp_frame.parentStory.contents;
temp_frame.remove();
// make a table from the text
var rows = text.split('\r');
var table = [];
for (var i = 1; i < rows.length; i++) table.push(rows[i].split('\t'));
// width and height of cards, gaps, properties of the grid
var card_width = 70;
var card_height = 80;
var gap = 5;
var cols = 2;
var rows = 3;
var cards_per_page = cols * rows;
// calculate and add pages
var cards = table.length;
var pages_to_add = Math.ceil(cards / cards_per_page) - 1;
while(pages_to_add--) doc.pages.add();
var page_num = 0; // start page
var start_point = [10,10]; // start point for a first card on a page
main_loop:
for (var i = 0; i < cards; i++) {
for (var row = 0; row < rows; row++) {
for (var col = 0; col < cols; col++) {
// break the loop if there is no more rows in the table
if (table.length == 0) break main_loop;
var table_row = table.shift(); // take a first row from the table
var title = table_row[0];
var description = table_row[1];
var price = table_row[2];
// make a card
var card = make_card(title, description, price);
// send the card to the some page and move at some place
card.move(doc.pages[page_num]);
card.move(start_point);
card.move(undefined, [(card_width + gap)*col, (card_height + gap)*row]);
}
}
if (i > (page_num-1) * cards_per_page) page_num++; // increase the page number
}
// the function to create and return a card
function make_card(title, description, price) {
var doc = app.activeDocument;
var title_frame = doc.textFrames.add();
title_frame.geometricBounds = [20, 80, 30, 150];
title_frame.contents = title;
var description_frame = doc.textFrames.add();
description_frame.geometricBounds = [30, 80, 80, 150];
description_frame.contents = description;
var price_frame = doc.textFrames.add();
price_frame.geometricBounds = [80, 80, 100, 150];
price_frame.contents = price;
var group = doc.groups.add([title_frame, description_frame, price_frame]);
return group;
}
But what if I want to have for example 3 pages. Every page will have a different template
Pages 1, 4, 7... will have this template
Pages 2,5,8 ...
and pages 3, 6, 9 ... will use grid template
I didn't get how can I resolve it in javascript.
Here is the template IDML: https://app.box.com/s/muqvroawbwv8zydwdizfc3xsmyzmenmy
Here is the code:
var doc = app.activeDocument;
// get a text from the XLSX file
var inputFile = File("~/Desktop/test.xlsx");
var temp_frame = doc.textFrames.add();
temp_frame.place(inputFile);
var text = temp_frame.parentStory.contents;
temp_frame.remove();
// split the text by rows
var rows = text.split('\r');
rows.shift(); // remove the first row (a header of the table)
// add pages if there is more than 10 rows
var i = rows.length - 10;
while (i > 0) {
doc.pages[0].duplicate(LocationOptions.AT_END);
i -= 1; if (i < 0) break;
doc.pages[1].duplicate(LocationOptions.AT_END);
i -= 5; if (i < 0) break;
doc.pages[2].duplicate(LocationOptions.AT_END);
i -= 4;
}
// fill all the text frames with texts from the rows
var frames = doc.textFrames;
var i = 0;
while (rows.length > 0) {
var row = rows.shift().split('\t');
frames[i++].contents = row[0]; // title
frames[i++].contents = row[1]; // text
frames[i++].contents = row[2]; // price
}
// remove empty frames
while (frames.length > i) frames[i].remove();
The script puts text from the Excel table to the text frames of the template. If there more than 10 rows the script adds pages.
The script depends on the order of the frames in the template. It fills the frames from the frist frame[0] on the frist page to the last frame. If you change the order the texts will get in wrong places.
Here is how the first 10 rows/cards are filling:

Passed value to function becomes undefined [duplicate]

This question already has answers here:
Get an error when I call object's properties in Array
(4 answers)
Closed 3 years ago.
I'm trying to write a script to disable layers in photoshop. So far the first part (first function) works, and it just grabs all the red layers and puts them in an array. Then I call the second function which takes one by one name from the array and passes it to the disabler (showBounds function). However, I'm looking at the input the showBounds receives ("name") and suddenly it stars saying it's getting "undefined". How can this be? what is happening?
var doc = app.activeDocument;
var theLayers = [];
function fillLayerArray(){
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
//var theOthers = new Array;
for (var m = 0; m <= theNumber; m++) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theColor = layerDesc.getEnumerationValue(stringIDToTypeID("color"));
globColor = theColor;
if (typeIDToStringID(theColor) == "red" && layerSet != "layerSectionEnd" ) {
theLayers.push(theName);
}
};
hideLayers(theNumber);
//showBounds(doc.layerSets);
}
function hideLayers(theNumber){
for (var i = 0; i <= theLayers.length; i++) {
//app.activeDocument.activeLayer.visible = false;
currentLayerName = theLayers[i];
//alert(currentLayerName)
alert(currentLayerName);
showBounds(doc.layerSets, currentLayerName);
}
}
function showBounds(layerNode, name) {
for (var i=0; i<layerNode.length; i++) {
showBounds(layerNode[i].layerSets);
for(var layerIndex=0; layerIndex < layerNode[i].artLayers.length; layerIndex++) {
var layer=layerNode[i].artLayers[layerIndex];
alert(layer.name + name)
try{
if (layer.name == name) {
layer.visible = 0;
}
}catch(e){
}
}
}
}
fillLayerArray();
You have
i <= theLayers.length
Should be
i < theLayers.length
In hideLayers.
(Also in m <= theNumber .... remember arrays are indexed starting from zero in JS)

Trying to find a cell value in a google sheets and then return the cell value

Getting an TypeError: Cannot read property '0' of undefined (line 35)
I am trying to find a cell value in google sheets and then return the cell value into another cell.
I dont get why I am getting a TypeError and I have tried all forms of toString methods or what not.
Please help
function hiearchyRoleGenerator() {
Logger.log("Setting spreasheets up")
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var generatorSheet = sheets[0];
var primeSheet = sheets[1];
var ecsSheet = sheets[2];
var cloudSalesSheet = sheets[3];
var salesDevSheet = sheets[4];
Logger.log("Setting cells up")
var businessUnit = generatorSheet.getRange("A2").getValue();
var startPoint = generatorSheet.getRange("B2").getValue();
var endPoint = generatorSheet.getRange("C2").getValue();
var separator = generatorSheet.getRange("D2");
var concat = generatorSheet.getRange("E2");
var target = startPoint.toString();
Logger.log(target)
//Logger.log("Finding Start Point")
//Logger.log(startPointResult)
//Logger.log(String(startPointResult))
if (businessUnit == "Prime") {
var lastRow = primeSheet.getLastRow();
var data = primeSheet.getRange(1,1,lastRow,7).getValues();
var dataResult = []
var i,j;
for (var i = 0; i <= lastRow ;i++) {
//Logger.log(values[i][0])
for (var j = 0; j<7; j++){
//Logger.log(values[i][j])
if(data[i][j] == startPoint){ //[i] row [j] column ERROR IS HERE
dataResult.push(data[[i][j]]);
}
}
}
concat.setValue(dataResult);
}
}
Can you try something like this?
var range = sheet.getRange(1,1,lastRow,7);
var values = range.getValues();
for (var row in values) {
for (var col in values[row]) {
if(values[row][column] == startPoint){ //[i] row [j] column ERROR IS HERE
dataResult.push(values[row][column]);
}
}
Also row and Column are relative to range so may be using var i,j starting with 0 might be an issue. you might have to use 1 instead

Ignoring empty cells in Google Sheets custom script

I am trying to create a formula to calculate Net Promoter Score on Google Sheets. I have the formula working but only when I specify the exact range. My issue is that this specific sheet will grow with data over time and I do not want to have to keep reselecting the range. What I want to do is select the entire row and just let it auto-update the NPS score. My issue with this approach is every empty cell is considered a zero which is screwing up my percentages. How can I make my function ignore the empty cells???
Here is my attempt:
/**
This is a custom formula that calculates the Net Promoter Score.
#customFunction
*/
function NPS(numArr) {
var detractors = new Array();
var passive = new Array();
var promoters = new Array();
var i = 0;
for (i = 0; i < numArr.length; i++) {
if (isNaN(numArr[i])) {
console.log(numArr[i]);
} else {
if (numArr[i] >= 9) {
promoters.push(numArr[i]);
} else if (numArr[i] === 7 || numArr[i] === 8) {
passive.push(numArr[i]);
} else if (numArr[i] <= 6) {
detractors.push(numArr[i]);
}
}
}
var promoPercentage = promoters.length / numArr.length;
var detractorsPercentage = detractors.length / numArr.length;
return (promoPercentage - detractorsPercentage) * 100;
}
You can use JavaScript filter [1] function to filter the empty values from the array you're getting (numArr). Also, notice that you're selecting a range of cells so the argument will be a 2D array [2], where each value is a "row" array filled with the column values for that row, in case you just want the first value of each row (for a one column range like A1:A25) you need to access the first element of each "row" array to get the actual value:
function NPS(numArr) {
var detractors = new Array();
var passive = new Array();
var promoters = new Array();
var i = 0;
//Filter empty elements
numArr = numArr.filter(function(element) {
return element[0] !== '';
})
for (i = 0; i < numArr.length; i++) {
if (isNaN(numArr[i][0])) {
console.log(numArr[i][0]);
} else {
if (numArr[i][0] >= 9) {
promoters.push(numArr[i][0]);
} else if (numArr[i][0] === 7 || numArr[i][0] === 8) {
passive.push(numArr[i][0]);
} else if (numArr[i][0] <= 6) {
detractors.push(numArr[i][0]);
}
}
}
var promoPercentage = promoters.length / numArr.length;
var detractorsPercentage = detractors.length / numArr.length;
return (promoPercentage - detractorsPercentage) * 100;
}
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
[2] https://developers.google.com/apps-script/guides/sheets/functions#arguments

Javascript grid calculation

I am creating a game where I represent boxes with a two dimensional array of their id.
var grid = [[X,X,X,X,X,X,X,X,X,X],
[X,X,4,4,4,4,X,X,X,X],
[X,3,3,3,3,X,X,X,X,X],
[X,X,X,2,2,2,2,X,X,X],
[X,1,1,1,1,5,5,5,5,5]];
The boxes stack on top of each other, and X represents a blank spot.
If one of the boxes are deleted I want any of the boxes above (That can fit) to shift down. So they are always neatly stacked.
So if I was to delete the box with ID: 1 I would get a new grid like this:
var grid = [[X,X,X,X,X,X,X,X,X,X],
[X,X,4,4,4,4,X,X,X,X],
[X,3,3,3,3,X,X,X,X,X],
[X,X,X,2,2,2,2,X,X,X],
[X,X,X,X,X,5,5,5,5,5]];
Then I would want Box: 3 to slide down into its spot like so:
var grid = [[X,X,X,X,X,X,X,X,X,X],
[X,X,4,4,4,4,X,X,X,X],
[X,X,X,X,X,X,X,X,X,X],
[X,X,X,2,2,2,2,X,X,X],
[X,3,3,3,3,5,5,5,5,5]];
Finally Box: 4 should move down into where 3 was:
var grid = [[X,X,X,X,X,X,X,X,X,X],
[X,X,X,X,X,X,X,X,X,X],
[X,X,4,4,4,4,X,X,X,X],
[X,X,X,2,2,2,2,X,X,X],
[X,3,3,3,3,5,5,5,5,5]];
Is there an easy way of doing this? I was thinking of a callback that checks the grid when a box is destroyed but what I came up with was mostly IF statements. Is there something elegant out there?
The box class itself also has the start position and its length:
box = {id: 3,
start: 1,
length: 4};
This is actually not an easy task. I created a little fiddle that does what you wanted to achieve (i think).
I extended the box prototype with some functions. My solution relies on the variables grid and blocks, but you could abstract that even more if you like to.
The functions testFunctionality and printGridToElement are just there for testing purposes.
My new Box prototype:
function Box(i, s, l) {
this.id = i;
this.start = s;
this.length = l;
this.row;
blocks.push(this);
}
Box.prototype.insertIntoGrid = function (row) {
this.row = row;
if (!grid[row]) grid[row] = [];
for (var i = 0; i < this.length; i++) {
grid[row][this.start + i] = this.id;
}
};
Box.prototype.destroy = function () {
blocks.splice(blocks.indexOf(this), 1);
this.removeFromGrid();
this.checkRemainingBlocksForMoveDown();
};
Box.prototype.checkRemainingBlocksForMoveDown = function () {
for (var i = 0; i < blocks.length; i++) {
var btmd = blocks[i].checkForMoveDown();
if (btmd) {
btmd[0].move(btmd[1]);
btmd[0].checkRemainingBlocksForMoveDown();
}
}
}
Box.prototype.move = function (row) {
this.removeFromGrid();
this.insertIntoGrid(row);
};
Box.prototype.removeFromGrid = function () {
for (var i = 0; i < this.length; i++) {
grid[this.row][this.start + i] = 0;
}
};
Box.prototype.checkForMoveDown = function () {
for (var i = 0; i < this.row; i++) {
var move = true;
for (var j = 0; j < this.length; j++) {
if (grid[i][this.start + j] != 0) {
move = false;
break;
}
}
if (move) {
return [this, i];
}
}
};
and the usage of it:
var b1 = new Box(1, 1, 4);
b1.insertIntoGrid(0);
var b2 = new Box(2, 3, 4);
b2.insertIntoGrid(1);
var b3 = new Box(3, 1, 4);
b3.insertIntoGrid(2);
var b4 = new Box(4, 2, 4);
b4.insertIntoGrid(3);
var b5 = new Box(5, 5, 5);
b5.insertIntoGrid(0);
b1.destroy();
b2.destroy();
b3.destroy();
NOTE: I designed the grid with 0 being the lowest row
I'm late, but here goes.
You should probably swap rows, and columns. That is make it like:
var rows = [];
column = [x,x,x,x,x,x,x,x];
rows.push(column);
Instead of:
var columns = [];
var row = [x,x,x,x,x,x,x,x];
columns.push(row);
This way a drop is just an array operation on columns. You can then do things like splice out a block, splice in a block, unshift, shift, and so on.
Do the array operations before animations, but not before you get the column, and row information from the grid.
You can even name the methods that do it by array methods. shift drops the bottom block, splice(start, stop, [optional]new block). Like that.
#Markai did swap the columns, and rows in their answer, but I thought I'd add some clarity.
This is what I came up with (Not working but the gist)
fallCheck = function(deletedPosition, deletedLength) {
var fallable = grid.reduce(
function(array, row) {
var unique = row.filter(function(item, i, ar) { return ar.indexOf(item) === i;});
var id = unique.find( function(boxId) {
var box = boxes.iterate("id", boxId, Phaser.Group.RETURN_CHILD); //Finds the instance within a Phaser Group
return (box.start >= deletedPosition) && (box.start + box.length) <= (deletedPosition + deletedLength);
});
if (id != -1) array.push(id);
}, []);
if (fallable.length > 0) { fall(fallable[0]); } //fall simply moves the box to the lowest position on the grid
};

Categories