Create query with multiple answers for each cell - google-sheets

So I have got 3 sheets with 2 predefined ranges as you can see them in the Example:
# RangeA # RangeB # Wanted Result
======== ======== ===============
A | B A A
-------- -------- ---------------
1 | a a a
2 | a b 1
3 | a 2
4 | b 3
5 | b b
6 | b 4
7 | c 5
8 | c 6
9 | c
...
Now I would like to have a Formular to get the wanted result I have been searching quite long time today already, but I wasn't successful. I hope there is anybody who may help me.
I hope the example is clear enough to understand what i want to do.
Thanks in advance for your time.

I solved it in the end with google apps script.
The function I used is pretty simple just two for loops:
/*
* Merge merges two arrays to get one list of wanted values
* #param needle {array} is a list of wanted values
* #param haystack {array} is a list of values and their group
* #return returns a list of merged values in the format group, value,
* value, group ...
**/
function Merge(needle, haystack) {
var result = [];
// Set default values to parameters if parameter is not set.
needle = needle || [[]];
haystack = haystack || [[]];
// Filter the array and remove empty items. # RangeB
needle = needle.filter(function(item){
return item[0];
});
// Filter the second array and remove empty or incomplete items # RangeA
haystack = haystack.filter(function(item){
return item[0] && item[1];
});
// Merge both arrays to get the # Wanted Result
needle.forEach(function(item){
result.push([item[0]]);
haystack.forEach(function(needle){
if(item[0] == needle[1]) {
result.push([needle[0]]);
}
});
});
// Check if the result has a length
if(result.length > 0) {
return result;
}
// else return null to overcome the #error message
return null;
}

Related

Processing each Row with pivot data from other cell

