Google Sheets Script - Sort By Multiple, Dynamic Ranges - sorting

*TL:DR
I would like to sort an entire sheet automatically with the following conditions.
Sort entire sheet by columns 1 2 and 3
Move any rows with "Z Shipped" in column 1 to the bottom of the populated list (there are many rows with blank data in column 1, so I want "other data, Z Shipped data, blank rows".*
Hi there, I as the title states, I am trying to sort my google sheet through multiple ranges. I have pasted my current code below that I have found gets me close, however I keep getting a
"The coordinates of the range are outside the dimensions of the sheet."
error on the second "newRange.sort(newSortOrder)" line.
I am not sure if my current code is what I need to have or not. I have no expertise in this and just try to find functions that work online and am getting stuck on this one as it does not seem to be a common request.
function autosort() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sortRange = "A3:E";
var range = ss.getRange(sortRange);
var sortOrder = [
{column: 1, ascending: true},
{column: 2, ascending: true},
{column: 3, ascending: true}
];
range.sort(sortOrder);
var status = ss.getDataRange().getLastRow();
for (var i=status;i>=1;i--){
if(ss.getRange(i,1).getValue()!=='Z Shipped') {break;}
}
var newSortRow = i+1;
var newSortRange = "A3:E"+newSortRow;
var newRange = ss.getRange(newSortRange);
var newSortOrder = [
{column: 2, ascending: true},
{column: 3, ascending: true}
];
newRange.sort(newSortOrder);
}

EDIT: Code has been updated so that it can work around the Array Formulas I have calculating certain columns. Basically just had to identify the ranges around each array column.
Awesome thanks to #Boris Baublys for his help on this!
I did tweak the code a little bit as I was giving sample data, and my real-life data didn't work exactly with the code as it was supplied. There may be a way to clean up my tweaked code a bit more, but it worked for me!
Basically the new code does a final sort of the "non Z Shipped" data to put it into the proper order from there, as well as specifying the sheet you want sorted.
// Sort entire sheet by a specified sort order
function autosort() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet Name");
let sortRange = "A3:BH";
var range = sheet.getRange(sortRange);
let sortOrder = [
{column: 1, ascending: true},
{column: 11, ascending: true},
];
range.sort(sortOrder);
// Find the boundaries of a specified block of entries ("Z Shipped" here).
let Avals = sheet.getRange("A3:A").getValues();
let fAvals = [].concat(...Avals); // Make the array flat
let start = fAvals.indexOf("Z Shipped");
let end = fAvals.lastIndexOf("Z Shipped");
/////
// Load data with non-empty column A into an array
let Alast = Avals.filter(String).length + 2;
let vals1 = sheet.getRange("A3:O" + Alast).getValues();
// Move the "Z Shipped" block to the end of the array
let removed1 = vals1.splice(start, end - start + 1);
let sorted1 = vals1.concat(removed1);
sheet.getRange("A3:O" + Alast).setValues(sorted1);
/////
// Load data with non-empty column A into an array
let vals2 = sheet.getRange("S3:AW" + Alast).getValues();
// Move the "Z Shipped" block to the end of the array
let removed2 = vals2.splice(start, end - start + 1);
let sorted2 = vals2.concat(removed2);
sheet.getRange("S3:AW" + Alast).setValues(sorted2);
/////
// Load data with non-empty column A into an array
let vals3 = sheet.getRange("AY3:BD" + Alast).getValues();
// Move the "Z Shipped" block to the end of the array
let removed3 = vals3.splice(start, end - start + 1);
let sorted3 = vals3.concat(removed3);
sheet.getRange("AY3:BD" + Alast).setValues(sorted3);
/////
// Load data with non-empty column A into an array
let vals4 = sheet.getRange("BF3:BF" + Alast).getValues();
// Move the "Z Shipped" block to the end of the array
let removed4 = vals4.splice(start, end - start + 1);
let sorted4 = vals4.concat(removed4);
sheet.getRange("BF3:BF" + Alast).setValues(sorted4);
/////
// Load data with non-empty column A into an array
let vals5 = sheet.getRange("BH3:BH" + Alast).getValues();
// Move the "Z Shipped" block to the end of the array
let removed5 = vals5.splice(start, end - start + 1);
let sorted5 = vals5.concat(removed5);
sheet.getRange("BH3:BH" + Alast).setValues(sorted5);
/////
// Sort all the non "Z Shipped" items with a new sort order
let sortRange2 = ("A3:BH" + start);
var range2 = sheet.getRange(sortRange2);
let sortOrder2 = [
{column: 6, ascending: true},
{column: 11, ascending: true},
{column: 4, ascending: true}
];
range2.sort(sortOrder2);
}
Huge thanks again #Boris Baublys

