here my specific case:
I have some range protected in google sheets
I need to replace some specific Editor if is editor of those range (var Editor2Replace and Editor2Add are emails)
Logically I tried to, for each sheet:
Cycle (FOR) of all the protected range (counter p)
For each protected range catch current editors and have it in array
Of the Editors read the email ==> this is what generate the mistake
Cycle (FOR) all the editors looking if someone of those is == Editor2Replace (that
is an email)
Here the code, but something is logically wrong, I doubt in what is an array and what not..
var Protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var p = 0; p < Protections.length; p++) {
var Protection_Desc = Protections[p].getDescription();
var Protection_Editors = [];
var Protection_Editors = [Protections[p].getEditors()];
for (var r = 0; r < Protection_Editors.length; r++){
var Protection_Email[r] = [Protection_Editors[r].getEmail()];
if (Protection_Idontknow == Editor2Replace){
Protections[p].addEditor = Editor2Add;
Protections[p].removeEditor = Editor2Replace;
var Protection_Range = Protections[p].getRange();
var Protection_Row = Protection_Range.getRow();
var Owner1 = sheet.getRange(Protection_Row,5).getValue();
var Owner2 = sheet.getRange(Protection_Row,6).getValue();
if (Owner1 == Editor2Replace){
sheet.getRange(Protection_Row,5).setValue(Editor2Add);
}
if (Owner2 == Editor2Replace){
sheet.getRange(Protection_Row,6).setValue(Editor2Add);
}
}
}
Many thanks for hepling
There were a lot of issues in your script and I will enumerate them one by one. Also, I was able to replace a user in the protected sheet by modifying your script.
Issues:
Duplicate declaration
var Protection_Editors = [];
var Protection_Editors = [Protections[p].getEditors()];
Storing the returned value (array) in another array (which should not be done in your issue, it doesn't help you with anything)
var Protection_Editors = [Protections[p].getEditors()];
...
var Protection_Email[r] = [Protection_Editors[r].getEmail()];
Newly declared variable having an index (which I don't understand why)
var Protection_Email[r] = [Protection_Editors[r].getEmail()];
Variable not declared Protection_Idontknow
if (Protection_Idontknow == Editor2Replace){
Incorrect usage of methods addEditor and removeEditor
Protections[p].addEditor = Editor2Add;
Protections[p].removeEditor = Editor2Replace;
Below code should fix those issues (added some comments):
Code:
var Protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var p = 0; p < Protections.length; p++) {
var Protection_Desc = Protections[p].getDescription();
// returned value of getEditors is already an array, return as is
var Protection_Editors = Protections[p].getEditors();
for (var r = 0; r < Protection_Editors.length; r++) {
var Protection_Email = Protection_Editors[r].getEmail();
// compare current email with the one you want to replace
if (Protection_Email == Editor2Replace) {
// add new and remove the one to replace
Protections[p].addEditor(Editor2Add);
Protections[p].removeEditor(Editor2Replace);
}
}
}
Note:
I have removed anything that were unrelated to the replacement of editors.
Reference:
Protection
I'm looking for the fastest way to pick a random object that has a certain condition (from an array).
In the example below I have a multidimensional array, 50 * 50 that contains objects. I want to pick a random object from that array but that object needs to have a size larger than 100.
while (object.size <= 100)
{
attempts++;
object = grid_array[Math.round(Math.random() * 49)][Math.round(Math.random() * 49)];
}
Currently I have tested this and in some instances it takes over 300+ attempts. Is there a more elegant way to do this?
Thanks,
What I would do is first filter the source array to extract only valid candidates, then return a random one (if there are any).
For example:
function getRandomObject(grid_array:Array, minSize:Number):Object {
var filtered:Array = [];
for(var i:int = 0; i < grid_array.length; i++){
var inner:Array = grid_array[i];
for(var ii:int = 0; ii < inner.length; ii++){
var object:Object = inner[ii];
if(object.size >= minSize){
filtered.push(object);
}
}
}
return filtered.length ? filtered[int(Math.random() * filtered.length)] : null;
}
// example:
var object:Object = getRandomObject(grid_array, 100);
if(object){
// do stuff with `object`
}
I asked if you need the indexes because you could do this with RegExps and the JSON Class (Flash Player 11). With this example I stored the indexes of the objects:
Create random multidimensional Array to test the function
//---I stored a variable size and the indexes inside the Object
//---Size variable will be numbers between 0 and 500
var array:Array = [];
var i;
var j;
var size:uint = 50;
var obj:Object;
for(i = 0; i < size; i++){
array[i] = [];
for(j = 0; j < size; j++){
obj = new Object();
obj.size = Math.floor(Math.random() * 500);
obj.files = i;
obj.columns = j;
array[i][j] = obj;
}
}
Method to get random Object with size property bigger than 100
//---I'll use to search the object a JSON string
var str:String = JSON.stringify(array);
//---Function to get the random Object
function getRandom():Object{
//---RegExp to search object with size between 100 and 500
var reg:RegExp = /\{[^\}]*"size":(?:10[1-9]|1[1-9]\d|[2-5]\d\d)[^\}]*\}/g;
//---Get all matches
var matches:Array = str.match(reg);
//---Return a random match converted to object
//---If no match founded the return will be null
return matches ? JSON.parse( matches[Math.floor(Math.random() * matches.length)] ) : null;
}
I believe is something simple but obviously not simple enough :). Any ideas how to check if a value already exists in the Array before adding the value using FOR loop?
I have this so far and it doesn't work as I want to because the Array can contain duplicate values!
var n:int = 5;
var cnt:int;
for (var i = 0; i < n; i++)
{
cnt = randomThief();
for (var a = 0; a < loto5.length; a++)
{
if (loto5[i] == cnt)
{
loto5[i] = cnt;
}
}
}
You can use the indexOf() method of the Array class to check if the value exists like this :
var index:int = loto5.indexOf(cnt);
indexOf() returns a -1, if the value doesn't exist. Here is an example of how to do a check :
if (loto5.indexOf(cnt) >= 0)
{
// do something
}
for (var a = 0; a < loto5.length; a++)
{
cnt = randomThief();
if (loto5.indexOf(cnt) == -1) //if cnt isn't in array do ...
{
trace (cnt+" is not in Array");
loto5[a] = cnt;
}
}
Works, simple and beauty :)
How do you randomize an array using actionscript 3?
There is a short version using Array.sort() function:
var arr : Array = [0,1,2,3,4,5,6,7,8,9];
function randomize ( a : *, b : * ) : int {
return ( Math.random() > .5 ) ? 1 : -1;
}
trace( arr.sort( randomize ) );
If you don't get "enough" randomness you can sort twice :)
EDIT - explanation line by line:
For Array class method sort() you can pass not only sort options like Array.CASEINSENSITIVE, Array.DESCENDING and so on but also your own custom compare function reference (a callback) that accepts two parameters (two elements from array to compare). From AS3 documentation:
A comparison function should take two arguments to compare. Given the elements A and B, the result of compareFunction can have a negative, 0, or positive value:
A negative return value specifies that A appears before B in the sorted sequence.
A return value of 0 specifies that A and B have the same sort order.
A positive return value specifies that A appears after B in the sorted sequence.
Note: compare function parameters might be typed (if your array is typed) and have any name you want eg.:
function compareElements ( elementA : SomeClass, elementB : SomeClass ) : int;
This method is very useful when you need to sort array elements by their special properties. In randomization case compareFunction randomly returns -1, 0 or 1 and makes array elements to switch their places (indices). I have found that better randomization (in my subjective and mathematically untested opinion) is when method returns only -1 and 1. Also have in mind that sorting function with custom compare function doesn't compare elements sequentially so in some special cases randomization results may differ from what you might expect.
There's a better way that will also allow you to randomize the array in place, if you need that, and it will not make you create more then a single copy of your original array.
package
{
import flash.display.Sprite;
public class RandomizeArrayExample extends Sprite
{
public function RandomizeArrayExample()
{
super();
testDistribution();
}
private function testDistribution():void
{
var hash:Object = { };
var tester:Array = [1, 2, 3, 4];
var key:String;
for (var i:int; i < 1e5; i++)
{
randomize(tester);
key = tester.join("");
if (key in hash) hash[key]++;
else hash[key] = 1;
}
for (var p:String in hash) trace(p, "=>", hash[p]);
}
private function randomize(array:Array):Array
{
var temp:Object;
var tempOffset:int;
for (var i:int = array.length - 1; i >= 0; i--)
{
tempOffset = Math.random() * i;
temp = array[i];
array[i] = array[tempOffset];
array[tempOffset] = temp;
}
return array;
}
}
}
I had an alternative requirement where i wanted to randomly insert lots of source arrays into a target array randomly. Like Rytis i'm a big fan of the forEach, map and sort functions on Arrays.
var randomInsert:Function = function callback(item:*, index:int, array:Vector.<MyItem>):void
{
var j:Number = Math.floor(Math.random() * targetArray.length);
targetArray.splice(j,0,item);
}
targetArray = new Vector.<MyItem>();
sourceArray1.forEach(randomInsert, this);
sourceArray2.forEach(randomInsert, this);
here's an easier function. Works also on multidimensional arrays
function randomizeArray(array:Array):Array
{
var newArray:Array = new Array();
while (array.length > 0)
{
var mn=Math.floor(Math.random()*array.length)
newArray[newArray.length]=array[mn]
array.splice(mn,1)
}
return newArray;
}
I found this very helpful. I hope it can help you too.
// Array to Randomize
var firstArray:Array = ["One","Two","Three","Four","Five","six","seven","eight","nine","ten"];
trace(firstArray); // Prints in order
var newArray:Array = new Array();
function randomizeArray(array:Array):Array
{
var newArray:Array = new Array();
while (array.length > 0)
{
newArray.push(array.splice(Math.floor(Math.random()*array.length), 1));
}
return newArray;
}
var randomArray:Array = randomizeArray(firstArray);
trace(randomArray); // Prints out randomized :)
If you need your array to be shuffled (your elements can not repeat). You could use this function:
/**
* Shuffles array into new array with no repeating elements. Simple swap algorithm is used.
*/
public function shuffleArray(original:Array):Array
{
// How many swaps we will do
// Increase this number for better results (more shuffled array, but slower performance)
const runs:int = original.length * 3;
var shuffled:Array = new Array(original.length);
var i:int;
var a:int;
var b:int;
var temp:Object;
// Copy original array to shuffled
for(i=0; i<shuffled.length; i++){
shuffled[i] = original[i];
}
// Run random swap cycle 'runs' times
for(i=0; i<runs; i++){
// There is a chance that array element will swap with itself,
// and there is always small probability it will make your shuffle
// results not that good, hence try to experiment with
// different runs count as stated above
a = Math.floor(Math.random() * original.length);
b = Math.floor(Math.random() * original.length);
// Swap messages
temp = shuffled[a];
shuffled[a] = shuffled[b];
shuffled[b] = temp;
}
return shuffled;
}
Usage:
var testArray:Array = ["Water", "Fire", "Air", "Earth"];
trace(shuffleArray(testArray).concat());
this is how I randomize my array of 36 cards for a memory game
const QUANT_CARTAS: int = 36;
//get the 36 numbers into the array
for (var i: int = 0; i < QUANT_CARTAS; i++)
{
cartas.push(i);
}
//shuffles them =)
for (var moeda: int = QUANT_CARTAS - 1; moeda > 0; moeda--)
{
var pos: int = Math.floor(Math.random() * moeda);
var carta: int = cartas[moeda];
cartas[moeda] = cartas[pos];
cartas[pos] = carta;
}
// and add them using the random order...
for (i = 0; i < QUANT_CARTAS; i++)
{
var novaCarta: Carta = new Carta();
novaCarta.tipoCarta = cartas[i];
etcetcetc.............
}
choose random string from array
function keyGenerator(len:Number):String
{
function randomRange(minNum:Number, maxNum:Number):Number
{
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
var hexArray = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
var key = "";
for (var i=0; i<len; i++)
{
key += hexArray[randomRange(0,hexArray.length-1)];
}
return key;
}
usage:
trace(keyGenerator(16));
I want to create a handlebars helper that works like {{#each}} but gives me the possibility to specify a number so that every n iterations some additional code is run.
The reason I need this is that I need to spit out the content in rows of three items, so every three items I need to open and close a new container div.
Of course I could simply let backbone format the array in packages of three items and iterate over that using {{#each}} but I thought it would be more elegant to create a helper so that I can say something like
{{#each_pack data 3}}
<div class="container">
{{#each pack_items}}
<span>{{content}}</span>
{{/each}}
</div>
{{/each_pack}}
I'm not entirely sure how to do this.
How do I make pack_items available to the inside block?
I solved this in a way that lets me use the exact same syntax I just proposed.
Here is the code:
window.Handlebars.registerHelper('each_pack', function(context, packsize, fn){
var ret = '';
/*
Function that creates packages of size
packsize from a given array
*/
var packagify = function(array, packsize){
var i = 0;
var length = array.length;
var returnArray = [];
var pack = [];
while(i < length){
/*
If this is not the first entry,
if this is the packsize-d entry
or this is the last entry,
push the pack to the return array
and create a new one
*/
if(((i % packsize) == 0 && i != 0) || (i == (length - 1))){
returnArray.push(pack);
pack = [];
}
pack.push(array[i]);
i++;
}
return returnArray;
}
var packArray = packagify(context,packsize);
for(var i = 0; i < packArray.length; i++){
var pack = packArray[i];
this['pack_items'] = pack;
ret = ret + fn(this);
}
delete this['pack_items'];
return ret;
});