Scope Issue: double array not accessible in if statement - c

I have a class method that is supposed to take an object and populate a few values in place. This is at the stage of functional demonstration, so the implementation will be better later. right now I just would like this to work.
In the code below, the districtID integer is successfully passed to the if statements. The rgb double array does not make it into the if statement scope. The values set at initialization make it all the way to the districtPoint.color without getting set inside the if statement.
the code below will not compile as is. I would like to know how to get the rgb variable to be visible within the if statement scope.
(note: I tried the naive solution of initializing the variables within the if statement. This clears the error, but doesn't let the new rgb variables out of the if scope)
// This method populates properties
+(void)setContantPropertiesForID:(DistrictPoint *)districtPoint
{
int districtID = [districtPoint.districtID intValue];
double rgb[3] = {0,0,0};
if (districtID == 1) {
districtPoint.title = #"District 1";
rgb = {1.0,0.0,0.0}; // error is expected expression
} else if (districtID == 2) {
districtPoint.title = #"District 1";
rgb = {0.0,1.0,0.0};
} else if (districtID == 3) {
districtPoint.title = #"District 1";
rgb = {0.0,0.0,1.0};
} else {
districtPoint.title = nil;
rgb = {1.0,1.0,1.0}; // error condition
}
districtPoint.color = [UIColor colorWithRed:rgb[0] green:rgb[1] blue:rgb[2] alpha:0.5];
}

This has nothing to do with the if statement. You can use the curly-braces notation to set an array's elements only when initializing (as you do, in fact, earlier in the code).

Related

Google Active Script: using map in an array with 2 arguments

This works:
Suppose I want to use in an ArrayFormula() the indirect() function, which doesn't work. That is, supposed I type this in cell E1, =ArrayFormula(indirect(address(row(E:E),column(A:A)))), this will return the value of cell A1 in all the cells in column E. To circumvent this, I created the custom function below:
function retValue(cell){
if(cell.map) {
return cell.map(retValue);
} else {
var cellRang = SpreadsheetApp.getActive().getRange(cell);
return cellRang.getValue();
}
}
Now, when I enter this =ArrayFormula(retValue(address(row(E:E),column(A:A)))) in cell E1, each cell in column E will have the corresponding value of the cell in the same row of column A.
My problem:
What I need is to have a custom function that receives 2 arguments, like function retValue2(cell, anotherRange) but I only care if cell is an array, as anotherRange must be an array anyway. What happens is that when I iteratively call cell.map(retValue2) the argument anotherRange is lost and I'm not entirely sure how to go about this.
I tried to come up with this:
function retValue2(cell, anotherRange) {
if (cell.map) {
return cell.map(retValue2);
} else {
var range = SpreadsheetApp.getActive().getRange(anotherRange);
var nrRows = range.getNumRows();
var nrCols = range.getNumColumns();
return cell + ',' + nrRows + ',' + nrCols;
}
}
But it fails because anotherRange is not recognized inside the iteration I think. How do I solve this?
PS.: in the example that works, why exactly does it work at all? I understand that when I do this return cell.map(retValue); it will use my own function as the callback, which would return all the values in the array, but in the spreadsheet it shows only the one on the same row. What is the magic here?
EDIT:
My end goal is to create my own lookup function where I pass a search key and a 2-dimensional array (rows and columns) and then, it locates the coordinates of that key in the array.
Look here:
function retCoord(sKey, sIRange) {
try {
var key = SpreadsheetApp.getActive().getRange(sKey).getValue();
}
catch(e) {
var key = sKey;
}
var range = SpreadsheetApp.getActive().getRange(sIRange).getValues();
nbRow = range.length;
nbColumn = range[0].length;
for(var i = 0; i<nbRow; i++){
for(var j = 0; j<nbColumn; j++){
if(range[i][j] == key){
return i + ", " + j;
}
}
}
}
If in my spreadsheet I enter something like =retCoord("K4","A:L") it will search the content of cell K4 in my 2-dimensional array A:L and return where in the array the value is, 1, 2 for example. It also works if I use =retCoord(K4,"A:L") or =retCoord("term searched","A:L"), and in this latter case I enter directly the term searched. This works fine until I use it in an ArrayFormula().
First, instead of =retCoord("K4","A:L") I could very well use =retCoord(address(4, 11),"A:L") for instance and my .getRange() method would get the cell K4 just fine.
Now, here is the big problem. I want to use my function in an ArrayFormula(), and, positioning my cursor in O1 and hoping to search the items from column D in the columns E through L I want to pass as one of the inputs of address(), row(O:O), like this: =ArrayFormula(retCoord(address(row(O:O),4,4),"E:L")), meaning that for each row, a new address is passed. that is, in O1 cell, it should return the result of retCoord(D1,"E:L"), in O2 should be retCoord(D2,"E:L"), in O3 should be retCoord(D3,"E:L") and so on.
The issue happens because in my function, sKey is an array and if I try to use the same approach as my function retValue (here above in the This works: section) it fails because now, in retCoord, I have 2 inputs, and the introspection function calling from before fails because of the second input. Of course I'm missing something and there is always a better and more elegant way to approach a problem. But for now, can anyone help me with this one?
EDIT2:
I changed the code a little and it seems I moved forward but not quite yet. Check comment below for line indicated by (*):
function retCoord(sKey, sIRange) {
var key = '';
try {
key = SpreadsheetApp.getActive().getRange(sKey).getValue();
return key;
}
catch(e) {
if (sKey.map) {
var objKey = sKey.map(retCoord);
return objKey; // (*) <--- comments below
key = objKey;
} else {
key = sKey;
}
}
var range = SpreadsheetApp.getActive().getRange(sIRange).getValues();
nbRow = range.length;
nbColumn = range[0].length;
for(var i = 0; i<nbRow; i++){
for(var j = 0; j<nbColumn; j++){
if(range[i][j] == key){
return key + " = "+ i + ", " + j;
}
}
}
}
This (*) line I added only to see what was returning from the map. Surprisingly (in a way), it is an object with all the elements of that column and that is expected. What I didn't expect was that if I return that object to my spreadsheet cell, it brings back only the value of that specific cell (as I wanted but not really as expected). But the problem is I cannot use that object to compare against a string as it will expand and become something else and will never match. Look:
If I do return objKey in my custom function, over cell O1 it returns "a", in O2 it returns
"b", in O3 it returns "c" as expected because those are the values
of my cells D1, D2, D3 respectively.
If I do return "-> " + objKey
in in my custom function, instead of returning -> a, -> b, -> c in O1, O2, O3 respectively, it returns -> =A:A,a,b,c,d,e,f for all the cells in column O, which seems it did some type of objKey.toString() under the hood before concatenating with "-> "
Conclusion: how do I "coerce" the apparent result of objKey into string keeping the apparent result when you return the object without changing it? Simply put, I want the concatenation "some string" + to be equal to "some string" + "one string representing the value in that row instead of an object". In other words, what the hell is happening here? How does Google Sheets now that in that row, that element is the one representing the one I want? This is what I asked in the "PS." in the first part of this post.
You want to search all keys in the sKey column within the Range sIRange and note the position of each key within the range into the corresponding row in a destination column ?
This is how you can do it with Apps Script without formulas:
function retCoord(sKeyColumn, destinationColumn, sIRange) {
var key = sKeyColumn;
var range=SpreadsheetApp.getActive().getRange(sIRange);
var rangeValues = SpreadsheetApp.getActive().getRange(sIRange).getValues();
var nbRow = rangeValues.length;
var nbColumn = rangeValues[0].length;
var sKeyRange=SpreadsheetApp.getActive().getRange(sKeyColumn);
var destinationRange=SpreadsheetApp.getActive().getRange(destinationColumn);
var sKeyValues=sKeyRange.getValues();
for(var k=0;k<sKeyValues.length;k++){
for(var i = 1; i<=nbRow; i++){
for(var j = 1; j<=nbColumn; j++){
if(range.getCell(i, j).getValue() == sKeyValues[k][0]){
destinationRange.getCell(k+1, 1).setValue(range.getCell(i, j).getA1Notation());
}
}
}
}
}
Sample call:
function myFunction(){
retCoord('A1:A6','B1:B6','C1:J7');
}
retCoord('A:A','B:B','C:J'); would also work but would take very long, since the code would also loop through empty rows

Angular - Objects seem bound to eachother but arent

I have an edit page where the user can edit a file in the system, and then save it. When loading the file, I make two objects out of the result, one is bound to the view and the other I wish to keep (in its original state) until "save" is clicked, and then use it to compare vs the view-bound object, to see if any changes have been made.
So, when the page loads, this is being run
$http.get('/api/files/' + $stateParams.id)
.then(function (result) {
vm.fileTemp = result.data;
vm.fileTempCopy = result.data;
The fileTempCopy is not being touched or referenced by anything in the view or elsewhere in the controller, except in the save-method, where i check if they are alike or not. But somehow, both of them are updated when i make changes to the input fields (as if they were both used as ng-model for the inputs).
if(vm.fileTemp === vm.fileTempCopy)//in save-function
is always true, and their fields are exactly the same.
Why does this happen and how can I solve it?
Using the assignment operator, you are actually just referencing the original array. Arrays are reference types. That means, that they don't actually store values, they only store references to those values. What you where doing is copying a reference to a memory location, meaning that any changes to the memory at that location (including removing elements) will be reflected in both arrays.
So you will want to do this instead:
vm.fileTemp = angular.copy(result.data);
vm.fileTempCopy = angular.copy(result.data);
here is a very basic approach to checking an object's "value equality".
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
//After your update Outputs: false
console.log(isEquivalent(vm.fileTemp, vm.fileTempCopy));

Drupal site received url request embedding suspicious codes presuming attempt of hacking

I found a url request having suspicious code to one of my Drupal site. Will someone explain what will be the depth of this code and advise any precautions to be taken. Code:
function (){try{var _0x5757=["/x6C/x65/x6E/x67/x74/x68","/x72/x61/x6E/x64/x6F/x6D","/x66/x6C/x6F/x6F/x72"],_0xa438x1=this[_0x5757[0]],_0xa438x2,_0xa438x3;if(_0xa438x1==0){return};while(--_0xa438x1){_0xa438x2=Math[_0x5757[2]](Math[_0x5757[1]]()*(_0xa438x1 1));_0xa438x3=this[_0xa438x1];this[_0xa438x1]=this[_0xa438x2];this[_0xa438x2]=_0xa438x3;};}catch(e){}finally{return this}}
Site returned page not found error and I observed no issues.
Run this code through a beatifier and you will receive:
function () {
try {
var _0x5757 = ["/x6C/x65/x6E/x67/x74/x68", "/x72/x61/x6E/x64/x6F/x6D", "/x66/x6C/x6F/x6F/x72"],
_0xa438x1 = this[_0x5757[0]],
_0xa438x2, _0xa438x3;
if (_0xa438x1 == 0) {
return
};
while (--_0xa438x1) {
_0xa438x2 = Math[_0x5757[2]](Math[_0x5757[1]]() * (_0xa438x1 1));
_0xa438x3 = this[_0xa438x1];
this[_0xa438x1] = this[_0xa438x2];
this[_0xa438x2] = _0xa438x3;
};
} catch (e) {} finally {
return this
}
}
First, let's rename some variables and decrypt the array of strings in the third line. I've renamed _0x5757 to arr and escaped the hex-chars within the array. That gives you:
var arr = ["length", "random", "floor"],
So here we have a list of functions that will be used shortly. Substitute the strings in and rename the variables and you will receive:
function () {
try {
var arr = ["length", "random", "floor"],
length_func = "length",
rand_number, temp;
if (length_func == 0) {
return
};
while (--length_func) {
rand_number = Math["floor"](Math["random"]() * (length_func 1));
temp = this[length_func];
this[length_func] = this[rand_number];
this[rand_number] = temp;
};
} catch (e) {} finally {
return this
}
}
Notice how there is a syntax error in the script when generating a random number.
* (length_func 1)
with length_func = "length" is not valid JavaScript syntax, so the code is actually not functional. I can still make a guess on what it was supposed to do: If we remove the obfuscation of calling a function by doing Math["floor"] instead of Math.floor() the important lines are
while (--length_func) {
rand_number = Math.floor( Math.random() * ( length 1 ));
temp = this.length_func;
this.length_func = this.rand_number;
this.rand_number = temp;
};
It seems that it tries to compute a random integer using Math.random() and Math.floor(), then swaps the contents of the variables length_func and rand_numerber, all wrapped in a while(--length_func) loop. There's nothing functional here or anything that makes sense. An attempt at an infinte loop hanging the browser maybe? The code is, as it stands, non-functional. It even fails to generate a random number, because Math.floor() will always round-down the inputted float, and Math.rand() will generate a number within 0.0 to 1.0, so nearly always something slightly below 1.0, therefore rand_number = 0 for most of the time. The multiplication with the rand() output with the length_func 1 maybe should have made the number bigger, but the syntax is invalid. When I use my browser's console to execute length, it gives me 0, when I try to do length(1), then length is not a function, the only length that makes sense here is a string-length or array length, but then it would have to explicitly be "someString".length. Hope this helps you.

Using array to change ToggleButton visibility on/off (AS3/Flex)

So I'm trying to be able to change the visibility of an array of togglebuttons that I have right now. Using the flex interface tool, I made 10 toggle buttons and named their caller IDs b1,b2,b3...etc. I then placed those IDs into the array I mentioned. I also have a numeric stepper which I changed the called ID to numericstepper.
var buttonArray: Array= new Array (b1,b2,b3,b4,b5,b6,b7,b8,b9,b10)//global ;
protected function numericstepper_changeHandler(event:Event):void {
var x:int=0
var y:int
x=numericstepper.value //the value of the numericstepper
for (y=0; y<x; y++) {
buttonArray[y].visible= false // trying to change the visibility of each button in the array
}
But this doesn't work and gives me an error. Actually, even when I just trace buttonArray[1] it gives me null....not sure what to do here.
Newbie coder here so please advise!
ok I probably know what the problem is: you are creating the array variable in a component, but at that time (on construction of the component) that buttons are uninitialized yet (i.e == null). Common practice is to wait for some of the component's life cycle event before working with inner components. Usually it is the creationComplete event.
something like this:
<?xml version="1.0"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onCreationComplete()">
<fx:Script><![CDATA[
private var buttonArray:Array;
private function onCreationComplete():void {
buttonArray = [b1, b2, b3, b4, b5, b6, b7, b8, b9, b10];
}
protected function numericstepper_changeHandler(event:Event):void {
const x:int = numericstepper.value;
for (var i:int = 0; i < x; i++) {
buttonArray[i].visible = false;
}
}
]]></fx:Script>
<!-- inner components here -->
</s:Application>
It is indeed a newbie error. You need to understand the difference between variables and objects. b1 is a variable, a ToggleButton is an object, a variable holds a reference to an object but is not the object itself, it is just a reference. So in your case you correctly add your variable to an array but at this point those variable do not reference any objects so in that case their default value (what they really reference) is "null". null is a special type of object that has no methods and no properties and can be used in place of any complex object.
When you add those variables b1, b2, etc to an array you truly add what they reference, you do not add the variable themselves to the array so whatever the variable reference get in the array so in your case it's 'null'.
To make sure you add the ToggleButton objects you can simply delay everything and check for value for example:
var buttonArray: Array;
protected function numericstepper_changeHandler(event:Event):void
{
if(!buttonArray && b1)//b1 is not null so time to fill the array
{
buttonArray = [b1,b2,b3,b4,b5,b6,b7,b8,b9,b10];
}
else if(!b1)
{
return;//still no b1 so exit.
}
const x:int = numericstepper.value;
for (var i:int = 0; i < x; i++)
{
buttonArray[i].visible = false;
}
}

How would I remove a "row" in an array depending on the value of an element?

Here's what I'm currently doing/trying to do to accomplish my goal. But it is not removing the "row" the way I would like it too.
So, I'm making an object, then pushing it into an array. And the adding to the array part works fine and just as I expect.
var nearProfileInfoObj:Object = new Object();
nearProfileInfoObj.type = "userInfo";
nearProfileInfoObj.dowhat = "add";
nearProfileInfoObj.userid = netConnection.nearID;
nearProfileInfoObj.username = username_input_txt.text;
nearProfileInfoObj.sex = sex_input_txt.selectedItem.toString();
nearProfileInfoObj.age = age_input_txt.selectedItem;
nearProfileInfoObj.location = location_input_txt.text;
nearProfileInfoObj.headline = headline_input_txt.text;
theArray.push(nearProfileInfoObj);
So after that later on I need to be able to remove that object from the array, and it's not working the way I'm expecting. I want to take a variable whoLeft and capture their ID and then look in the array for that particular ID in the userid part of the object and if its there DELETE that whole "row".
I know you can do a filter with an array collection but that doesnt actually delete it. I need to delete it because I may be adding the same value again later on.
whoLeft = theiruserIDVariable;
theArray.filter(userLeaving);
public function userLeaving(element:*, index:int, arr:Array):Boolean
{
if (element.userid == whoLeft)
{
return false;
}
else
{
return true;
}
}
But this doesnt seem to be deleting the whole row like it implies. Does anyone know what i'm doing wrong?
Instead of modifying the original array, the new filtered array is returned by the filter method. So you need to assign the returned array to theArray.
Try this
theArray = theArray.filter(userLeaving);
EDIT This turned out to be slower than for loop:
An alternative to the hand coded loop could be something like this:
theArray.every(searchAndDestroy);
public function searchAndDestroy(element:*, index:int, arr:Array):Boolean
{
if (element.userid == whoLeft)
{
arr.splice(index,1);
return false;
}
return true;
}
As far as I know, every() terminates the first time the test function returns false. So the question is: for a big list, which is faster, the for loop or the loop that every() does with the overhead of the test function call.
EDIT #2 But this was faster than a for loop for a test I ran on an array of a million Points:
for each(var element:Object in theArray)
{
if (element.userid==whoLeft)
{
theArray.splice(theArray.indexOf(element),1);
break;
}
}
I think this is what you're looking for:
for(var i:uint = 0, len:uint = theArray.length; i<len; i++)
{
if(thisArray[i].id == whoLeft.id)
{
thisArray.splice(i, 1);
break;
}
}
However, do you really need it in an Array because you could always use a Dictionary which would mean accessing it by id which would be a lot simpler to remove.

Resources