"I want "other data, Z Shipped data, blank rows"
We sort in 2 stages.
First, sort entire sheet by columns 1, 2 and 3.
Then find the boundaries of the "Z Shipped" block and move it to the bottom of the populated list.
function autosort() {
let ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
let sortRange = "A3:E";
var range = ss.getRange(sortRange);
let sortOrder = [
{column: 1, ascending: true},
{column: 2, ascending: true},
{column: 3, ascending: true}
];
range.sort(sortOrder);
// Find the boundaries of the "Z Shipped" block
let Avals = ss.getRange("A3:A").getValues();
let fAvals = [].concat(...Avals); // Make the array flat
let start = fAvals.indexOf("Z Shipped");
let end = fAvals.lastIndexOf("Z Shipped");
// Load data with non-empty column A into an array
let Alast = Avals.filter(String).length + 2;
let vals = ss.getRange("A3:E" + Alast).getValues();
// Move the "Z Shipped" block to the end of the array
let removed = vals.splice(start, end - start + 1);
let sorted = vals.concat(removed);
ss.getRange("A3:E" + Alast).setValues(sorted);
}

Related

Google Apps Script - Copy Paste values on same page and in same row based on cell criteria

I have a table with data in columns A:Ak. Some of the data contains formulas and when a row of data is complete (i.e. the status in column W is "Y") I would like to copy that row in place and only past the values.
This is what I have tried:
function Optimize() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
// This searches all cells in columns W copies and pastes values in row if cell has value 'Y'
if (row[22] == 'Y') {
sheet.getDataRange(sheet.getCurrentCell().getRow() - 0, 1, 1, sheet.getMaxColumns()).activate();
sheet.getActiveRange().copyTo(sheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
rowsCopiedpasted++;
}
}
}
When I am in the sheet and click on the cell in column A of a row with a "Y" value this works, but I need it to go through the whole sheet and copy/paste values of all rows with "Y" in column W.
Try this:
function Optimize() {
const sh = SpreadsheetApp.getActiveSheet();
const rg = sh.getDataRange();
const vs = rg.getValues();
const lc = sh.getLastColumn();
let o = vs.forEach((r,i) => {
if(r[22] == "Y") {
sh.getRange(i + 1, lc + 1,1,lc).setValues([r])
}
});
}

Sorting Data Range Based on Arbitrarily Ordered String Key Through Google Scripts

