I can't seem to figure out what I'm doing wrong on this code I wrote yesterday. This was my first time whipping up JavaScript, and using jQuery and Node.js also both for the first time, and I would think this three dimensional array should work as is. I've seen confusing mention about what multidimensional arrays are and people saying that JavaScript does not have any, though it has arrays of arrays. Anyways I guess I'm technically using an array of an array of an array, and don't understand why my outer array, which I imagined being an outer dimension by design, over-writes elements from the two inner-arrays into its own elements. The two inner arrays appear to work as they are supposed to, but the outter-most array mixes up the data in some way I don't really understand.
The inconsistency/problem can be observed by scrolling through the output.json file that is generated by this code and seeing that the outputs clearly do not match up with each of the three tables on this webpage I'm scraping from:
// My server.js file:
var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var app = express();
// the link below is a tutorial I was loosely following
// see http://scotch.io/tutorials/javascript/scraping-the-web-with-node-js
app.get('/scrape', function(req, res) {
url = 'http://espn.go.com/nba/player/stats/_/id/4145/kareem-abdul-jabbar'
request(url, function(error, response, html) {
if(!error) {
// utilize the Cheerio library on the returned html, yielding jQuery functionality
var $ = cheerio.load(html);
var numOfRows;
var stats = [[[]]];
for(var chart = 0; chart < 3; chart++) {
stats.push([[]]); // allocates space for each grid on each chart (each set of rows and columns)
$('.tablehead').eq(chart).filter(function(){
var data = $(this);
numOfRows = data.children().length - 2;
for(var i = 0; i < numOfRows + 1; i++) {
stats[chart].push([]); // allocates space for each row in the chart
}
})
var numOfColumns;
$('.stathead').eq(chart).filter(function(){
var data = $(this);
stats[chart][0][0] = data.children().first().text();
})
$('.colhead').eq(chart).filter(function(){ // first() specifies to select the first of the three occurances of this class; use eq(param) to find the Nth occurance
var data = $(this);
numOfColumns = data.children().length;
for(var i = 0; i < numOfColumns; i++) {
stats[chart][1][i] = data.children().eq(i).text();
}
})
var currentRow = 2;
for(var oddRow = 0; oddRow < (numOfRows + 1)/2 - 1; oddRow++) {
$('.tablehead .oddrow').eq(oddRow).filter(function(){
var data = $(this);
for(var c = 0; c < numOfColumns; c++) {
stats[chart][currentRow][c] = data.children().eq(c).text();
}
currentRow += 2;
})
}
currentRow = 3;
for(var evenRow = 0; evenRow < (numOfRows + 1)/2 - 1; evenRow++){
$('.tablehead .evenrow').eq(evenRow).filter(function(){
var data = $(this);
for(var c = 0; c < numOfColumns; c++) {
stats[chart][currentRow][c] = data.children().eq(c).text();
}
currentRow += 2;
})
}
currentRow -= 1; // gets the last allocated row index (after "currentRow += 2" has been executed)
$('.tablehead .total').eq(chart).filter(function(){
var data = $(this);
var LOGOIDX = 1;
for(var c = 0; c < numOfColumns - 1; c++) {
if(c < LOGOIDX) {
stats[chart][currentRow][c] = data.children().eq(c).text();
}
if(c == LOGOIDX) {
stats[chart][currentRow][c] = "N.A.";
stats[chart][currentRow][c + 1] = data.children().eq(c).text();
continue;
}
else {
stats[chart][currentRow][c + 1] = data.children().eq(c).text();
}
}
})
} // end chart loop
}
// Want to parse my json so that it displays in format: "name: value" rather than just "name" as it is now...
fs.writeFile('output.json', JSON.stringify(stats, null, 4), function(err){
console.log('File successfully written! - Check the project directory for the output.json file');
console.log('Number of columns in chart is: ' + numOfColumns);
})
// message to browser reminding that there's no UI involved here.
res.send('Check the console!')
})
})
app.listen('8081')
console.log('Magic happens on port 8081');
exports = module.exports = app;
Aha! Caught my bug -- just a simple logical error. Kind of embarrassing I didn't see it earlier, but oh well, got some practice and research (and a fair amount of distraction) by the end of the day:
As it can be seen, all the searching for HTML classes I did were parameterized by a variable named "chart", except for where I searched for odd rows and even rows within each chart -- the actual scraping of the bulk of each chart, so it naively appeared that my "3d array [was] over-writing values from other dimensions" <-- lol.
In simple, I just needed to create an offset based on a condition for each chart (a few extra lines of code), and I needed to edit two lines of code to reflect the newly calculated offset like so:
$('.tablehead .oddrow').eq(rowOffset + oddRow).filter(function(){
and
$('.tablehead .evenrow').eq(rowOffset + evenRow).filter(function(){
Thanks anyways for any help! I hope this issue tangentially benefits others greatly : P
Related
Short term problem: I have three paths on an artboard. The end of one path has a point at the same position as the beginning of another. The other path is separate. They are all grouped. I have some code that loops through the pathsin the group, and if one path ends where another begins it tried to join them together. The group must be highlighted. To start with my artboard look like this (The top line is two paths):
An after the script is run it looks like this:
With a lot of points added to the end of the line underneath. Could someone lend me a hand with this, Ideally, I'd like it to look like this:
The code looks like this:
var doc = activeDocument;//Gets the active document
var numArtboards = doc.artboards.length;//returns the number of artboards in the document
var intersections = true
var group = doc.selection[0]
var paths = []
var intersecttions = 0
// Builds an array of all the paths in the grouped object
if (group !== undefined && group.pageItems.length >= 2) {
for (var i = 0; i < group.pageItems.length; i++) {
var item = group.pageItems[i];
if (item instanceof PathItem) {
item.id = 'Path No' + i;
paths.push(item)
}
}
}
//Sets the first path that will be added to
$.write('paths length ', paths.length,'\n')
var chain = paths[0]
var chainPoints = chain.pathPoints
var chainLength = chainPoints.length - 1
var c1 = chainPoints[0]
var c2 = chainPoints[chainLength]
$.write('c ', c1.anchor,':::', c2.anchor,'\n')
//loops through the paths in the group to see if any overlap the first past
for (var i = 1; i < paths.length-1; i++) {
var link = paths[i]
$.write(link, '\n')
var linkPoints = link.pathPoints
var linkLength = linkPoints.length - 1
$.write('l ', l1.anchor, ':::', l2.anchor, '\n')
if (toString(c1.anchor) === toString(l2.anchor)) {
$.write('inttersection', '\n')
$.write('link', link.id, '\n')
for (var j = 0; j < linkLength; ++j) {
chain.pathPoints.add(linkPoints[j])
$.write (linkPoints[j], '\n')
}
}
}
The first problem is that it's not detecting the instance of overlap correctly. The line:
if (toString(c1.anchor) === toString(l2.anchor)) {
is not comparing one string to another but comparing a true response with another true response. It should be:
if (String(c1.anchor) === String(l2.anchor)) {
you also have to pass across the attributes of each point you are adding to the line and remove the old line, so within the j loop you'll need to add the following
for (var j = 0; j < linkLength; ++j) {
var pp1 = chainPoints.add()
var p2i = linkPoints[j];
pp1.anchor = p2i.anchor;
pp1.rightDirection = p2i.rightDirection;
pp1.leftDirection = p2i.leftDirection;
pp1.pointType = p2i.pointType;
pp1.handle = p2i.handle;
}
link.remove();
This seems to work except that it doesn't add the last point of the second line. I'm guessing that the loop length may not be set correctly If I work it out I'll update the post. I found this in Hiroyuki Sato code for his JoinReasonable scripts http://shspage.com/aijs/en/
I have a list of players in denoted as
activeRange[x]
where x will vary from day-to-day.
Each of the x values will have to have AT LEAST 4 more subsequent values (likely a bit more). Ideally I'd like the array to look like:
activeRange[x][y]
So here's what I've done so far:
var MATCH = AllData[TotalRows][TotalColumns+1];
activeRange[TotNumPlayers].push(MATCH);
This is all located within 3 nested for loops.
TotNumPlayers
will iterate through a given set declared at the beginning (somewhat like 23). Once done, the
TotalRows
will iterate, then finally
TotalColumns
I'm running into the following error:
TypeError: Cannot find function push in object mitch
mitch is the value of activeRange[0]. I've been staring at this way too long, so any help would be appreciated!
EDIT: Code inserted below:
PLEASE IGNORE ALL THE COMMENTS. I COPY/PASTED THIS FROM A BIT OF CODE I USED YESTERDAY TO PERFORM A DIFFERENT FUNCTION.
This is the second time I've ever posted on this website, so trying to format this monster to be pretty was scary sounding. Hopefully this is good enough.
This is how activeRange was declared and initialized.
var activeRange = new Array();
for (var b=0; b<=lastRow-2; b++){
activeRange[b] = sheetRANK.getRange(b+2,1).getValue();
}
This is the function.
function getTotalScore(activeRange, w) {
Logger.clear()
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetWAR = ss.getSheetByName('WAR');
var sheetRANK = ss.getSheetByName('RANK');
var AllData = sheetRANK.getDataRange().getValues();
Logger.log('First');
for (var TotNumPlayers = 0; TotNumPlayers <= activeRange.length; TotNumPlayers++) {
Logger.log('Second');
var f = 0;
for (var TotalColumns = 0; TotalColumns <= AllData[0].length; ++TotalColumns) { // Init n. If n <= the total columns (second dimension), inc n.
Logger.log('Third');
for (var TotalRows = 0; TotalRows <= AllData.length; ++TotalRows) { // Init i. If i <= the total rows (first dimension), inc i.
Logger.log('Fourth');
//try{ // to avoid errors.
if (activeRange[TotNumPlayers] != "") {
Logger.log('Here?');
if (AllData[TotalRows][TotalColumns].valueOf().toUpperCase() == activeRange[TotNumPlayers].toUpperCase()) {
Logger.log('How About Here?');
var MATCH = AllData[TotalRows][TotalColumns + 1];
activeRange.push(TotNumPlayers, MATCH);
for (var Calc = 0; Calc <= activeRange[TotNumPlayers].length - 1; Calc++) {
var OverallScore = ((activeRange[TotNumPlayers][0] * 1.0) + (activeRange[TotNumPlayers][1] * .75) + (activeRange[TotNumPlayers][2] * .50) + (activeRange[TotNumPlayers][3] * .25));
sheetRANK.getRange(activeRange[TotNumPlayers] + 1, 2).setValue(OverallScore);
f = f + 1;
}
if (TotalRows == AllData.length - 1 && TotalColumns == AllData[0].length - 1 && f == 0) {
Browser.msgBox('No names matching \'' + activeRange[TotNumPlayers] + '\' found. Check your spelling!');
return;
}
}
}
}
}
}
}
Try thinking about what kind of data structures you can use to make your life easier. For this particular case, you have a list of players that you want to associate some data with. You'd probably use a structure like:
activeRange = [
{
name: 'mitch',
data: []
}
]
When you want to update the data, you'd simply call activeRange[0].data.push(someData).
activeRange is an array of players and each player is represented by an object with some properties, (name, data, etc).
Calling activeRange[0] yields the first player in your array and activeRange[0].data will yield the data associated with that player, which you can then manipulate however you want (push, pop, etc)
Based on your comments, you need a structure more like this
var activeRange = [
{
name: 'mitch',
otherData: [
10,
11,
12,
13
]
},
{
name: 'viper',
otherData: [
//values
]
}
]
you can access that by activeRange[0].otherData[2]
to add to it, just push into the sub array activeRange[0].otherData.push(newValue)
It is quite clear. I have an array with some links and I want to build a loopto try all of them, but the problem is that link is always 3. It means that it read the last number in my array. Why? How can I fix it?
var categories = ['1','2','3'];
for( var i = 0; i < categories.length; i++ ) {
var link = '/'+categories[i];
browser.get(link);
browser.sleep(2000);
browser.driver.getCurrentUrl().then( function(url) {
expect(url).toMatch(link);
});
}
and I have list of divs and I want to read randomly infos from them. So I made the following
chosenOffer = Math.floor( (Math.random() * count ) + 1);
offer = element.all( by.className('offer')).get( chosenOffer );
But it shows always error message chosenOffer object...
This is a classic closure problem that is described in detail in:
Using protractor with loops
In your case, just let expect() resolve the promise:
var categories = ['1','2','3'];
for (var i = 0; i < categories.length; i++) {
var link = '/' + categories[i];
browser.get(link);
browser.sleep(2000);
expect(browser.driver.getCurrentUrl()).toMatch(link);
}
I'm am working on a script to count the number of times a certain string (in this case, coordinates) occur in a string. I currently have the following:
if (game_data.mode == "incomings") {
var table = document.getElementById("incomings_table");
var rows = table.getElementsByTagName("tr");
var headers = rows[0].getElementsByTagName("th");
var allcoord = new Array(rows.length);
for (i = 1; i < rows.length - 1; i++) {
cells = rows[i].getElementsByTagName("td");
var contents = (cells[1].textContent);
contents = contents.split(/\(/);
contents = contents[contents.length - 1].split(/\)/)[0];
allcoord[i - 1] = contents
}}
So now I have my variable allcoords. If I alert this, it looks like this (depending on the number of coordinates there are on the page):
584|521,590|519,594|513,594|513,590|517,594|513,592|517,590|517,594|513,590|519,,
My goal is that, for each coordinate, it saves how many times that coordinate occurs on the page. I can't seem to figure out how to do so though, so any help would be much appreciated.
you can use regular expression like this
"124682895579215".match(/2/g).length;
It will give you the count of expression
So you can pick say first co-ordinate 584 while iterating then you can use the regular expression to check the count
and just additional information
You can use indexOf to check if string present
I would not handle this as strings. Like, the table, is an array of arrays and those strings you're looking for, are in fact coordinates. Soooo... I made a fiddle, but let's look at the code first.
// Let's have a type for the coordinates
function Coords(x, y) {
this.x = parseInt(x);
this.y = parseInt(y);
return this;
}
// So that we can extend the type as we need
Coords.prototype.CountMatches = function(arr){
// Counts how many times the given Coordinates occur in the given array
var count = 0;
for(var i = 0; i < arr.length; i++){
if (this.x === arr[i].x && this.y === arr[i].y) count++;
}
return count;
};
// Also, since we decided to handle coordinates
// let's have a method to convert a string to Coords.
String.prototype.ToCoords = function () {
var matches = this.match(/[(]{1}(\d+)[|]{1}(\d+)[)]{1}/);
var nums = [];
for (var i = 1; i < matches.length; i++) {
nums.push(matches[i]);
}
return new Coords(nums[0], nums[1]);
};
// Now that we have our types set, let's have an array to store all the coords
var allCoords = [];
// And some fake data for the 'table'
var rows = [
{ td: '04.shovel (633|455) C46' },
{ td: 'Fruits kata misdragingen (590|519)' },
{ td: 'monster magnet (665|506) C56' },
{ td: 'slayer (660|496) C46' },
{ td: 'Fruits kata misdragingen (590|517)' }
];
// Just like you did, we loop through the 'table'
for (var i = 0; i < rows.length; i++) {
var td = rows[i].td; //<-this would be your td text content
// Once we get the string from first td, we use String.prototype.ToCoords
// to convert it to type Coords
allCoords.push(td.ToCoords());
}
// Now we have all the data set up, so let's have one test coordinate
var testCoords = new Coords(660, 496);
// And we use the Coords.prototype.CountMatches on the allCoords array to get the count
var count = testCoords.CountMatches(allCoords);
// count = 1, since slayer is in there
Use the .indexOf() method and count every time it does not return -1, and on each increment pass the previous index value +1 as the new start parameter.
You can use the split method.
string.split('517,594').length-1 would return 2
(where string is '584|521,590|519,594|513,594|513,590|517,594|513,592|517,590|517,594|513,590|519')
How could I populate a second select element? I've figured out how to do the first one. But how could I do the same for the second depending on which "Make" is selected? I've tried to talk myself through it while taking small steps but I'm thinking this may be too advanced for me.
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"},{"name":"Spider","value":"22172"}]}';
var carobj = eval ("(" + cars + ")");
var select = document.getElementsByTagName('select')[0];
//print array elements out
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
select.options.add(new Option(d.name, i))
};
If I read your question right, you want to populate a second select with the models for the make in the first select. See below for a purely JS approach (with jsfiddle). If possible, I would recommend looking into jQuery, since I would prefer a jQuery solution.
http://jsfiddle.net/m5U8r/1/
var carobj;
window.onload = function () {
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"}, {"name":"Spider","value":"22172"}]}]}';
carobj = eval ("(" + cars + ")");
var makes = document.getElementById('make');
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
makes.options.add(new Option(d.name, i));
}
makes.onchange = getModels;
getModels();
}
// add models based on make
function getModels () {
var makes = document.getElementById('make');
var make = makes.options[makes.selectedIndex].text;
for (var i = 0; i < carobj.USED.length; i++) {
if (carobj.USED[i].name == make) {
var models = document.getElementById('model');
models.options.length = 0;
for (var j= 0; j < carobj.USED[i].models.length; j++) {
var model = carobj.USED[i].models[j];
models.options.add(new Option(model.name, j));
}
break;
}
}
}
I would also recommend looking into safer JSON parsing. There is a security risk in using eval if it runs on any user input. You could look into JSON.org and their json2.js. Or if you want to use jQuery: parseJSON. Below is the jQuery version:
jQuery.parseJSON(jsonString);
JSON parsing tips from: Safely turning a JSON string into an object.