My task is to grab a 2-dimensional table from cells on a worksheet into a 2-dimensional array, delete some or all of the rows (right terminology?) from testing, and then paste what's left into a worksheet.
To determine the range for pasting I need to know the length of the edited array. This is where I'm challenged.
// This gets the array which is 3 columns wide and X rows (X will vary)
var termEmp = spreadsheet.getRangeByName("roeList").getValues();
// e.g. termEmp = [ ["Bob", 1, "day", "key"] ["Cindy", 2, "day", "it"] ["Laura", 1, "night", "we"] ]
// Then I find the number of rows that actually have data
numRows = termEmp[0].length; // result = 3
// A for loop with counter i tests if the second element equals 2 of each row and deletes each array row if it's there
// In this example I want to delete the row with Cindy because of the 2
// To do this is use the splice method to delete the second row thusly:
termEmp.splice(i,1); // i = 1 in the for loop
// After testing all elements, and deleting the rows I want, I then need to count the number of rows remaining (to create a range for pasting into the worksheet)
numRows = termEmp[0].length;
// This is SUPPOSED to count the number of rows remaining (first element is ALWAYS non-blank)
Here's my problem. For this example the number of rows after the splice goes from 3 to 2. I looked at the array to confirm this.
But in my code termEmp[0].length STAYS at 3. I can't use it to define my range for pasting.
What's needed to get the count right?
For number of rows, you can get the length of the full array.
var numRows = termEmp.length
What you're getting with termEmp[0].length is the number of columns in first row.
EDIT
OP indicated the answer "doesn't work" (which is false) however, as a courtesy here's subsequent code that helps his followup question to identify members in an array contain another array (effectively 2-dimensional spreadsheet data). The below code will take all memembers from termEmp that are an array, and inserts them into cleanedArray.
var cleanedArray = [];
for(var i=0;i<termEmp.length;i++){
var singleMember = termEmp[i];
if(Array.isArray(singleMember)){
//makes a clean array with only 2d values
cleanedArray.push(singleMember);
}
}
var numberOfMembers = cleanedArray.length;
Logger.log(numberOfMembers);
Related
I am trying to convert an old Excel VBA program to Google Sheets for projection 30 years of financial results. The key issue is the VBA Excel sheet haw a row (a1:ad1) of (30) cells that each contain the volatile function Rand() to generate a random number in each cell through each of 1000 loops:
In each loop these cells 1) reset their random number, then 2) some rows of calculations are made based on the new random numbers, then 3) then a resulting row of 30 new values in copy-pasted (values only) to another part of the sheet so the individual results can be statistically analyzed.
The copy-paste in google sheets is too slow and I hit a 6 minute execution time out, so I am trying to accumulate each loop into a single 2D array, each row being one of the 1000 loop results in 3 above,then write the entire array back to the sheet at once. To get the 30 cells to recalc, each time I read a row, I force a new value to be set in a single cell of the spreadsheet at the beginning of each loop. That seems to work, but trying move the data from the row to the accumulation array and then writing the values back to individual rows in the sheet at once, I always wind up with arrays the have too many dimensions some other problems getting the data back to the sheet as a 1000 x 30 range of static data.
Here's the basic outline of where I am so far after many variations of this :
function test1(){
var sheet=SpreadsheetApp.getActive().getSheetByName('UST_Results'),
range;
var values=[];
var LargeArray= Array(new Array()) //new Array()
for (var i=0;i<3;i++) { // using a small number (3) rows to get it work
values =getRandRow(sheet)[0][i];
LargeArray(LargeArray.length) = JSON.stringify(values)
};
};
function getRandRow(sheet) {
var values_array =[];
let myData =[];
var cell = 0;
// force the Rand() to recalc before getting the next copy of A1:Ad1
for (var i=0;i<1;i++) {
var cell = sheet.getRange(i+5,1);
cell.setValue(i);
SpreadsheetApp.flush();
}
range = sheet.getRange('A1:ad1');
values_array = range.getValues();
myData.push(values_array);
return myData;
}
A picture of the sheet testing with just the rand numbers and not the derived row.
enter image description here
Let's say I have 5 arrays of different sizes. All of the arrays have the same number of columns, in my case 2, but a different number of rows. I need to find the elements of the rows that appear in at least 3 of such arrays.
Right now, I compare two arrays using ismember, then compare that result with the third array and then save the row values which occur in all the three arrays. I do this for every possible combination of 3 arrays; basically in my case, I have 10 of such operations in total. It's like choosing three out of 5 without repetitions.
It works, but I am looking for a more efficient implementation. In particular, I was looking for any implementation that can perform this by voting. It's like having sets of different sizes and trying to find the elements that appear in the majority of sets, in my case 3 arrays out of 5.
Not sure what you mean by voting but I think does what you are looking for.
It creates 1 big unique matrix of all the rows from the arrays. The does an ismember by rows of the unique array with each individual array. The ismember is summed together to get a count of how many times each unique row exists across your set of arrays.
You can then use that count to return a new array that has at least minNum occurrences.
You would call it like this:
>> [outRows, uRows, memCount]= getRowDuplicates(3,a,b,c,d,e)
Where a,b,c,d,e are you arrays and 3 is the minimum number of occurrences
function [outRows, uRows, memCount]= getRowDuplicates(minNum,varargin)
uRows = unique(vertcat(varargin{:}),'rows');
memCount = false(size(uRows,1),1);
for j = 1:nargin-1
memCount = memCount + ismember(uRows,varargin{j},'rows');
end
rowIdx = memCount >= minNum;
outRows = uRows(rowIdx,:);
Thanks to Aero Engy for the solution! This is my own attempt, after rewriting my initial, not very efficient, implementation. I thought someone might find it useful:
function [majorityPts] = MajorityPointSelection(Mat, numMajority)
result = vertcat(Mat{:});
allUniq = unique(result(:,1:2),'rows');
numUniq = size(allUniq,1);
allUniq = [allUniq zeros(numUniq,1); zeros(size(result,1)-numUniq, 3)];
for i = 1:numUniq
sumNumRow = sum(result(:,1:2) == allUniq(i,1:2),1);
allUniq(i,3) = sumNumRow(1);
end
allUniq(numUniq+1:end,:) = [];
majorityPts = allUniq(allUniq(:,3)>=numMajority,1:2);
end
Here Mat is a cell that contains all of the arrays that I want to compare in order to find the rows that appear in at least numMajority of them, where in my case numMajority = 3. Basically, I first dump all of the arrays in one big matrix (result), and then find the unique rows of this matrix. Finally, I count the number of each unique row and return the points that appear in the majority number of arrays as majorityPts.
In the function below, I grab data that is in multiple columns via a form response on my second sheet and place the information on my first sheet organized in rows.
I would like to have the first blank column after the data, currently G on my new sheet editable so that someone can come in and "approve" the contents of each row. Right now, when this script runs, it overwrites the contents of Column G. I thought the number 6 in the line with sh0.getRange(2, 1, aMain.length, 6).setValues(aMain); was telling the script to only put data into 6 columns... looks like that's not the case.
I also thought that I may be able to do a workaround by changing that line to sh0.getRange(2, 2 ... it would let me keep the first column as an editable column... that didn't work either.
Any suggestions to allow me to use this script and keep a column editable?
function SPLIT() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh0 = ss.getSheets()[0], sh1 = ss.getSheets()[1];
// get data from sheet 2
var data = sh1.getDataRange().getValues();
// create array to hold data
var aMain = new Array();
// itterate through data and add to array
// i is the loop, j=3 is the column it starts to loop with, j<9 tells it where to stop.
// in the aMain.push line, use data[i][j] for the rows to search and put in the one column.
for(var i=1, dLen=data.length; i<dLen; i++) {
for(var j=5; j<9; j++) {
aMain.push([data[i][0],data[i][1],data[i][2],data[i][3],data[i][4],data[i][j]]);
}
// add array of data to first sheet
// in the last line, change the last number to equal the number of columns in your final sheet.
// the first number in getrange is the row the data starts on... 1 is column.
sh0.getRange(2, 1, aMain.length, 6).setValues(aMain);
}
}
I have a matrix that looks like :
A =[
1 5
2 10
3 12
4 25
5 8]
Let's assume that I want to remove the rows that contain elements that are larger than 10. I have been trying to use a for loop and simply read the matrix and use
for ii = 1:5
for jj = 2
if A(ii,jj) > 10
A(ii,2) = [];
end
end
end
The problem is that, I keep receiving errors regarding the size of a matrix. As one row is deleted, the size of the matrix is reduced and I know that I have to set size(A)=size(A)-1 but it doesn't work. Any help is appreciated!
You can't remove a single element when using subscript notation, so your error is here:
A(i,2) = [];
You have two choices, either remove the entire row:
A(i,:) = [];
or else linearise your matrix and remove single elements (but then you won't be able to recover you original shape:
for ii = numel(A):-1:1
if A(ii) > 10
A(ii) = [];
end
end
but using a loop for this is unnecessary and probably inefficient. You can do it using logical indexing like so:
A(any(A'>10),:) = [];
to remove the whole row or else to just remove single elements try:
A(A>10) = []
but then you'll see that you end up with a row vector (linearized) result
Here is a solution, quite similar to the one of #Dan. Basically this keeps what you want instead of removing what you don't want:
A(all(A<=10,2),:)
Let's say I have some code like:
private function makeGrid():void
{
_grid = new Array();
for(var i:int = 0; i < stage.stageWidth / GRID_SIZE; i++)
{
_grid[i] = new Array();
for(var j:int = 0; j < stage.stageHeight / GRID_SIZE; j++)
{
_grid[i][j] = new Array();
}
}
}
I don't quite understand what's going on. I get that in the first for loop it determines the number of columns needed, and in the second it determines rows, but I don't get why I'm making arrays out of _grid[i] and _grid[i][j].
For instance, _grid[i] = new Array(); get's called 16 times (800px/50px), so that would make 16 arrays right? Why do I need those if the second for loops is already calculating the amount of rows I need?
I'm just going to elaborate on what has already been commented. Let's say that you are creating a 2D grid formed of rows and columns and you wanted to store some sort of data at each "cell" or specified index of the grid.
The first step is to create the first array to hold either the rows or columns (which you choose first doesn't really matter as you can adjust the for loops either way).
The first for loop creates a new row, then in the next inner loop you fill all the columns of that row (if we had chosen columns to be created first then we would fill all the rows of the columns). In this case the inner loop is creating all the columns with another array (making it a 3-dimensional array as mentioned in the comments).
The reason for doing this is for organization and easy look up. If you wanted to see the data stored in the 1st column of the 3rd row it would be as easy as doing _gird[2][0].
Now as to why a 3rd dimension is made as in _grid[i][j] = new Array(); that is specific to what kind of data needs to be stored at that row and column.