I have a Google Sheets spreadsheet in which I'd like to pas a range of that row as an array to a custom function (for example, d37:m37). This works fine if you pass a range of a column (for example, d4:d37), but apparently rows behave differently than columns in this regard.
The only workaround I have found is to pass a two dimensional range (for example, d36:m37) and then within the custom function ignore all but the last row of this 2 dimensional "array".
Is there no way to get the same behavior from rows as from columns, and simply pass a range of a row in and have it treated as an array?
When you call a custom function with a range as a parameter, the function gets a two-dimensional array. For example,
range A1:B2 is represented as [['a1', 'b1'], ['a2', 'b2']] (where the strings are placeholders for actual content of those cells)
range A1:B1 (a row) is represented as [['a1', 'b1']]
range A1:A2 (a column) is represented as [['a1'], ['a2']]
as an exception, a single-cell range, such as A1, passes the value in that cell directly, not an array.
As you can see, the array structure is different for row-ranges and column-ranges. A custom function can be written to handle all of the above cases appropriately. But if your custom function is such that it expects a column range, there is an easy workaround: use the transpose to turn a row into a column, e.g.,
=customfunction(transpose(D37:M37))
Related
In Google Sheets "Apps Script" i defined a custom function that receives a format, and some values, then replaces the values into the format's placeholders and return it.
function PRINT_DETAILS(format, productName, quantity) {
/* format expects a string with placeholders like: "{productName} {quantity}"
product: expects a string
quantity: expects an integer
*/
}
In the Products "table" there are the following columns: name, quantity, price, etc.
Each of those have their own named range.
I defined a "print" column where i call the function like this:
= PRINT_DETAILS("{productName} {quantity}"; Products.Name; Products.Quantity)
The problem is that my function is receiving the whole named range, and not just the cell value of the same row.
i.e. im receiving all the products names and all the products quantities...
I successfully used named ranges as cell values in lot of ways: adding them, in "IF" conditions, concat operator "&", etc.
Even in standard functions calls.
Thats why i believe there would be a way to do the same in custom functions.
I wouldnt like to sacrifice readability using cell references like A1 or something.
So im looking for a way to constrain arguments, or get my desired values in a non too-hacky way.
Also if we could get a look into Google Sheets source code, maybe those standard functions are also implemented in JavaScript and they have this already solved.
Description
Excel has a feature where a value of a named range can be passed to a custom formula corresponding to the row the formula is in. Unfortunately Google Sheets does not have the feature. Instead you need to get the row number toof the formula.
Note that this assumes the named ranges start in row 1 and the formulas are on that same row in any sheet.
Thanks to Mike Steelson I have modified the script slightly to improve the script. But not the screeen shot.
=PRINT_DETAILS("{productName} {quantity}",Products.Name,Products.Quantity)
Screen shots
Script
function PRINT_DETAILS(format,productName,quantity) {
try {
var row = SpreadsheetApp.getActiveRange().getRow();
return [productName[row-1],quantity[row-1]];
}
catch(err) {
return err.message;
}
}
Reference
https://support.google.com/docs/thread/92023000?authuser=0&hl=en
The A column is first name and the B column is last name in the spreadsheet I'm manipulating. In the other sheet it's the full name(but not necessarily just firstname + lastname)
HC is the spreadsheet I need to get the data from. This code will work if the person has a unique first name that is the same in both sheets : =VLOOKUP(""&A2&"",HC!$C$1:$I$400,7,False) but this isn't enough in my case. The *s known as wildcards check if this string is contained in another string regardless of it's position.
I tried =VLOOKUP(""&A5&" "&B5&"",HC!$C$1:$I$400,7,False) but that means the two strings have to occur one after the other. I need it to return if search_key1 is contained within the string regardless of accents(á or a) or case (A or a) and the same for search_key2 e.g. the string could be "Seárch_key1 kdslfj SEARCH_KEY2 akldfj" and this would return True and then give the 7th column.
function VLOOKUP2(search_key1,search_key2,range,index){
//is search_key1 contained in one of the cells in the range if so is search_key2 also contained in one of the cells in the range regardless of capitilization and diacritical marks
// return the column specified by the index parameter
}
I know you're asking for a custom function, but I think you could do this with two existing vlookup functions.
If your data on sheet HC looks like this:
Then on your lookup sheet, try this in cell C1 (deleting everything below):
=arrayformula({"Result";if(A2:A<>"",iferror(iferror(vlookup("*"&A2:A&"*"&B2:B&"*",{HC!C:I},7,0),vlookup("*"&B2:B&"*"&A2:A&"*",{HC!C:I},7,0)),),)})
HELP NOTES
The inital vlookup tries to match "*"&A2:A&"*"&B2:B&"*" in {HC!C:I}. {} is used to create an array.
If not, then the iferror tries another vlookup to match "*"&B2:B&"*"&A2:A&"*".
Another iferror deals with no match, removing #N/A.
Then the if(A2:A<>"" only tries the above formula if there is a corresponding value in the same row, in Col A.
The final array {} places "Result" at the top of the column, with the main formula below. After the heading text, ; forces a return in the array.
Within the vlookup, 0 denotes false. 1 would be true.
Supposing, you have a list of data in a sheet1, now, you need to copy the rows which contain the text “Complete” in Column E to another new sheet2 and text “Not start” in Column E to another new sheet3,text “Prorees” in Column E to another new sheet4,text “Not Complete” in Column E to another new sheet5
How could deal with this job in Google Apps Script?
Link: https://docs.google.com/spreadsheets/d/1C_O_L1W828Y7hyfwi57coYv5udnmiJ1MMc52GfpfrnE/edit#gid=1310628341
Typically, you're going to need a combination of two methods:
Google Apps Script's getRange() method, and the chainable methods getValues() and setValue().
Using the JavaScript forEach() method.
The getRange() method is used in your Google script to target specific cells, (for example, the columns you need to target in sheet1). When you then use the method getValues() (ex: ss.getRange( ... ).getValues()), you'll create an array with the data from the range you've targeted.
Once you have that array, you can then iterate over it using something like the array.forEach() method. For example, you might use the array.push() method to only push values into a new array when the cell next to it has the string "Complete" in it.
Once you have that new array, you can then use the getRange( ... ).setValues() methods to "print" to sheet2.
I want to understand how the reduce functions are used in Crossfilter. Namely, the
reduceAdd(p,v){...}
reduceRemove(p,v){...}
and
reduceInitial(p,v){...}
in
group.reduce(reduceAdd, reduceRemove, reduceInitial);
From Crossfilter's API reference, I understand that the p and v arguments represent the group and dimension value respectively.
From what I understand, the return value of the reduce functions determine what the groupValue should be changed to after the element is added or removed from the group. Is this correct?
Also, what is the reduceInitial function for?
That is correct, if you substitute "bin value" for "groupValue" in what you wrote.
A group is made of an array of bins; each bin is a key-value pair. For every group, all rows of the data supplied to crossfilter will "fall into" one bin or another. The reduce functions determine what happens when a row falls into a bin, or is removed from it because the filters changed.
Crossfilter determines which bin any row falls into by using using the dimension value accessor and the group value function.
When are reduction functions called?
When a crossfilter initializes a new group, it adds all the currently matching rows of the data to all the groups. The group determines a key for each row by using the dimension value accessor and the group value function. Then it looks up the bin using that key, and applies the reduceAdd function to the previous bin value and the row data to produce the new bin value.
When any filter on any dimension of the crossfilter changes value, some rows will stop matching and some rows will start matching the new set of filters. The rows that stop matching are removed from the matching bin using the reduceRemove function, and the rows that start matching are added using the reduceAdd function.
When a row is added, some groups may not already have a bin which matches the key for that row. At that point a new bin must be initialized and at that point the group calls reduceInitial to get the user-specified blank value for the bins of that group.
Crossfilter's group.reduce compared to Array.reduce
The reduceAdd and reduceRemove functions are similar to the functions you would pass to Javascript's Array.reduce function. The first parameter p takes the previous value of the bin, and the second paramter v takes the current row data being considered.
In contrast to Array.reduce, in group.reduce
values can be removed as well as added
the initial value is produced by the reduceInitial function instead of being passed to reduce
it doesn't perform the aggregation immediately; instead you are supplying functions that will get called whenever filters change or data is added or removed
I have 3 large tables, resp1, resp2, and resp3. Each object in these tables are anonymous tables.
I can combine the tables very easily with var list = (resp1.concat(resp2)).concat(resp3);, but I want to sort them in a very specific way, then combine them.
Each anonymous table contains an index called Number, with a number as the value. I want the anonymous tables with the smallest Numbers to be first in the table, and the second smallest Number to be next, and so on and so forth.
How should I go about doing this?
How should I go about doing this?
Put all the objects in your tables in an array. Then sort that array by the Number index of its anonymous table. Then join the array into the new table.
If you would give us more details (code) about what "object" or "anonymous table" means, we can further help you with this.