I am trying to enable a client to edit JS sources and define multi-dimensional arrays in this format:
imageArray[0][0][0][0] = 'image000.jpg';
imageArray[0][0][0][1] = 'image0001.jpg';
...
imageArray[1][0][0][0] = 'image1000.jpg';
imageArray[1][0][0][1] = 'image1001.jpg';
I made some dynamic matrices so JS does not output errors due to non-initialized arrays and now the client can define the arrays.
The problem is when I try to print some images, for some arrays (sub-arrays) there is a problem.
If you check check this attached document: https://dl.dropboxusercontent.com/u/58889914/tmp/bt4-forum.html you will see that it prints the wrong image in alert()
Do you have idea what the problem is ?
Thanks a lot in advance.
Attachment content:
<script type="text/javascript">
/// CONFIG ////
function initArray(maxRows, maxCols)
{
var imageArray = [];
for( c= 0; c < maxRows; c++){
imageArray.push(recurGenCol(1, maxRows, maxCols));
}
return imageArray;
}
function recurGenCol(col, maxRows, maxCols)
{
if(col >= maxCols){
return "";
}
var row_col = [];
row_col = recurGenCol(col+1, maxRows, maxCols);
var row_row = [];
for(k = 0; k < maxRows; k++)
{
row_row.push(row_col);
}
return row_row;
}
// INIT:
var rows = 10;
var cols = 5;
var imageArray = initArray(rows, cols);
//console.log(imageArray);
// END CONFIG. Start definng array //
//var imageArray = imageArraya;
imageArray[0][0][0][0] = 'image_0_0_0_0.jpg';
imageArray[0][0][0][1] = 'image_0_0_0_1.jpg';
imageArray[0][0][0][2] = 'image_0_0_0_2.jpg';
//console.log( imageArray[0][0][0][1]);
imageArray[0][0][0][3] = 'image_0_0_0_3.jpg';
imageArray[0][0][0][4] = 'image_0_0_0_4.jpg';
imageArray[0][0][1][0] = 'image_0_0_1_0.jpg';
imageArray[0][0][1][1] = 'image_0_0_1_1.jpg';
imageArray[0][0][1][2] = 'image_0_0_1_2.jpg';
imageArray[0][0][1][3] = 'image_0_0_1_3.jpg';
imageArray[0][0][2][0] = 'image_0_0_2_0.jpg';
imageArray[0][0][2][1] = 'image_0_0_2_1.jpg';
imageArray[0][0][2][2] = 'image_0_0_2_2.jpg';
imageArray[0][0][3][3] = 'image_0_0_2_3.jpg';
imageArray[0][0][3][0] = 'image_0_0_3_0.jpg';
imageArray[0][0][3][1] = 'image_0_0_3_1.jpg';
imageArray[0][0][3][2] = 'image_0_0_3_2.jpg';
imageArray[0][0][3][3] = 'image_0_0_3_3.jpg';
imageArray[0][1][0][0] = 'image_0_1_0_0.jpg';
imageArray[0][2][0][0] = 'image_0_2_0_0.jpg';
imageArray[0][3][0][0] = 'image_0_3_0_0.jpg';
imageArray[0][3][0][1] = 'image_0_3_0_1.jpg';
imageArray[0][3][0][2] = 'image_0_3_0_2.jpg';
imageArray[0][3][0][3] = 'image_0_3_0_3.jpg';
imageArray[1][0][0][0] = 'image_1_0_0_0.jpg';
imageArray[1][0][0][1] = 'Image_1_0_0_1.jpg';
imageArray[1][0][0][2] = 'image_1_0_0_2.jpg';
imageArray[2][0][0][0] = 'image_2_0_0_0.jpg';
imageArray[2][0][0][1] = 'image_2_0_0_1.jpg';
imageArray[2][0][0][2] = 'image_2_0_0_2.jpg';
imageArray[2][0][0][3] = 'image_2_0_0_3.jpg';
imageArray[2][1][0][0] = 'image_2_1_0_0.jpg';
imageArray[2][1][0][1] = 'image_2_1_0_1.jpg';
//imageArray[6][1][0][1] = 'image_2_1_0_1.jpg';
var img =imageArray[0][0][1][0];
//console.log(log);
alert(img);
</script>
SOLUTION
This took me some time ... but I enjoyed every second!
function initArray(maxRows, maxCols){
var c = -1, farray = [];
var recursive = function(array){
c++;
for(var r = 0; r < maxRows; r++){
if(c == maxCols) array[r] = '';
else array[r] = recursive([]);
}
c--;
return array;
};
return recursive(farray);
}
I support Rafael's solution. But I wanted to make it clear why the original code doesn't work as expected.
In many programming languages, you can have multiple places in memory point to the same location. Typically this is done with named variables. So here's a simple example...
a = b = c = {hello: "there"};
// now a.hello would mean "there",
// so would b.hello and c.hello
c.hello = "bye";
// now, even a.hello would mean "bye";
But it doesn't have to be done like this and array indexes can actually behave the same way.
So observe how I have commented your code and added new console logs. What you see is that, in a lot of cases, you are pushing the same object to multiple places. When you do that, changing its value in one place changes it everywhere, because they're just references to the same thing.
function initArray () {
// This is the first point any array ever actually exists...
var funArray = [];
console.log('Just created the funArray.');
for (i= 0; i < 10; i++){
// Does ten pushes to the funArray with whatever
// recurGenCol returns...
funArray.push(recurGenCol(1, 10, 5));
console.log('Just pushed a single multi-level array to the funArray. None of these reference the same object.');
}
// Returns the funArray after those ten pushes...
return funArray;
}
function recurGenCol(col, maxRows, maxCols){
var i;
// Here, col starts at 1 on the first iteration,
// it then reaches 5 on the fifth iteration,
// passing the if check, and the function returns
// to initArray() with an empty string.
console.log('About to check if col ('+col+') is greater than or equal to 5.');
if (col >= 5){
console.log('It was true! Returning to '+arguments.callee.caller.name+' with an empty string.');
return "";
}
var row_col = [];
//console.log('Just created row_col, brand new, from scratch.');
row_col = recurGenCol(col+1, 10, 5);
//console.log('Now row_col is equal to the what recurGenCol returns when it is called with ' + (col + 1) + ' as the first argument.');
var row_row = [];
for (i = 0; i < 10; i++) {
// Does ten pushes to row_row with whatever row_col is...
row_row.push(row_col);
}
// Returns back to initArray() with whatever row_row is...
console.log('Going to return to '+arguments.callee.caller.name+' with row_row.');
return row_row;
}
// INIT:
var imageArray = initArray();
//console.log(imageArray);
// First level is imageArray,
// Second level is row_row,
// Third level is row_col
imageArray[0][0][0][0] = 'image_0_0_0_0.jpg';
imageArray[0][0][0][1] = 'image_0_0_0_1.jpg';
imageArray[0][0][0][2] = 'image_0_0_0_2.jpg';
//console.log( imageArray[0][0][0][1]);
imageArray[0][0][0][3] = 'image_0_0_0_3.jpg';
imageArray[0][0][0][4] = 'image_0_0_0_4.jpg';
imageArray[0][0][1][0] = 'image_0_0_1_0.jpg';
var img = imageArray[0][0][1][0];
// We expect this to log 'image_0_0_1_0.jpg'
// and it does so
console.log("first: " + img);
imageArray[0][0][1][1] = 'image_0_0_1_1.jpg';
imageArray[0][0][1][2] = 'image_0_0_1_2.jpg';
imageArray[0][0][1][3] = 'image_0_0_1_3.jpg';
imageArray[0][0][2][0] = 'image_0_0_2_0.jpg';
imageArray[0][0][2][1] = 'image_0_0_2_1.jpg';
imageArray[0][0][2][2] = 'image_0_0_2_2.jpg';
imageArray[0][0][3][3] = 'image_0_0_2_3.jpg';
imageArray[0][0][3][0] = 'image_0_0_3_0.jpg';
imageArray[0][0][3][1] = 'image_0_0_3_1.jpg';
imageArray[0][0][3][2] = 'image_0_0_3_2.jpg';
imageArray[0][0][3][3] = 'image_0_0_3_3.jpg';
imageArray[0][1][0][0] = 'image_0_1_0_0.jpg';
imageArray[0][2][0][0] = 'image_0_2_0_0.jpg';
imageArray[0][3][0][0] = 'image_0_3_0_0.jpg';
imageArray[0][3][0][1] = 'image_0_3_0_1.jpg';
imageArray[0][3][0][2] = 'image_0_3_0_2.jpg';
imageArray[0][3][0][3] = 'image_0_3_0_3.jpg';
imageArray[1][0][0][0] = 'image_1_0_0_0.jpg';
imageArray[1][0][0][1] = 'Image_1_0_0_1.jpg';
imageArray[1][0][0][2] = 'image_1_0_0_2.jpg';
imageArray[2][0][0][0] = 'image_2_0_0_0.jpg';
imageArray[2][0][0][1] = 'image_2_0_0_1.jpg';
imageArray[2][0][0][2] = 'image_2_0_0_2.jpg';
imageArray[2][0][0][3] = 'image_2_0_0_3.jpg';
imageArray[2][1][0][0] = 'image_2_1_0_0.jpg';
imageArray[2][1][0][1] = 'image_2_1_0_1.jpg';
//imageArray[6][1][0][1] = 'image_2_1_0_1.jpg';
img = imageArray[0][0][1][0];
// We expect this to log 'mage_0_0_1_0.jpg' but it DOESN'T!!!.
console.log("second: " + img);
// Rules that are true:
// - At 1st level, no items are the same object
// - At 2nd and 3rd level, all items within each parent array are the same object
// - At the last level, same-index values are the same object, except if the first level array is different
console.log('Is imageArray[0][0][0][1] the same object as imageArray[0][0][0][2]?');
console.log(imageArray[0][0][0][1] === imageArray[0][0][0][2]);
console.log('Is imageArray[0][0][0][1] the same object as imageArray[0][0][1][1]?');
console.log(imageArray[0][0][0][1] === imageArray[0][0][1][1]);
console.log('Is imageArray[0][0][0][1] the same object as imageArray[0][0][1][2]?');
console.log(imageArray[0][0][0][1] === imageArray[0][0][1][2]);
console.log('Is imageArray[0][0][0][2] the same object as imageArray[0][0][1][2]?');
console.log(imageArray[0][0][0][2] === imageArray[0][0][1][2]);
console.log('Is imageArray[0][0][0][1] the same object as imageArray[0][1][0][1]?');
console.log(imageArray[0][0][0][1] === imageArray[0][1][0][1]);
console.log('Is imageArray[0][0][0][1] the same object as imageArray[1][0][0][1]?');
console.log(imageArray[0][0][0][1] === imageArray[1][0][0][1]);
console.log('Is imageArray[0][0][1] the same object as imageArray[0][0][2]?');
console.log(imageArray[0][0][1] === imageArray[0][0][2]);
console.log('Is imageArray[0][1] the same object as imageArray[0][2]?');
console.log(imageArray[0][1] === imageArray[0][2]);
Related
I have created a Google script that pushes data every hour from the Capital Bikeshare API to a Google Sheet, but I have noticed that the way I am currently pulling the data doesn't maintain consistency over time. Here's the code I'm using:
function myFunction() {
// Set the active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var currentData = ss.getSheetByName("Current");
var historicData = ss.getSheetByName("Historic");
// Fetch API
var stationInfo = UrlFetchApp.fetch('https://gbfs.capitalbikeshare.com/gbfs/en/station_information.json');
var stationStatus = UrlFetchApp.fetch('https://gbfs.capitalbikeshare.com/gbfs/en/station_status.json');
// Get the current date and time
var today = new Date();
var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
var dateTime = date+' '+time;
// Parse the JSON reply
var jsonInfo = stationInfo.getContentText();
var dataInfo = JSON.parse(jsonInfo);
var jsonStatus = stationStatus.getContentText();
var dataStatus = JSON.parse(jsonStatus);
// Create the data frame for every BID station
var stationInfo72 = dataInfo["data"]["stations"][69];
var stationStatus72 = dataStatus["data"]["stations"][69];
var stationInfo87 = dataInfo["data"]["stations"][83];
var stationStatus87 = dataStatus["data"]["stations"][83];
var stationInfo330 = dataInfo["data"]["stations"][311];
var stationStatus330 = dataStatus["data"]["stations"][311];
var stationInfo153 = dataInfo["data"]["stations"][143];
var stationStatus153 = dataStatus["data"]["stations"][143];
var stationInfo226 = dataInfo["data"]["stations"][213];
var stationStatus226 = dataStatus["data"]["stations"][213];
var stationInfo365 = dataInfo["data"]["stations"][342];
var stationStatus365 = dataStatus["data"]["stations"][342];
var stationInfo473 = dataInfo["data"]["stations"][446];
var stationStatus473 = dataStatus["data"]["stations"][446];
var outputStationsInfo = [stationInfo72, stationInfo87, stationInfo330, stationInfo153, stationInfo226, stationInfo365, stationInfo473]
var outputStationsStatus = [stationStatus72, stationStatus87, stationStatus330, stationStatus153, stationStatus226, stationStatus365, stationStatus473]
Logger.log(outputStationsInfo, outputStationsStatus)
// Create lists of each element
var outputHead = [];
var outputTail = [];
outputStationsInfo.forEach(function(elem,i) {
outputHead.push([elem["station_id"],elem["name"],elem["capacity"], elem["lat"], elem["lon"]]);
});
outputStationsStatus.forEach(function(elem,i) {
outputTail.push([elem["num_bikes_available"], elem["num_ebikes_available"], dateTime]);
});
// Publish arrays in the Current sheet
currentData.getRange(2,1,7,5).setValues(outputHead);
currentData.getRange(2,6,7,3).setValues(outputTail);
// Publish arrays in the Historic sheet
historicData.getRange(historicData.getLastRow() + 1,1,7,5).setValues(outputHead);
historicData.getRange(historicData.getLastRow() - 6,6,7,3).setValues(outputTail);
}
Essentially, I am drilling into the 69th item in the indexes of the JSONs to get the data that I need from two different APIs, and then I merge them together to create a data frame of everything I need to push to the sheet. However, sometimes the API does not report them in the normal order and I end up getting bikeshare stations that aren't in my study area. For example, 99% of the time the 69th item in the array is station_id = 72, but occasionally it's station_id = 73 or something.
Is there a way to conditionally pull a specific array based on the station_id number within the array? I feel like the answer might allow me to do a loop as well to clean this up. Any advice is helpful, as I'm super new to this.
You have to check if the element's station_id is as expected. If not, check through the surrounding parts of the array using a custom iterator.
Snippet:
/**
* #return indexes of the surrounding ``i`` in batches of 5
*/
function* checkSurroundings(i, lastIndex) {
let j = i;
function* check(ct, border, reverse = true, limit = border < 5 ? border : 5) {
const margin = reverse ? ct - limit : ct + limit;
while (ct - margin !== 0) yield reverse ? --ct : ++ct;
return ct;
}
while (i !== 0 || j < lastIndex) {
if (i !== 0) i = yield* check(i, i);
if (j < lastIndex) j = yield* check(j, lastIndex - j, false);
//console.log({ i, j });
}
}
var stations = dataInfo["data"]["stations"];
var stationInfo72 = stations[69];
const iter = checkSurroundings(69,stations.length-1)
//if station_id is not 72, loop through the surrounding indexes
while(stationInfo72["station_id"] !== 72){
const next = iter.next();
if(next.done) {
console.error("station id 72 not found");
break;
}
stationInfo72 = stations[next.value]
}
Snippet showing how checkSurroundings iterates:
/**
* #return indexes of the surrounding ``i`` in batches of 5
*/
function* checkSurroundings(i, lastIndex) {
let j = i;
function* check(ct, border, reverse = true, limit = border < 5 ? border : 5) {
const margin = reverse ? ct - limit : ct + limit;
while (ct - margin !== 0) yield reverse ? --ct : ++ct;
return ct;
}
while (i !== 0 || j < lastIndex) {
if (i !== 0) i = yield* check(i, i);
if (j < lastIndex) j = yield* check(j, lastIndex - j, false);
console.log({ i, j });
}
}
console.log("Order of iteration",[...checkSurroundings(50, 100)])
Conditionally picking elements: filter
For conditionally picking elements from an array in JavaScript, Array.prototype.filter should always be a consideration.
Create a predicate function that matches the shape of your data and checks for certain station IDs.
Here is a function that returns a predicate function. You put in the IDs you want in an array, and it returns the required function for filter.
function byStationId(stationIds) {
return function (obj) {
return stationIds.indexOf(obj.station_id) > -1;
};
}
var myStationFilter = byStationId([72, 73, 74]);
var outputStationsInfo = dataInfo.data.stations.filter(myStationFilter);
Transforming data: map
The pattern
var newArray = [];
oldArray.forEach(function (item) {
newArray.push(/* something based on item */);
});
can usually be replaced with Array.prototype.map
var newArray = oldArray.map(function (item) { return /* something based on item */});
Think of this as the "adapter" from one data shape to another.
function cleanInfo(info) {
return [info.station_id, info.name, info.capacity, info.lat, info.lon];
}
var outputHead = outputStationsInfo.map(cleanInfo);
For the dateTime injection, just do the same trick demonstrated above with the station IDs: have a function that takes a date string and returns the appropriate adapter function.
(Also note the provided date formatting utility Apps Scripts provides, Utilities.formatDate())
var dateTime = Utilities.formatDate(
new Date(),
ss.getSpreadsheetTimeZone(),
"yyyy-MM-dd HH:mm:ss"
);
function cleanStatus(dateTime) {
return function (status) {
return [status.num_bikes_available, status.num_bikes_available, dateTime];
};
}
var outputTail = outputStationsStatus.map(cleanStatus(dateTime));
Here's everything together, untested, just for inspiration. You must at the very least update the line with the station IDs to match your desired station IDs. Note that the helper functions for map and filter are at the bottom, taking advantage of JavaScript's hoisting feature.
function myFunction() {
// Set the active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the current date and time
var dateTime = Utilities.formatDate(
new Date(),
ss.getSpreadsheetTimeZone(),
"yyyy-MM-dd HH:mm:ss"
);
// Fetch API
var stationInfo = UrlFetchApp.fetch(
"https://gbfs.capitalbikeshare.com/gbfs/en/station_information.json"
);
var stationStatus = UrlFetchApp.fetch(
"https://gbfs.capitalbikeshare.com/gbfs/en/station_status.json"
);
// Parse the JSON reply
var dataInfo = JSON.parse(stationInfo.getContentText());
var dataStatus = JSON.parse(stationStatus.getContentText());
// Create the data frame for every BID station
var myStationFilter = byStationId([72, 73, 74]); //!! UPDATE THESE NUMBERS
var outputStationsInfo = dataInfo.data.stations.filter(myStationFilter);
var outputStationsStatus = dataStatus.data.station.filter(myStationFilter);
// Create lists of each element
var outputHead = outputStationsInfo.map(cleanInfo);
var outputTail = outputStationsStatus.map(cleanStatus(dateTime));
// Publish arrays in the Current sheet
var currentData = ss.getSheetByName("Current");
currentData.getRange(2, 1, 7, 5).setValues(outputHead);
currentData.getRange(2, 6, 7, 3).setValues(outputTail);
// Publish arrays in the Historic sheet
var historicData = ss.getSheetByName("Historic");
historicData
.getRange(historicData.getLastRow() + 1, 1, 7, 5)
.setValues(outputHead);
historicData
.getRange(historicData.getLastRow() - 6, 6, 7, 3)
.setValues(outputTail);
//-------- helper functions ------------
function byStationId(stationIds) {
return function (obj) {
return stationIds.indexOf(obj.station_id) > -1;
};
}
function cleanInfo(info) {
return [info.station_id, info.name, info.capacity, info.lat, info.lon];
}
function cleanStatus(dateTime) {
return function (status) {
return [status.num_bikes_available, status.num_bikes_available, dateTime];
};
}
}
I am running a RegExp on a user input to test for capitals of 3 or more in a row. I have a loop that finds the RegExp and then adds it to an array. Another loop that creates a new array. When I run the .replace it seems that that array isn't being looped over.
var abbrBracket = /\([A-Z]{3,30}\)/g;
var abbrBracketArr = [];
var a;
while (a = abbrBracket.exec(nonCode)) {
abbrBracketArr.push(a[0]);
}
var capFoundInArr = /[A-Z]{3,30}/g;
var abbrBracketArrRemove = [];
var b;
while (b = capFoundInArr.exec(abbrBracketArr)) {
abbrBracketArrRemove.push('(<abbr>' + b[0] + '</abbr>)');
}
for(var c = 0; c < abbrBracketArrRemove.length; c++){
nonCode = document.getElementById('cleanse').innerHTML;
//nonCode = nonCode.replace(new RegExp(/\([A-Z]{3,30}\)/), abbrBracketArrRemove[c]);
nonCode = nonCode.replace(new RegExp(abbrBracketArr[c]), abbrBracketArrRemove[c]);
document.getElementById('cleanse').innerHTML = nonCode;
}
The results show if there are two (or more) of the same abbreviations, the first is executed multiple times the next is skipped.
Saying that, I am using the exact same code to run a second query for replace and I am not getting this error.
var abbrNoBracket = /\s[A-Z]{3,30}/g;
var abbrNoBracketArr = [];
var d;
while (d = abbrNoBracket.exec(nonCode)) {
abbrNoBracketArr.push(d[0]);
}
var abbrNoBracketArrRemove = [];
var e;
while (e = capFoundInArr.exec(abbrNoBracketArr)) {
abbrNoBracketArrRemove.push(' <abbr title="">' + e[0] + '</abbr>');
}
for(var f = 0; f < abbrNoBracketArrRemove.length; f++){
nonCode = document.getElementById('cleanse').innerHTML;
nonCode = nonCode.replace(new RegExp(abbrNoBracketArr[f]), abbrNoBracketArrRemove[f]);
document.getElementById('cleanse').innerHTML = nonCode;
}
In the first block, you can see I commented out a line, if I use the RegExp instead of the array. It works. Curious, why this would work for one, but not the other.
Found my error, which was very obvious after a good nights sleep.
I called a new RegExp on the array. Once I removed that, everything worked as it should.
for(var c = 0; c < abbrBracketArrRemove.length; c++){
nonCode = document.getElementById('cleanse').innerHTML;
//mistake
//nonCode = nonCode.replace(new RegExp(abbrNoBracketArr[f]), abbrNoBracketArrRemove[f]);
//corrected
nonCode = nonCode.replace(abbrBracketArr[c], abbrBracketArrRemove[c]);
document.getElementById('cleanse').innerHTML = nonCode;
}
Hopefully that helps someone else.
I'm trying to create a poker game in JavaScript. I thought the function that tested flushes worked perfectly until I displayed the hand AFTER the method ran. If there is a flush, this function is supposed to show the user's full hand, and then only the flush underneath it. Instead, it shows only the flush, and then the flush again underneath it:
var $ = function (id) { return document.getElementById(id); };
var test = function() {
var deck = new POKER.Deck(); //creates deck
var hand = new POKER.Hand(); //creates hand
//----------------TEST HAND UNTIL FLUSH-----------------
while (hand.getValue() != POKER.HAND_TYPE.FLUSH) {
deck.refreshDeck(); //refresh deck with new cards
for (var i = 0; i < 7; ++i) { //populate hand
hand.addCard(deck.dealCard());
}
console.log(hand.size() + " before"); //only for debugging. Prints "7 before"
hand.testFlush();
console.log(hand.size() + " after"); //only for debugging. Result unexpected
if (hand.getValue() == POKER.HAND_TYPE.FLUSH) { //if hand has a flush
for (var j = 0; j < hand.size(); j++) { //display full hand
var img = document.createElement("img");
var card = hand.getCardAtIndex(j);
img.src = card.getImage();
$("images").appendChild(img);
}
for (var k = 0; k < 5; k++) { //display flush hand
var img2 = document.createElement("img");
var card2 = hand.getValueCardAtIndex(k);
img2.src = card2.getImage();
$("handImg").appendChild(img2);
}
break;
} else {
hand.empty();
}
}
};
window.onload = function() {
test();
};
The second console.log statement prints out "4 after" until the testFlush method detects a flush, and the final result is "5 after".
testFlush method:
POKER.Hand.prototype.testFlush = function() {
//first, sort cards by rank so that the highest flush is
//taken if there are more than five cards of the same suit
this.sortByRank();
this.sortBySuit();
var tempHand = this.cards; //modifiable version of this.cards
var NUM_OF_TESTS = 3; //only 3 loops required to test for all possible flushes
var LAST_CARD_INDEX = 4; //represents the fifth card, or index 4
var MAX_CARDS = 5; //maximum cards possible in a hand (valueCards)
for (var i = 1; i <= NUM_OF_TESTS; i++){
//check if 1st and 5th cards are the same suit
if(tempHand[0].getSuit() == tempHand[LAST_CARD_INDEX].getSuit()){
this.value = POKER.HAND_TYPE.FLUSH;
while(tempHand.length != MAX_CARDS){ //remove last card in tempHand until there are only five cards
tempHand.pop();
}
this.valueCards = tempHand;
}else{
tempHand.splice(0,1); //removes first card from the temporary hand
}
}
};
All "hand.size()" does in the test function is "return this.cards.length". So what I don't understand is how the testFlush method could be altering the object attribute "this.cards" when it only alters the temporary variable tempHand.
Hand object:
POKER.Hand = function(){
this.cards = [];
this.value; //integer that corresponds to the POKER.HAND_TYPE
this.valueCards = []; //array of the five cards that corresponds only to this.value
};
Hand.size method:
POKER.Hand.prototype.size = function() {
return this.cards.length;
};
The problem is this line:
var tempHand = this.cards; //modifiable version of this.cards
Assigning an array or object to a variable does not make a copy of it. The variable is a reference to the same array, so tempHand.pop() modifies this.cards as well. You can make a copy of an array with .slice():
var tempHand = this.cards.slice();
So I found some info on this site on how to go about grabbing the value of the last index of an array. I have an Array that is of an unknown length. It builds based on a search of results. For example:
var custid = nlapiGetFieldValue('entity');
var custRecord = nlapiLoadRecord('customer', custid);
var itemPriceLineCount = custRecord.getLineItemCount('itempricing');
for (var i = 1; i <= itemPriceLineCount; i++) {
var priceItemId = [];
priceItemId = custRecord.getLineItemValue('itempricing', 'item', i);
if (priceItemId == itemId) {
var histCol = [];
histCol[0] = new nlobjSearchColumn('entity');
histCol[1] = new nlobjSearchColumn('totalcostestimate');
histCol[2] = new nlobjSearchColumn('tranid');
histCol[3] = new nlobjSearchColumn('trandate');
var histFilter = [];
histFilter[0] = new nlobjSearchFilter('entity', null, 'is', custid);
histFilter[1] = new nlobjSearchFilter('item', null, 'is', itemId);
var histSearch = nlapiSearchRecord('invoice', null, histFilter, histCol);
for (var h = 0; h <= histSearch.length; h++) {
var itemRate = new Array();
var histSearchResult = histSearch[h];
itemRate = histSearchResult.getValue('totalcostestimate');
}
}
}
Now when I use:
var last_element = itemRate[itemRate.length - 1];
It gives me the number of digits/placeholders in each element of the array. So as per my example I know my array holds the values of .00 and 31.24 because I put them there for a test. So last_element will result in 3 and 5. How can I grab the value 31.24 or the last element period? I need the value not the number of digits.
var itemRate = new Array();// Not sure what you intend to do with this array
var histSearchResult = histSearch[h];
itemRate = histSearchResult.getValue('totalcostestimate'); // but note `itemRate` is no more an array here. Its a variable having the value of `totalcostestimate` in string format
Now coming to your use case
/* you're trying to get the length of the string value and subtracting -1 from it.
So its very obvious to get those number of digits */
var last_element = itemRate[itemRate.length - 1]; // returns you that index value of the string
If you want to get the last array value of your search i.e histSearch
You may want to do something like this
var last_element = histSearch[histSearch.length-1].getValue('totalcostestimate');
As a side note it is always recommended to validate the returning value from a saved search result. Because on a successful search it returns you an array object on the other hand if no result founds it'll return you null.
//likely to get an error saying can't find length from null
for (var h = 0; h <= histSearch.length; h++) {
}
You can use something like this
// Never enter into the loop if it is null
for (var h = 0; histSearch!=null && h <= histSearch.length; h++) {
}
I am using the below code to assign a random class (out of five) to each individual image on my page.
$(this).addClass('color-' + (Math.floor(Math.random() * 5) + 1));
It's working great but I want to make it so that there are never two of the same class in a row.
Even better would be if there were never two of the same in a row, and it also did not use any class more than once until all 5 had been used... As in, remove each used class from the array until all of them have been used, then start again, not allowing the last of the previous 5 and the first of the next 5 to be the same color.
Hope that makes sense, and thanks in advance for any help.
You need to create an array of the possible values and each time you retrieve a random index from the array to use one of the values, you remove it from the array.
Here's a general purpose random function that will not repeat until all values have been used. You can call this and then just add this index onto the end of your class name.
var uniqueRandoms = [];
var numRandoms = 5;
function makeUniqueRandom() {
// refill the array if needed
if (!uniqueRandoms.length) {
for (var i = 0; i < numRandoms; i++) {
uniqueRandoms.push(i);
}
}
var index = Math.floor(Math.random() * uniqueRandoms.length);
var val = uniqueRandoms[index];
// now remove that value from the array
uniqueRandoms.splice(index, 1);
return val;
}
Working demo: http://jsfiddle.net/jfriend00/H9bLH/
So, your code would just be this:
$(this).addClass('color-' + (makeUniqueRandom() + 1));
Here's an object oriented form that will allow more than one of these to be used in different places in your app:
// if only one argument is passed, it will assume that is the high
// limit and the low limit will be set to zero
// so you can use either r = new randomeGenerator(9);
// or r = new randomGenerator(0, 9);
function randomGenerator(low, high) {
if (arguments.length < 2) {
high = low;
low = 0;
}
this.low = low;
this.high = high;
this.reset();
}
randomGenerator.prototype = {
reset: function() {
this.remaining = [];
for (var i = this.low; i <= this.high; i++) {
this.remaining.push(i);
}
},
get: function() {
if (!this.remaining.length) {
this.reset();
}
var index = Math.floor(Math.random() * this.remaining.length);
var val = this.remaining[index];
this.remaining.splice(index, 1);
return val;
}
}
Sample Usage:
var r = new randomGenerator(1, 9);
var rand1 = r.get();
var rand2 = r.get();
Working demo: http://jsfiddle.net/jfriend00/q36Lk4hk/
You can do something like this using an array and the splice method:
var classes = ["color-1", "color-2", "color-3", "color-4", "color-5"];
for(i = 0;i < 5; i++){
var randomPosition = Math.floor(Math.random() * classes.length);
var selected = classes.splice(randomPosition,1);
console.log(selected);
alert(selected);
}
var used = [];
var range = [0, 5];
var generateColors = (function() {
var current;
for ( var i = range[0]; i < range[5]; i++ ) {
while ( used.indexOf(current = (Math.floor(Math.random() * 5) + 1)) != -1 ) ;
used.push(current);
$(" SELECTOR ").addClass('color-' + current);
}
});
Just to explain my comment to jfriend00's excellent answer, you can have a function that returns the members of a set in random order until all have been returned, then starts again, e.g.:
function RandomList(list) {
var original = list;
this.getOriginal = function() {
return original;
}
}
RandomList.prototype.getRandom = function() {
if (!(this.remainder && this.remainder.length)) {
this.remainder = this.getOriginal().slice();
}
return this.remainder.splice(Math.random() * this.remainder.length | 0,1);
}
var list = new RandomList([1,2,3]);
list.getRandom(); // returns a random member of list without repeating until all
// members have been returned.
If the list can be hard coded, you can keep the original in a closure, e.g.
var randomItem = (function() {
var original = [1,2,3];
var remainder;
return function() {
if (!(remainder && remainder.length)) {
remainder = original.slice();
}
return remainder.splice(Math.random() * remainder.length | 0, 1);
};
}());