==================================
UPDATE 11 December 2019
My Question is more about Macro Script
The GOAL (in illustration)
to change below raw sheet:
to more readable format:
Basically what i'm doing is split the campaign name with the separator and parse it.
I don't have the problem if the function on only process single cell,for example:
on "Report" Sheet the CELL B2 , is taking data from "Data" B2 ONLY
i got problem when the return data require conditional operator that involve specific condition. So while processing cell B2, it require content from E2, D2, etc
=====================================
i'm taking data from Google Ads/Analytics API to Google Sheet on specific worksheet (i call it 'Raw Data').
Now i'm using pattern for the campaign, so i can easily split/break with separator in order for me to get specific data.
For Example:
With this, by using underscore as separator, i can split campaign name, into various data:
Campaign Objective: Sales
Campaign Title: TBMB
Network: SEM
Branch: All
Targeting: Keywords
..etc
Then i create new sheet called Called CReport which consist the same data from Raw Data sheet, but in much better visualization for marketing people.
Now, after searching on Google, i found the solution for self reference cell.
The script goes like this:
function getSegment(data,index){
temp=data.split("_");
return temp[index-1];
}
function dataParse(input,dataSegment){
return Array.isArray(input) ? input.map(function(e){
return e.map(function(f){
if(f!=""){
return getSegment(f,dataSegment);
}
}
)}
) : "false usage";
}
​So if i want to have a column with Network Name, i can place this formula on row 2 (because row 1 is for table header) something like this:
=ArrayFormula(dataParse('RAW DATA'!B2:B;2))
Now my question:
This works for self-reference cell, means if the data taken from B2 in RAW DATA sheet, it will be the only data referenced to cell in Campaign Report sheet.
If the pointer is in B2 on CReport Sheet require data not only from B2 in RAW DATA but also D2 Cell.
What script i need to add in my function ?
i'm expecting the chunk of code will something like this
function dataParse(input,dataSegment){
return Array.isArray(input) ? input.map(function(e){
return e.map(function(f){
if(f!=""){
segmentData=getSegment(f,dataSegment);
if(segmentData=="google"){
returnData=get reference from column D //<---
}else{
returnData=get reference from column E //<---
}
return returnData
}
}
)}
) : "false usage";
}
Hope its clear enough.
Thanks in Advance !
I modified your function in this way:
// range (String): It will be used to get the info in a range
function dataParse(input,dataSegment, range){
var val = "";
return Array.isArray(input) ? input.map(function(e, index){
return e.map(function(f){
if(f!=""){
// If col D has value google then take info from col B
if(f === "google") val = getDesiredRangeValue("B", range, index);
// else take info from col E
else val = getDesiredRangeValue("E", range, index);
// Take segment as needed
return getSegment(val,dataSegment);
}
}
)}
) : "false usage";
}
In order to make it work, I inserted an extra argument to the function. Now you will need to pass as an string the range in A1 notation in your ArrayFormula, this is because the input argument only gives you the values in the cells, and with that extra argument it will be possible to obtain extra info. To make it work fine, always use the same range as the next example shows:
=ArrayFormula(dataParse('RAW DATA'!D2:D5, 2,"D2:D5"))
or
=ArrayFormula(dataParse('RAW DATA'!D2:D, 2,"D2:D"))
Notice I also added a new function called getDesiredRangeValue, which will take the values from the column you need, depending if one of the cells from Col D has the value google. This is how the function looks:
/*
// A1 (String): The col from where you will want the info
// range (String): It will be used to get the info in a range
// index (Integer): It gives the index number from the main array gotten in the input arg
*/
function getDesiredRangeValue(A1, range, index){
var rowNumbers = range.match(/\d+/g);
// It checks if the range will has and end or it will prolong without specifying and end row
if(rowNumbers.length > 1){
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1 + rowNumbers[1]).getValues();
} else {
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1).getValues();
}
// It returns the whole value from each cell in the specified col
return rangeCol[index][0];
}
Code
Now your whole code will look like this:
// Global var
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("RAW DATA");
function getSegment(data,index){
temp=data.split("_");
return temp[index-1];
}
/*
// A1 (String): The col from where you will want the info
// range (String): It will be used to get the info in a range
// index (Integer): It gives the index number from the main array gotten in the input arg
*/
function getDesiredRangeValue(A1, range, index){
var rowNumbers = range.match(/\d+/g);
// It checks if the range will has and end or it will prolong without specifying and end row
if(rowNumbers.length > 1){
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1 + rowNumbers[1]).getValues();
} else {
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1).getValues();
}
// It returns the whole value from each cell in the specified col
return rangeCol[index][0];
}
// range (String): It will be used to get the info in a range
function dataParse(input,dataSegment, range){
var val = "";
return Array.isArray(input) ? input.map(function(e, index){
return e.map(function(f){
if(f!=""){
// If col D has value google then take info from col B
if(f === "google") val = getDesiredRangeValue("B", range, index);
// else take info from col E
else val = getDesiredRangeValue("E", range, index);
// Take segment as needed
return getSegment(val,dataSegment);
}
}
)}
) : "false usage";
}
Docs
These are the docs I used to help you:
Class Sheet
Custom Functions

Export the data to excel & formatting that as requirements