I am trying to sort values of column A depending on the values at column B ordered by matching keys at column E expected results are in column C
I can achieve this through formula described here
=SORT(A2:A;ARRAYFORMULA(MATCH(B2:B;E2:E;0));1)
I am seeking a google script answer to enhance its automation by using it through onEdit trigger. I can sort column A according to column B alphabetically through this pseudocode snippet. I need help on attaching this the numeric order of sort key
var sortRange = sheet.getRange(2,1,lastRow,lastCol)
sortRange.sort({column: 2, ascending: false});
Thanks in advance
====== Update ======
Found a Solution Thanks to ziganotschka
// Retrieve Keys&Range
var sortKeys = ss.getRange(2, 5, 3, 1).getValues().flat();
var sortRange = ss.getRange(2,1,lastRow,2).getValues();
// Set an index to each row according to its relation to the search keys
for (var ii=0; ii < sortRange.length; ii++){
var index = sortKeys.indexOf(sortRange[ii][1]);
sortRange[ii].push(index);
}
// Sort the array ascending by index
activeCell.setValue(sortRange[0][2]);
sortRange.sort(function(a,b) {
var varA = a[2];
var varB = b[2];
return varA == varB ? 0 : varA > varB ? -1 : 1;
});
// Reduce the array to the sorted values of Column A
var sortedColumn = sortRange.map(function(i){return [i[0]];});
//set values into column C
ss.getRange(2, 6, sortedColumn.length, 1).setValues(sortedColumn);
If you want to sort by script instead of formula, you have to do it differently
sortRange.sort will sort you current spreadsheet contents rather than outputting the sorted version in a new column
Instead you need to retrieve the values as an array and perform a Javascript array sorting function on it: Array.prototype.sort()
Also, if you want to sort by searchkeys in column E, rather than alphabetically / numerically, you need to append to the retrieved array an index column based on a row's relation tot he search key
The index can be retrieved with Array.prototype.indexOf()
After you sort the three column array (column A, column B and the new index column), reduce the array to the sorted values column and set it into column C with setValues(values)
Sample:
function myFunction() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var lastRow = sheet.getLastRow();
var lastCol = 2;
var sortRange = sheet.getRange(2,1,lastRow-1,lastCol).getValues();
//retrieve the keys in column E
var keys = sheet.getRange(2, 5, lastRow, 1).getValues().flat();
for (var i =0; i < sortRange.length; i++){
//set an index to each row according to its relation to the search keys
var index = keys.indexOf(sortRange[i][1]);
sortRange[i].push(index);
}
//sort the array ascending by index
sortRange.sort(function(a,b) {
return a[2] - b[2]
});
//reduce the array to the sorted values of Column A
var sortedColumn = sortRange.map(function (i){return [i[0]];});
//set values into column C
sheet.getRange(2, 3, sortedColumn.length, 1).setValues(sortedColumn);
}

Simple nested for loop problems

I am very new to programming and I am trying to create a short macro on google sheets. I want to copy and paste a list of cells a certain number of times based on variables see here. On the first column you have a list of locations and the second column the number of times they should be pasted into a 3rd column (column F). For example i would like to paste A4 fives times into column F4. Then A5 twice into F8 (F4 + 5 rows) and so one until the end of my list.
I came up with the below code but it currently copies each locations of my list 5 times on the same rows (F4-F8) then repeat the same process on the following 5 rows, and so on. I think the issue is with the order of the loops but i can't figure it out really.
Another problem i have is that It only copies each location 5 times (B1), but i am not sure how to make my variable numShift an array for (B4-B16) so that each locations is copied the correct number of times
Any help would be really appreciated !
function myFunction() {
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet()
var activeSheet = ss.getActiveSheet()
var numShift = activeSheet.getRange(4,2).getValue()
for (var k=0;k<65;k=k+5){
for (var indexNumLocation=0;indexNumLocation<15;indexNumLocation++){
for (var indexNumShift=0;indexNumShift<numShift;indexNumShift++)
{var locationRange = activeSheet.getRange(4+indexNumLocation,1).getValue();
activeSheet.getRange(4+k+indexNumShift,6).setValue(locationRange);
}
}
}
}
Try this
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = sheet.getLastRow();
var data = [];
var i, j;
// get times count for each row from B4 to last row
var times = sheet.getRange(4, 2, lastRow - 4, 1).getValues();
// get the data that needs to be copied for each row from A4 to last row
var values = sheet.getRange(4, 1, lastRow - 4, 1).getValues();
// loop over each row
for (i = 4; i <= lastRow; i++) {
// run loop number of times for that row
for (j = 1; j <= times[i]; j++) {
// push the value that needs to be copied in a temporary array
data.push([values[i][0]]);
}
}
// finally put that array data in sheet starting from F4
sheet.getRange(4, 6, data.length, 1).setValues(data);
}

Returning A Sorted List's Index in Lua

