I'm having trouble writing a UDF for Sheets that would allow me to concatenate multiple rows of data into a single text field with a delimiter based on unique ID. I've seen examples of this for excel, but haven't been able to find a sheet's equivalent. Basically, I want to concatenate all values in B:B where B(i) = A(i) delimited by a comma or pipe or some other delimiter.
Basically, I want to concatenate all values in B:B where B(i) = A(i) in a single cell delimited by a comma or pipe or some other delimiter.
See if this formula helps
=textjoin(", ", 1, unique(filter(B:B, B:B=C:C)))
or, depending on your locale
=textjoin(", "; 1; unique(filter(B:B; B:B=C:C)))
If that doesn't help, please share a copy of your spreadsheet with editing rights.
to concatenate all values in B:B where B(i) = A(i) in a single cell delimited by a comma
=ARRAYFORMULA(REPT(INDIRECT("A1:A"&COUNTA(A1:A))&",",
QUERY(B:B, "select count(B) where B matches '"&
TEXTJOIN("|", 1, A:A)&"' group by B label count(B)''", 0)))
and to remove commas from the end you can do:
=ARRAYFORMULA(REGEXREPLACE(REPT(INDIRECT("A1:A"&COUNTA(A1:A))&",",
QUERY(B:B, "select count(B) where B matches '"&
TEXTJOIN("|", 1, A:A)&"' group by B label count(B)''", 0)), ",$", ))
Related
Given the following conditions:
A cell containing a Comma Separated List (Column B in table)
A cell containing how many values to sum from the Comma Separated List (Column A in table)
Data
MAX INDEX
COMMA SEPARATED LIST TO SUM
EXPECTED MATH
EXPECTED VALUE
5
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
=1+2+3+4+5
15
3
5,10,15,20,25,30,35,40,45,50
=5+10+15
30
image
I have seen formulas using OFFSET, but in application, OFFSET doesn't seem to work with a CS List (only an actual range).
Is there a formula that will sum up only the first N values in a Comma Separated List without using helper columns? (So the entirety of the operation will consist of only three cells... MAX Index value, the Comma Separated List, and the output cell containing the formula).
try:
=INDEX(IFERROR(1/(1/BYROW(
IF(SEQUENCE(1, COLUMNS(SPLIT(B2:B, ",")))<=A2:A, SPLIT(B2:B, ","), ),
LAMBDA(xx, SUM(xx))))))
or alternative:
=INDEX(IFERROR(1/(1/
BYROW(IFERROR(SPLIT(REGEXEXTRACT(B2:B5&",",
BYROW(A2:A5, LAMBDA(xx, JOIN(, REPT("\d+,", xx))))), ",")),
LAMBDA(yy, SUM(yy))))))
or non-lambda alternative:
=FLATTEN(INDEX(IFERROR(1/(1/QUERY(TRANSPOSE(
IF(SEQUENCE(1, COLUMNS(SPLIT(B2:B, ",")))<=A2:A, SPLIT(B2:B, ","), )*1),
"select "&TEXTJOIN(",", 1, "sum(Col"&SEQUENCE(ROWS(A2:A))&")")))),2))
to quickly confuse noobs:
=INDEX(BYROW(BYROW(
IF(SEQUENCE(1, COLUMNS(SPLIT(B2:B, ",")))<=
INDIRECT("A2:A"&MAX((A2:A<>"")*ROW(A2:A))), SPLIT(B2:B, ","), )*1,
LAMBDA(aa, JOIN("+", aa))),
LAMBDA(xx, INDEX(QUERY(, "select "&xx), 2))))
=sum(array_constrain(split(B2, ","),1, A2))
Explanation:
sum over
a constrained array (of A2 columns and 1 row)
formed by splitting the contents of B2 on ,
Use this
=MAP(B2:B,A2:A, LAMBDA(rg,idx, IF(rg="",, ARRAYFORMULA(SUM(ARRAY_CONSTRAIN(SPLIT(rg,", ",0),1,idx))))))
Demo
Used formulas help
MAP - LAMBDA - IF - ARRAYFORMULA - SUM - SPLIT
=MAP(
A2:INDEX(A2:A,COUNTA(A2:A)),
B2:INDEX(B2:B,COUNTA(A2:A)),
LAMBDA(a,b,
QUERY(
QUERY(SPLIT(b,","),
"Select "&ARRAYFORMULA(JOIN("+","Col"&SEQUENCE(a))),0
)
,"offset 1",0)
)
)
SPLIT by comma, then use QUERY to do the addition. The addition string Col1+Col2+.. is created dynamically using JOIN
Is there a better way of writing this formula out? I mean it works but there has to be a better way of doing it where I can have multiple Search parameters.
I am using a search box to then that splits the cell with this formula =if(E1="","", if(REGEXMATCH(E1,";"),SPLIT(E1,";"),E1))
https://docs.google.com/spreadsheets/d/1-ymDylwRjd0zYnu3m0uxCz8hAavnL5Sxx39YrtlWylc/edit?usp=sharing Example Sheet
=if(E1="",QUERY(IMPORTRANGE("Spreadsheetidabcd", "RawData!A:Z"), "Select Col1,Col12,Col2,Col10,Col3 where (Col11='')",1),QUERY(IMPORTRANGE("Spreadsheetidabcd", "RawData!A:Z"), "Select Col1,Col12,Col2,Col10,Col3 where Col1 contains '"&$F$1&"' or Lower(Col12) contains Lower('"&$F$1&"') or Lower(Col12) contains Lower('"&$G$1&"')or Lower(Col10) contains Lower('"&$F$1&"')or Lower(Col10) contains Lower('"&$G$1&"')or Lower(Col2) contains Lower('"&$F$1&"')or Lower(Col2) contains Lower('"&$G$1&"')or Col3 contains '"&$F$1&"' or Col3 contains '"&$G$1&"'or Col4 contains '"&$F$1&"'or Lower(Col5) contains Lower('"&$F$1&"')"))
You can try this one, although this returns the row if any of the columns contain the values of either F1 or G1 (I assume that's what your search box is for, to find the rows containing the search term).
Formula:
=if(E1="",
QUERY(IMPORTRANGE("Spreadsheetidabcd", "RawData!A:Z"), "Select Col1,Col12,Col2,Col10,Col3 where (Col11='')",1),
arrayformula(split(query({transpose(split(textjoin(",", true, QUERY(IMPORTRANGE("Spreadsheetidabcd", "RawData!A:Z"), "Select Col1, Col12, Col2, Col10, Col3, ';' label ';' ';'")), ",;,",))}, "where lower(Col1) contains lower('"&$F$1&"') or lower(Col1) contains lower('"&$G$1&"') or Col1 contains 'Timestamp'"), ",")))
Basically, what the formula does step by step is:
It appends a column that marks the end of the row when we combine them into 1 column.
Combines all columns into 1 using , as delimiter (, marks per column and ; marks per row)
Then we split them by ,;, to separate each rows (each row contains the textjoined result)
After that, we filter the data using query where a row should contain the values of F1 and G1 (and including Timestamp to include the header).
Filtered data will then split back to their original form
Output:
EDIT:
To be dynamic, I modified it to check F1:1 range instead.
Formula:
=if(E1="",
QUERY(IMPORTRANGE("Spreadsheetidabcd", "RawData!A:Z"), "Select Col1,Col12,Col2,Col10,Col3 where (Col11='')",1),
arrayformula(split(query({transpose(split(textjoin(",", true, QUERY(IMPORTRANGE("Spreadsheetidabcd", "RawData!A:Z"), "Select Col1, Col12, Col2, Col10, Col3, ';' label ';' ';'")), ",;,",))}, "where lower(Col1) contains lower('"&join("') or lower(Col1) contains lower('", filter($F$1:$1, not(isblank($F$1:$1))))&"') or Col1 contains 'Timestamp'"), ",")))
Output:
I am trying to get a google sheet to search for a specific cell in a table. The headers change so it might be A6 one week and then A9 the other and so on.
Once it's found that row, I want it to search and pull all of that departments names and data for the column its matched with.
I am 23 sheets in and my heads hit a brick wall and I just can figure it out.
You can try:
=QUERY({A:B,INDEX(A:G,0,MATCH(D25,1:1,0))},"SELECT * WHERE Col2='" & LOWER(F25) & "'")
Note - you should remove unnecessary spaces. In sample data, they were in cells C1 and D25.
Try this:
=QUERY(
FILTER(
IFS(
TRIM(1:20) = "", 0,
ISNUMBER(1:20), 1:20,
True, LOWER(TRIM(1:20))
),
1:1 <> ""
),
"SELECT Col1, Col2, Col" & MATCH(TRIM(D25), ARRAYFORMULA(TRIM(1:1)),) & "
WHERE Col2 = '" & LOWER(F25) & "'",
1
)
You can use a combination of CHAR(MATCH...)) and Query formula to get this
=QUERY('Sheet Name'!A1:G20,"SELECT A, B, "&CHAR(MATCH("Log 4",'Sheet Name'!A1:G1)+64)&" WHERE B='w'")
Above formula only works till column Z, but thanks to Kishkin's comment below, you can use it beyond Z like this:
=QUERY('Sheet Name'!A1:G20,"SELECT A, B, `" & =REGEXEXTRACT(ADDRESS(1, MATCH("Log 4",'Sheet Name'!A1:G1), 4), "\D+") & "` WHERE B='w'")
You use SUBSTITUTE instead of REGEXTRACT too. You can refer to this Question.
the CHAR(MATCH...)) gets the column name of the desired Log
you can then use the column name to include that column in Query select statement
In the MATCH formula, you can also dynamically Match for a cell reference instead of specifying "Log 4" manually
Note: This is basically splitting the Query formula and concatenating it with another formula. So the &, ' and " symbols are really important
Sample Screenshot:
Please see: Extracting and counting unique word frequency from a range
In that question the asker was seeking unique single words.
I'm trying to accomplish the same but finding every unique pair of words.
If a cell doesn't have two words, then it doesn't have any entries.
If a cell has 3 words then it would have two combinations A + B and B + C
I've tried to parse with splits and substitute pipes for spaces by using the len(cell) - len(substitute(cell," ","")) which gives me the number of words, but that doesn't work either.
Try this (assuming your words are in Column A, put this in cell B1):
=index(
query(
query(
trim(iferror(flatten(split(
regexreplace(regexreplace(lower(A:A),"[^A-Za-z\ \']+",""),"([\w\']+\ [\w\']+)","$1,")
&","&
regexreplace(regexreplace(lower(if(len(A:A)=len(substitute(A:A," ",""))+1,,A:A)),"[^A-Za-z\ \']+",""),"\w*\ ([\w\']+\ [\w\']+)","$1,")
,",",1,1)),)),
"where Col1 like '% %' order by Col1",1),
"select Col1, count(Col1) group by Col1 label Col1 'Word pairs', count(Col1) 'Qty'",0)
)
It's quite involved, but I'll break it down if it works for you!
How can I get the IMPORTRANGE function to update references in Spreadsheet #2 if I've added/deleted rows to Spreadsheet #1?
Test docs:
Spreadsheet1: "S1"
Spreadsheet2: "S2"
All the answers I've found say
just duplicate/copy sheet from S1 to S2 (but I want S2 to dynamically update when I update S1. don't want to update both S1 and S2 each time)
Use this script, but I got an error saying the range was wrong
use an array (not sure how to do that...)
use address feature (but this did not work either)
use VLOOKUP array (but my spreadsheets do not have the matching column headers)
tried using absolute reference $L$48 but this didnt work either
Would appreciate some assistance figuring this out. I need L47, M47 (S1) to populate into A2, A3 (S2). My actual source spreadsheet (not linked) has tons of data (range A1:Z55) and I add/delete/move rows often.
If I add a row to S1, how can I ensure L47 will update in S2 to L48? Is the address feature the best solution or is an array better?
this can be achieved by adding an extra column far away, where it can be hidden and then populating this column by joining desired set of cells by unique separator until split will occur on the second spreadsheet. note that:
adding or deleting rows will not affect dynamicity of IMPORTRANGE
adding deleting columns will break all imported data
there is no need for an extra column if there is a unique separator per every IMPORTRANGE of data and the search is applied always to such unique separator
in this particular case, there was used column AG from which IMPORTRANGE was fed.
in Spreadsheet1 in Sheet1!AG (no matter of row number) there are formulas which JOIN content of L50 and M50 as well as the content of L51 and M51, etc... (no matter if it's done directly or indirectly as far as the output is TEXT):
=JOIN("¤"; L50; MIN(FILTER(L:L; ISNUMBER(SEARCH("*banana*"; P:P))
+ISNUMBER(SEARCH("*banana*"; Q:Q))
+ISNUMBER(SEARCH("*banana*"; R:R)))))
outputing: next banana¤30-Aug-2004
=JOIN("¤"; L51; MIN(FILTER(L:L; ISNUMBER(SEARCH("*orange*"; P:P))
+ISNUMBER(SEARCH("*orange*"; Q:Q))
+ISNUMBER(SEARCH("*orange*"; R:R)))))
outputing: next orange¤2-Oct-2003
=JOIN("♥"; L52; AVERAGE(FILTER(L:L; ISNUMBER(SEARCH("orange"; P:P))
+ISNUMBER(SEARCH("orange"; Q:Q))
+ISNUMBER(SEARCH("orange"; R:R)))))
outputing: X♥25-Sep-2013
=JOIN("♀"; L53; MIN(FILTER(L5:L48; ISNUMBER(SEARCH("*banana*"; Q5:Q48))
*ISNUMBER(SEARCH("open"; R5:R48)))))
outputing: next banana♀20-Aug-2000
=JOIN("♂"; L54; AVERAGEIFS(M5:M48; R5:R48; "open",
Q5:Q48; "*banana*"))
outputing: avg days open (banana)♂74.41
=JOIN("♪"; L55; Q50/Q51)
outputing: util♪0.370544987
=JOIN("♫"; L56; MINIFS(M5:M48; R5:R48; "open",
Q5:Q48; "*banana*"))
outputing: newest (mo)♫3.48
=JOIN("¤"; L57; M56*30.5)
outputing: newest(days)¤106.2580645
=JOIN("♤"; L58; M58)
outputing: avg LMT♤25051.35484
at this point, it doesn't matter if the format of joined cells is outputting elsehow (eg. 2nd part of the output should be formatted as $, %, mm/dd/yyyy) because in Spreadsheet2 after splitting you can format it back as you wish
in Spreadsheet2 you are free to paste following formula at any column and any row as well as you are free to:
add or delete any rows in Spreadsheet1
and add or delete any rows or columns in Spreadsheet2
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("banana";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"¤"; 1; 0)
this basically SEARCHes for text value "banana" in Spreadsheet1 under Sheet1 from range AG1:AG1000 and feed it to the FILTER which feeds criterion of QUERY which is ARRAY_CONSTRAINed to return one entry and that entry is SPLIT after unique separator "¤" (used earlier in JOIN) into two columns at the same row. and that's it.
if the content of cell L50 is static like banana and also unique per column you can SEARCH for "banana" otherwise you need to use unique separator per column and SEARCH for such separator instead of "banana"
for a successful linkup, you need to be sure that separator in SPLIT matches separator in JOIN ("¤"). you can use any symbol you wish as the separator (http://www.i2symbol.com/symbols)
example: for formula =JOIN("♤"; L58; M58) you can use:
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("lmt";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"♤"; 1; 0)
or
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("♤";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"♤"; 1; 0)
or
=SPLIT(
ARRAY_CONSTRAIN(
QUERY(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
"select Col1 where Col1 ='"&
FILTER(
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000");
ISNUMBER(
SEARCH("avg LMT";
IMPORTRANGE("13evadbMLzvQVSGbYssn_0deFdcmb5l3sqpeFgcNTjOY"; "'Sheet1'!AG1:AG1000"))
))
&"'");
1; 1);
"♤"; 1; 0)