I am able to give color, width for columns also & now I want to format a number that which we are sending to excel(i.e, I am sending 0 it should appear as 0.00 in excel): How is it possible?
gone through Define cell format on AlaSQL/JS-XLSX Excel Export,
I know it would be too late to answer this but would help for people who are looking for formatting number with fixed decimal as 2.
You can define your custom function by adding to alasql.fn object as below
alasql.fn.precise_round = function (num, dec) {
if ((typeof num !== 'number') || (typeof dec !== 'number'))
return false;
console.log(num);
var num_sign = num >= 0 ? 1 : -1;
return (Math.round((num * Math.pow(10, dec)) + (num_sign * 0.0001)) / Math.pow(10, dec)).toFixed(dec);
}
alasql.fn.numberWithCommas = function (num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
You can now use this function inside your alasql select query
var data = alasql("SELECT INTO XLSX precise_round(column_name) from ?", [objData]);
alasql("SELECT INTO XLSX('Test.xlsx', { headers: true}) FROM ?", [data]);
Assuming one of your column in objData holds numbers including 0 as well which is output as 0.00 as per your requirement

Use Fitnesse symbol to inject baseUrl into RestFixture

I'd like to set the baseUrl for a table based on the outcome of another test (on the same page).
I tried following these pages of Fitnesse's docs (and other resources) :
smartrics blog post, fitnesse symbols page,
but I can't seem to get it working.
So far I've tried with the following syntaxes :
| Fit Rest Fixture | %emailLink% |
| GET | / | 200 |Content-Type: text/plain|Email Verified|
| Fit Rest Fixture | emailLink= |
| GET | / | 200 |Content-Type: text/plain|Email Verified|
| Fit Rest Fixture | $emailLink |
| GET | / | 200 |Content-Type: text/plain|Email Verified|
but none of those work.
I know that the emailLink symbol is not null because I'm testing it in another table, but I can't seem to inject it into the RestFixture.
I always get an IllegalArgumentException indicating that the symbol name has not been resolved against its value, e.g.
java.lang.IllegalArgumentException: Malformed base URL: $emailLink
Any help would be appreciated.
Are you using slim?
http://www.fitnesse.org/FitNesse.UserGuide.WritingAcceptanceTests.SliM.SymbolsInTables
I have used symbols in this way a few time with Slim, but not the REST Fixture specifically.
By taking a look at the code of FitRestFixture and fiddling with it, I've come up with something that works for me.
It seems that the feature I was looking for is not supported out of the box, but can be easily achieved (although this way is not the cleanest) with a simple mod such as the following :
/**
* #return Process args ({#link fit.Fixture}) for Fit runner to extract the
* baseUrl of each Rest request, first parameter of each RestFixture
* table.
*/
protected String getBaseUrlFromArgs() {
String arg = null;
if (args.length > 0) {
arg = args[0];
/* mod starts here */
if (isSymbol(arg)) {
String symbolName = stripSymbolNotation(arg);
arg = resolveSymbol(symbolName);
}
/* mod ends here */
}
return arg;
}
private boolean isSymbol(String arg) {
// notice that I've used the '<<' notation convention to extract the
// the value from a symbol, while in RestFixture the conventional
// notation is %symbolName%
return null != arg && arg.startsWith("<<");
}
private String stripSymbolNotation(String arg) {
return arg.substring(2);
}
private String resolveSymbol(String arg) {
String symbolValue = (String) Fixture.getSymbol(arg);
LOG.warn(String.format("resolved symbol %s to value %s", arg, symbolValue));
return symbolValue;
}

tcl loop through multiple lists

I have two lists I'd like manipulate.. ( I am a tcl newbie..). I'd like to associate these two lists and create a third list with some data added.
The data I have:
set aes {ae0 ae3 ae6 ae1v1 ae1v8}
set c {c1 c2 c3 k1 k2}
foreach A $aes {
foreach C $c {
puts ${A}_$C
}
}
The data I get as you'd expect is:
ae0_c1
ae0_c2
ae0_c3
ae0_k1
ae0_k2
..
..
What I want to do is
append some data in front of this.
AE-To-c = All ae0_c1 ae0_c2 ae0_c3 ae0_k1 ae0_k2 .. End.
% set aes {ae0 ae3 ae6 ae1v1 ae1v8}
ae0 ae3 ae6 ae1v1 ae1v8
% set c {c1 c2 c3 k1 k2}
c1 c2 c3 k1 k2
% foreach A $aes {
foreach C $c {
# saving into 'result' variable
lappend result ${A}_${C}
}
}
% set data "some more here"
some more here
% set result
ae0_c1 ae0_c2 ae0_c3 ae0_k1 ae0_k2 ae3_c1 ae3_c2 ae3_c3 ae3_k1 ae3_k2 ae6_c1 ae6_c2 ae6_c3 ae6_k1 ae6_k2 ae1v1_c1 ae1v1_c2 ae1v1_c3 ae1v1_k1 ae1v1_k2 ae1v8_c1 ae1v8_c2 ae1v8_c3 ae1v8_k1 ae1v8_k2
% set result [linsert $result 0 $data]
some more here ae0_c1 ae0_c2 ae0_c3 ae0_k1 ae0_k2 ae3_c1 ae3_c2 ae3_c3 ae3_k1 ae3_k2 ae6_c1 ae6_c2 ae6_c3 ae6_k1 ae6_k2 ae1v1_c1 ae1v1_c2 ae1v1_c3 ae1v1_k1 ae1v1_k2 ae1v8_c1 ae1v8_c2 ae1v8_c3 ae1v8_k1 ae1v8_k2
Your question isn't 100% clear. Is it something like this you want?
set res [list AE-To-c = All]
foreach A $aes {
foreach C $c {
lappend res ${A}_$C
}
}
lappend res End
If you want to do what I think you want to do, you need to capture the permutations of the two lists in a list instead of printing them out, and then wrap that list in a prefix and suffix.
The method above pre-loads the result list with the AE-To-c = All prefix, then picks up the permutations using lappend, and finally adds the End suffix as a last element in the list.
Another way:
set res [list]
foreach A $aes {
foreach C $c {
lappend res ${A}_$C
}
}
concat [list AE-To-c = All] $res End
In this variant the list of permutations is created first, and then the prefix list, the permutation list, and the suffix list (yes, End is a list) are concatenated into one flat list.
Documentation: concat, foreach, lappend, list, set

LPeg Increment for Each Match

I'm making a serialization library for Lua, and I'm using LPeg to parse the string. I've got K/V pairs working (with the key explicitly named), but now I'm going to add auto-indexing.
It'll work like so:
#"value"
#"value2"
Will evaluate to
{
[1] = "value"
[2] = "value2"
}
I've already got the value matching working (strings, tables, numbers, and Booleans all work perfectly), so I don't need help with that; what I'm looking for is the indexing. For each match of #[value pattern], it should capture the number of #[value pattern]'s found - in other words, I can match a sequence of values ("#"value1" #"value2") but I don't know how to assign them indexes according to the number of matches. If that's not clear enough, just comment and I'll attempt to explain it better.
Here's something of what my current pattern looks like (using compressed notation):
local process = {} -- Process a captured value
process.number = tonumber
process.string = function(s) return s:sub(2, -2) end -- Strip of opening and closing tags
process.boolean = function(s) if s == "true" then return true else return false end
number = [decimal number, scientific notation] / process.number
string = [double or single quoted string, supports escaped quotation characters] / process.string
boolean = P("true") + "false" / process.boolean
table = [balanced brackets] / [parse the table]
type = number + string + boolean + table
at_notation = (P("#") * whitespace * type) / [creates a table that includes the key and value]
As you can see in the last line of code, I've got a function that does this:
k,v matched in the pattern
-- turns into --
{k, v}
-- which is then added into an "entry table" (I loop through it and add it into the return table)
Based on what you've described so far, you should be able to accomplish this using a simple capture and table capture.
Here's a simplified example I knocked up to illustrate:
lpeg = require 'lpeg'
l = lpeg.locale(lpeg)
whitesp = l.space ^ 0
bool_val = (l.P "true" + "false") / function (s) return s == "true" end
num_val = l.digit ^ 1 / tonumber
string_val = '"' * l.C(l.alnum ^ 1) * '"'
val = bool_val + num_val + string_val
at_notation = l.Ct( (l.P "#" * whitesp * val * whitesp) ^ 0 )
local testdata = [[
#"value1"
#42
# "value2"
#true
]]
local res = l.match(at_notation, testdata)
The match returns a table containing the contents:
{
[1] = "value1",
[2] = 42,
[3] = "value2",
[4] = true
}

Resources