I want a function that can extract numbers with their units of measurment from a text.
For example in A2 i have:
This box weights 5kg and the other box weights 10 kg.
So i want a function that will return:
5kg 10kg
NOTE: I want the function to work with any unit of measurment, not just "kg".
I am a begginer in google sheets so it would be really helpful if you could provide me with a working function.
You can use this sample custom function that extracts words that starts with a number followed by a character/s.
/**
* #customfunction
*/
function EXTRACTMEASUREMENT(input) {
// match all words which starts with a number
var result = input.match(/\d+[a-zA-Z]+\S*/g)
// combine array into a string separated with spaces
result = result.join(' ');
// Remove special characters(except whitespace) in the string
result = result.replace(/[^\/a-zA-Z0-9\s]/g, '')
return result;
}
Output:
Limitations:
Measurements with spaces between the value and the unit cannot be detected. (See result in cell A5 when space exist in 10 kg.)
Regardless whether the character/s after the number is a valid unit or not, it will be extracted. (See result in cell A5 where 20yy is not a valid measurement unit)
If you want to exempt particular characters not to be removed, you can add them in the braces [^\/a-zA-Z0-9\s] (example / will not be removed).
Note:
This can be improved if you can list valid measurement units that should be supported.
Try
=arrayformula(substitute(transpose(query(flatten(split(
REGEXREPLACE(A1,"([0-9.,/]+[ ]{0,1}[a-z1-3/.\-""]+)","♣♦$1♣")
,"♣")),"select * where Col1 like '♦%' ")),"♦",""))
One more option:
=ArrayFormula(IF(LEN(A:A),
SPLIT(
REGEXREPLACE(
REGEXREPLACE(A:A,"("&
REGEXREPLACE(
REGEXREPLACE(
REGEXREPLACE(A:A,"(\d+[.,/]*\d*(?:\w+|\s\w+)[\""./\-\w+]*)",""),
"\s+\.","|\\."),
"\s+","|")
&")",""),
"(\d)\s","$1")
," ",,1)
,))
try:
=INDEX(SPLIT(FLATTEN(QUERY(TRANSPOSE(IFERROR(SUBSTITUTE(
REGEXEXTRACT(SPLIT(REGEXREPLACE(A1:A, "(\d+.\d+|\d+)", "×$1"), "×"),
TEXTJOIN("|", 1, {"\d+.\d+ ","\d+.\d+","\d+ ","\d+"}&
SORT({"nm";"mm";"cm";"dm";"km";"m";"t";"kg";"dg";"g";"l";"ml";"dl"},
LEN({"nm";"mm";"cm";"dm";"km";"m";"t";"kg";"dg";"g";"l";"ml";"dl"}), 0))),
" ", ))),,9^9)), " "))
update:
all in one cell:
=INDEX(TRIM(FLATTEN(QUERY(TRANSPOSE(IFERROR(SUBSTITUTE(
REGEXEXTRACT(SPLIT(REGEXREPLACE(A1:A, "(\d+.\d+|\d+)", "×$1"), "×"),
TEXTJOIN("|", 1, {"\d+.\d+ ","\d+.\d+","\d+ ","\d+"}&
SORT({"nm";"mm";"cm";"dm";"km";"m";"t";"kg";"dg";"g";"l";"ml";"dl"},
LEN({"nm";"mm";"cm";"dm";"km";"m";"t";"kg";"dg";"g";"l";"ml";"dl"}), 0))),
" ", ))),,9^9))))
Related
I am facing a problem related to the dynamic array.
I have data in the below format.
And I want to convert to this format.
Here is the sheet link.
I am using this formula to filter Fruits category.
={FILTER(A5:D11,B5:B11="Fruits");SUM( FILTER(D5:D11,B5:B11="Fruits"))}
But it gives this error
In ARRAY_LITERAL, an Array Literal was missing values for one or more rows
NOTE: Data should be pulled dynamically from the formula, as the data may change.
To build the result table without hard coding category names in the formula, use the recently introduced lambda functions, like this:
={
lambda(
data, categories, headers, totalsHeader, blankRow, selectPrice,
reduce(
headers, query(unique(categories), "where Col1 is not null", 0),
lambda(
resultTable, filterKey,
{
resultTable;
lambda(
filterData,
{
filterData;
{ totalsHeader, query(filterData, selectPrice, 0) };
blankRow
}
)(filter(data, categories = filterKey))
}
)
)
)(
B5:D,
B5:B,
B4:D4,
{ "", "Total:" },
{ "", "", "" },
"select sum(Col3) label sum(Col3) '' "
);
{ "", "Grand Total:", sum(D5:D) }
}
See { array expressions }, filter(), query(), reduce() and lambda().
The formula will repeat each category name on several rows. If they get in the way, you can hide them from view by using a conditional formatting custom formula rule.
I did some tests to add all the information in just one formula. It will change the format you want, but it will still divide all the information.
Here is the formula:
={"Fruits:","";QUERY(B5:D,"select C, D where B ='Fruits'");
{"Total:",SUMIF(B5:D,"Fruits",D5:D)};"","";
"Vegetables:","";QUERY(B5:D,"select C, D where B ='Vegetables'");
{"Total:",SUMIF(B5:D,"Vegetables",D5:D);"","";
"condiments:","";QUERY(B5:D,"select C, D where B ='condiments'");
{"Total:",SUMIF(B5:D,"condiments",D5:D)};"","";
"Grand Total:",SUM(D5:D)}}
Note:
I added : and the end of each category in the formula so they will look like Fruits: and the table will look like this:
The formula opens with { to open an array in Google Sheets, and you use , to separate columns to write a row of data, and ; to separate the rows to help you write a column of data. After that, you use } to close the array. For example:
{"1","2";"3","4"}
It will print:
So basically, I organize the data with arrays of the same amounts of columns. The first one with part
= { => To open the array.
"Fruits:",""; => This create a cell with "Fruits:" + an empty cell.
QUERY(B5:D,"select C, D where B ='Fruits'"); => which is
already on an array of 2 columns.
{"Total:",SUMIF(B5:D,"Fruits",D5:D)}; => Creates the "Total" cell + the sum
of values that has Fruits in column B.
"",""; => Which will create an empty row to separate the information
for the next set of arrays.
You do the same pattern for the other categories.
} => to end the initial array.
You can add a "Conditional formatting" that will change the text with : to bold automatically.
Reference:
QUERY function
SUMIF
ARRAYFORMULA
I suggest you read on: https://stackoverflow.com/a/58042211/5632629
the first part of your formula outputs a grid of 4×3 cells
the second part of your formula outputs a single cell
if you want to combine it properly use:
={FILTER(A5:D11, B5:B11="Fruits");
{"","","Totals",SUM(FILTER(D5:D11, B5:B11="Fruits"))}}
or:
={FILTER(B5:D11, B5:B11="Fruits");
{"","Totals",SUM(FILTER(D5:D11, B5:B11="Fruits"))}}
Ok so, I will arleady say that im not that good with excel and even worse on google sheets.
I would like to know if there is a formula that checks for the name of someone and then sums the points said person got to his total. Thank you very much!
https://docs.google.com/spreadsheets/d/1D4M8Dlao8yFHJulNDff-i2jZJeVGGY_gejbAfzWdhFo/edit?usp=sharing is the link if you want to check it out.
I assume you want Punti tot. (R:R) column to have the sum of all TA Brawl (column C:C), Notte (L:L) and 50 Shades (H:H; in this case any of the two players should be checked, right?) for every player.
You can use put this in R2 (do not forget to remove everything below R2):
={
"Punti tot.";
ARRAYFORMULA(
IF(
P3:P = ""; ;
SUMIF(A3:A; P3:P; C3:C)
+ SUMIF(E3:E; P3:P; H3:H)
+ SUMIF(F3:F; P3:P; H3:H)
+ SUMIF(J3:J; P3:P; L3:L)
)
)
}
Update: here is another solution using QUERY which is also sorts players by total points:
=QUERY(
{
FILTER({A3:A\ C3:C}; A3:A <> "");
FILTER({E3:E\ H3:H}; E3:E <> "");
FILTER({F3:F\ H3:H}; F3:F <> "");
FILTER({J3:J\ L3:L}; J3:J <> "")
};
"
select Col1, SUM(Col2)
group by Col1
order by SUM(Col2) desc,
Col1
label Col1 'Player',
SUM(Col2) 'Punti tot.'
";
-1
)
And you might want to use this in O2 for ranking:
={
"Posizioni Finali";
ARRAYFORMULA(
RANK(
FILTER(Q3:Q; Q3:Q <> "");
FILTER(Q3:Q; Q3:Q <> "")
)
)
}
This way same amount of points gives players the same place. Otherwise hysen would get 9th place and Imurshh would be 10th just because h goes before i.
I've the below formula using ImportRange and Query along with Join and Split working correctly:
=join(" / ", QUERY(IMPORTRANGE("Google-Sheet-ID","RawData!A:AC"),"select Col25 where Col1 = " & JOIN(" OR Col1 = ", split(V2:V,"+")), 0))
Also, I've the below ArrayFormula with Split function working smoothly:
=ARRAYFORMULA(if(len(V2:V)=0,,split(V2:V,"+")))
But When I tried combining them together using the below formula:
=ARRAYFORMULA(if(len(V2:V)=0,,join(" / ", QUERY(IMPORTRANGE("Google-Sheet-ID","RawData!A:AC"),"select Col25 where Col1 = " & JOIN(" OR Col1 = ", split(V2:V,"+")), 0))))
It failed, and gave me the below error:
Error
Function SPLIT parameter 1 value should be non-empty.
Here is my sheet for your testing.
UPDATE
I changed it to:
=ARRAYFORMULA(if(len(C2:C)=0,,JOIN(" OR Col1 = ", ARRAYFORMULA(if(len(C2:C)=0,,split(C2:C,"+"))))))
So my full formula is:
=ARRAYFORMULA(
if(
len(C2:C)=0,,
join(" / ",
QUERY(
IMPORTRANGE("14iNSavtvjRU0XipPWIMKyHNwXTA85P_CafFTsIPHI6c","RawData!A:AC"),"select Col25 where Col1 = " &
ARRAYFORMULA(
if(len(C2:C)=0,,
JOIN(" OR Col1 = ",
ARRAYFORMULA(
if(
len(C2:C)=0,,split(C2:C,"+")
)
)
)
)
),
0
))))
And now getting the error:
Error
JOIN range must be a single row or a single column.
I believe this formula on the tab called MK.Testing will pull the info you're hoping for.
=QUERY(IMPORTRANGE("14iNSavtvjRU0XipPWIMKyHNwXTA85P_CafFTsIPHI6c","RawData!A:AC"),"select Col25 where Col1="&TEXTJOIN(" or Col1=",TRUE,A2:A))
I think you might have been overcomplicating things? This formula just forms a text string out of the shipment IDs to use in a query. one thing that may be tripping you up is that query() is very particular about the type of data in a column. Your shipment IDs can be numbers, or they can be number letter combos, but not both. That is, if you have some shipment IDs that contain letters and others that don't, it will be more difficult to get a query that works. (though not impossible). For the sake of helping you though, it's important that your sample IDs reflect the real ones in this way as accurately as possible.
How about doing this with Apps Script? You can get the values from the Sheet2, Shipment Ids, and the Ids from MK.Testing and compare them. If they coincide, the you copy the ETA into the Column C of MK. Testing:
function myFunction() {
var sprsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet2 = sprsheet.getSheetByName("Sheet2");
var mkTesting = sprsheet.getSheetByName("MK.Testing");
var shipmentId = sheet2.getRange("A2:A").getValues();
var idList = mkTesting.getRange("A2:A").getValues();
for (var i = 0; i < shipmentId.length; i++){
for (var j = 0; j < idList.length; j++){
if (idList[j][0] == ""){break;} //Stops if there is an empty cell in Mk.Testing's column A
if (idList[j][0] === shipmentId[i][0]){
var eta = sheet2.getRange("E"+(i+2)).getValue();
mkTesting.getRange("C"+(j+2)).setValue(eta);
}
}
}
}
References:
SpreadsheetApp Class
Range Class
When I use the Query function like this using a number, it returns the correct results:
=QUERY(Sheet2!A1:B12,"select B where A matches '1200'", 0)
But, when I try to match a string, the result is always an empty output:
=QUERY(Sheet2!A1:B12,"select B where A matches 'qwerty'", 0)
This seems ultra-simple, but I can't seem to find an answer anywhere.. What exactly is the correct syntax to match the string? I've included an example of my problem here: sheets_query
I suggest you format ColumnA of Sheet2 as Plain text.
This indeed is simple, instead of matches, you need to use an "=" symbol. So your formula will be =QUERY(Sheet2!A1:B12,"select B where A='qwerty'", 0)
for number it would be:
=QUERY(Sheet2!A1:B12, "select B where A = 1200", 0)
for text, it would be:
=QUERY(Sheet2!A1:B12, "select B where A = 'qwerty'", 0)
for number with external reference:
=QUERY(Sheet2!A1:B12, "select B where A ="&1200, 0)
for text with external reference:
=QUERY(Sheet2!A1:B12, "select B where A = '"&"qwerty"&"'", 0)
for mixed data you can either format it as Plain text or:
=ARRAYFORMULA(QUERY(TO_TEXT(Sheet2!A1:B12), "select Col2 where Col1 = 'qwerty'", 0))
I have a complex Google Sheet query that works great except when a Google Sheet doesn't have as many columns as I use in my formula.
Here's what the formula looks like now:
=sum(filter(query(INDIRECT("'" & A2 & "'!$A$7:$23"),"select Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH where B='"&C2&"'",0),query(INDIRECT("'" & A2 & "'!$A$7:$23"),"select Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH where B='PROJECT'",0) >=date(2017,1,1),query(INDIRECT("'" & A2 & "'!$A$7:$23"),"select Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH where B='PROJECT'",0) <=date(2017,12,31)))
It works great. But the problem is I run it against many worksheets and some don't have e.g. column AG,AH and end at AF at which point I get an error.
So what I need is a way to generate the string Q,R,S....[Name of Last Column in Sheet] and then I can use that instead of my hard-coded Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH but I cannot figure out how to do that.
Any help is greatly appreciated. Thanks!
Per comments above, final formula was:
LEFT("Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC ",2*Columns(INDIRECT(A2&"!1:1"))-33+IF(Columns(INDIRECT(A2&"!1:1"))>26,Columns(INDIRECT(A2&"!1:1"))-26,0))
where column A contains the list of worksheets (tabs) in the Google Sheet. Put this in B2, and then copied it down. I am not marking this as the correct answer since others gave a correct formula-based answer but this did the trick for me.
This can be done with built-in functions:
On a helper sheet, let say you name it, helper, fill up range with letters A to Z, let say A1:A26
Let say that on B1 you write the following formula:
=ArrayFormula({A1:A26;TRANSPOSE(SPLIT(JOIN(",",SUBSTITUTE(QUERY(TRANSPOSE(A1:A26)&A1:A26,,27)," ",",")),","))}) . This will create a list of column letter headers.
On each new worksheet use columns(1:1) to get the total number of columns.
To get your string of column headers, then you could use something like :
JOIN(",",OFFSET(helper!B1,16,0,columns(1:1)-16))
QUERY(helper!B:B,"select B limit "&columns(1:1)-7&" offset 7")
NOTE:
If you decide to have only one helper sheet and use it on several spreadsheets, then use
QUERY(IMPORTRANGE(your_url,"helper!B:B"),"select Col1 limit "&columns(1:1)-7&" offset 7")
This can be done with script. Without seeing you spreadsheet, it is hard to know exactly what you need, but this should be close. I get the variables from Sheet1 and return the formula to Sheet1. Adjust the sheet name to fit your needs. This will look at your data sheets based on the variable sheet name determine the last column. Determine the column letters and build the string the query needs. It then sets the new query formula. I added a menu to run it from.
function onOpen() {
SpreadsheetApp.getActiveSpreadsheet().addMenu(
'Create Data', [
{ name: 'Run', functionName: 'formula' },
]);
}
function formula(){
var ss= SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getSheetByName("Sheet1") //sheet where variables are
var sheet=s.getRange("A2").getValue()//variable sheet name
var sel=makeString(sheet) //get the select string of column letters
//Create formula and return to Sheet1 A3
var f= s.getRange("A3").setFormula('=sum(filter(query(INDIRECT("\'" & A2 & "\'!$A$7:$23"),"select '+sel+' where B=\'"&C2&"\'",0),query(INDIRECT("\'" & A2 & "\'!$A$7:$23"),"select '+sel+' where B=\'PROJECT\'",0) >=date(2017,1,1),query(INDIRECT("\'" & A2 & "\'!$A$7:$23"),"select '+sel+' where B=\'PROJECT\'",0) <=date(2017,12,31)))')
}
function makeString(sht){
var ss= SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getSheetByName(sht)
var lc=s.getLastColumn()
var rng=s.getRange(1, 17, 1, lc).getValues()
var str=''
var ltr=[]
for(var i=17;i<rng[0].length+1;i++){
ltr[i]= columnToLetter(i)
str=ltr.join(',')
}
var str1=str.substr(17)
return str1
}
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
Let me know if you have any questions.