I access object properties with an index number
object = {}
object.y = {60,20,40}
object.g = {box1,box2,box3} -- graphic
object.c = {false,false,false} -- collision
-- object.y[2] is 20 and its graphic is box2
-- sorted by y location, index should be, object.sort = {2,3,1}
I know table.sort sorts a list, but how can I sort the y list that returns index for the purpose of drawing each object in-front depending on the y location.
Maybe the quicksort function can be edited, I don't understand it.
http://rosettacode.org/wiki/Sorting_algorithms/Quicksort#Lua
https://github.com/mirven/lua_snippets/blob/master/lua/quicksort.lua
Is this possible?
Do not store the data as you're currently doing. Use something like:
object = {
{
y = 60,
g = box1,
c = false,
},
{
y = 20,
g = box2,
c = false,
},
{
y = 40,
g = box3,
c = false,
},
}
and then use the following callback function in table.sort:
function CustomSort(L, R)
return L.y > R.y
end
as shown below:
table.sort(object, CustomSort)
This should work:
local temp = {}
local values = object.y
-- filling temp with all indexes
for i=1,#values do
temp[i] = i
end
-- sorting the indexes, using object.y as comparison
table.sort(temp,function(a,b)
return values[a] < values[b]
end)
-- sorting is done here, have fun with it
object.sort = temp
temp will be {2,3,1} when using this code combined with yours.
#EinsteinK #hjpotter92 : Thank you
RESULT: This is the final version of the answers I received. My question is solved.
Use sortIndex(object) to get sorted list in object.sort . Update sort after objects move.
box1 = love.graphics.newImage("tile1.png")
box2 = love.graphics.newImage("tile2.png")
box3 = love.graphics.newImage("tile3.png")
hero = love.graphics.newImage("hero.png")
object = {
{ x = 200, y = 50, g = box1 },
{ x = 50, y = 100, g = box2 },
{ x = 150, y = 200, g = box3 },
{ x = 0, y = 0, g = hero }
}
function sortIndex(item)
-- Sort id, using item values
local function sortY(a,b)
return item[a].y < item[b].y
end
--------------------------------
local i
local id = {} -- id list
for i = 1, #item do -- Fill id list
id[i] = i
end
-- print( unpack(id) ) -- Check before
table.sort(id,sortY)-- Sort list
-- print( unpack(id) ) -- Check after
item.sort = id -- List added to object.sort
end
sortIndex(object) -- print( unpack(object.sort) ) -- Check sorted id's
function drawObject()
local i,v, g,x,y
for i = 1, #object do
v = object.sort[i] -- Draw in order
x = object[v].x
y = object[v].y
g = object[v].g
love.graphics.draw(g,x,y)
end
end

Ranking in Linq

There's a generic list of numbers, say
{980, 850,700, 680}---n nos.
I try to compare the above list with a decimal no. say 690., the O/p I need is,to get the ranking of the number which I'm gonna input("692). i,e the desired O/P should be Ranking ="4"
How can I get the O/p for above scenario..??
Following on from Alex's post I think you are looking for
var numbers = new List<int>() { 980, 850, 700, 680 };
var dec = new Decimal(692.0);
var temp = numbers.Count(x => x > dec) + 1;
this will return the position you are looking for
If you want to look for an exact match of a decimal input to a int on the list,you can use FindIndex.
var numbers = new List<int>() { 980, 850, 700, 680 };
var dec = new Decimal(680.0);
var res = numbers.FindIndex(x => x == dec);
It returns the 0-based position of the match.
Your question is not clear, i'm not sure what role 690 is playing.
Assuming that the user can ernter a number and you want to find the rank(index) of the number in the list when it would be inserted. Assuming also that your list should be sorted descending since you want the position of the new int according to it's value:
var input = 692;
var numbers = new List<int>() { 980, 850, 700, 680 };
var firstLower = numbers.OrderByDescending(i => i)
.Select((i, index) => new { Value = i, Index = index })
.FirstOrDefault(x => x.Value < input);
var rank = firstLower == null ? numbers.Count + 1 : firstLower.Index + 1;
Note that the OrderByDescending might be redundant if your list is already sorted, but i assume that your sample data is only sorted accidentally.

Resources