Related
I have 3 columns A, B & C as shown in the image. Column A contains the search key. The second column B contains names and their respective content in the third column C.
I am filtering rows that contain the text in A1 in B:C and concatenating them. The challenge is that each text in the third column is roughly 40k characters. The filter formula works well so the issue is the character limit. This formula =ArrayFormula(query(C1:C,,100000)) which I have in F1 concatenates more than 50000 characters but I am not how to apply it for my case.
Tried to wrap my formula in E1 inside the query function but it wasn't successful. Like so:
=ArrayFormula(query(CLEAN(CONCATENATE(FILTER(C1:C, B1:B=A1))),,100000)).
I also tried to SPLIT the concatenated result into sets of 50000 characters and put the extras in the next columns but wouldn't manage either. The formula I tried in this case is:
=SPLIT(REGEXREPLACE(CLEAN(CONCATENATE(FILTER(C1:C, B1:B=A1))),".{50000}", "$0,"),",")
The link to the spreadsheet
https://docs.google.com/spreadsheets/d/1rhVSQJBGaPQu6y2WbqkO2_UqzyfCc3_76t4AK3PdF7M/edit?usp=sharing
Since cell is limited to 50,000 characters, using CONCATENATE is not possible. Alternative solution is to use Google Apps Script's custom function. The good thing about Apps Script is it can handle millions of string characters.
To create custom function:
Create or open a spreadsheet in Google Sheets.
Select the menu item Tools > Script editor.
Delete any code in the script editor and copy and paste the code below.
At the top, click Save.
To use custom function:
Click the cell where you want to use the function.
Type an equals sign (=) followed by the function name and any input value โ for example, =myFunction(A1) โ and press Enter.
The cell will momentarily display Loading..., then return the result.
Code:
function myFunction(text) {
var arr = text.flat();
var newStr = arr.join(' ');
var slicedStr = stringChop(newStr, 50000);
return [slicedStr];
}
function stringChop(str, size){
if (str == null) return [];
str = String(str);
size = ~~size;
return size > 0 ? str.match(new RegExp('.{1,' + size + '}', 'g')) : [str];
}
Example:
Based on your sample spreadsheet, there are 4 rows that matches the criteria of the filter and each cell contains 38,976 characters, which is 155,904 characters in total. Dividing it by 50,000 is 3.12. The ceiling of 3.12 is 4 which means we have 4 columns of data.
Usage:
Paste this in cell E1:
=myFunction(FILTER(C1:C, B1:B=A1))
Output:
Reference:
Custom Function
Your assistance will be greatly appreciated as I have been struggling with this for a while and couldn't find a solution.
I have a Google Sheets file with comma-separated data in two columns as per the screenshot attached.
Screenshot of the two columns
text from the screenshot:
soon,son,so,on,no N/A
kind,kid,din,ink,kin,in dink
sing,sign,sin,gin,in,is gis,ins,sig,gins
farm,arm,ram,far,mar,am arf
may,yam,am,my N/A
tulip,lip,lit,pit,put,tip piu,pul,til,tui,tup,litu,ptui,puli,uplit
gift,it,if,fit,fig gif,git
hear,are,ear,hare,era,her hae,rah,rhea
dish,his,is,hi,hid dis,ids,sidh
trip,pit,rip,tip,it N/A
wife,few,if,we fie
thaw,what,hat,at haw,taw,twa,wat,wha
red,deer,reed ere,dee,ree,dere,dree,rede
as,save,vase,sea ave,sae,sev,vas,aves
from,for,form,of,or fro,mor,rom
won,now,on,own,no N/A
sport,port,spot,post,stop,sort,top,opt,pot,pro tor,sotrot,ops,tors,tops,trop,pots,opts,rots,pros,prost,strop,ports
I would love to have in another column a formula to show if in these two columns there are any duplicate values.
Thank you in advance for your help... it's been weeks without success haha
If you have Excel for Windows O365 with the UNIQUE and FILTERXML functions,
and if you mean to consider both columns together as if they were a single piece of data,
then try:
=UNIQUE(FILTERXML("<t><s>" & SUBSTITUTE(TEXTJOIN("</s><s>",TRUE,$A$1:$A$17,$B$1:$B$17),",","</s><s>") & "</s></t>","//s[.=following-sibling::*]"))
If that is not what you want, please clarify your question.
First place your data in columns A and B of an Excel worksheet. Then run this short VBA macro:
Sub report()
Dim rng As Range, r As Range, c As Collection, K As Long
Set rng = Range("A1:B17")
Set c = New Collection
K = 1
For Each r In rng
arr = Split(r.Value, ",")
For Each a In arr
On Error Resume Next
c.Add a, CStr(a)
If Err.Number <> 0 Then
Err.Number = 0
Cells(K, "C").Value = a
K = K + 1
End If
On Error GoTo 0
Next a
Next r
Range("C:C").RemoveDuplicates Columns:=1, Header:=xlNo
Set c = Nothing
End Sub
The duplicates appear in column C
What I have understood from your question: you want to find out if there are any words delimited by commas matching between the cells of two different columns.
For this solution I have used Apps Script. The following commented piece of code will find matching words between the two columns. Moreover, as the function used is an onEdit() trigger, it will automatically detect any changes done in either of these columns and automatically find out new matches or matches that are no longer there and update the value of cell C1:
function onEdit() {
// get current sheet
var sheet = SpreadsheetApp.getActive().getActiveSheet();
// get values from our columns. This returns a 2D array that is flatten into a
// 1 D array to then convert it into a string where its elements are separated
// by a comma and white spaces are removed (so that a matches space + a for example)
var colA = sheet.getRange('A1:A2').getValues().flat().join().replace(/\s/g, '');
var colB = sheet.getRange('B1:B2').getValues().flat().join().replace(/\s/g, '');
// Create two arrays where each element is a word delimited by a comma in their original
// string
var ArrayA = colA.split(',');
var ArrayB = colB.split(',');
// find matches in these two arrays and return these matches
var matchingValues = ArrayA.filter(value => ArrayB.includes(value));
// set the value of C1 to the words that the filter has matched between our two columns
// join is used to display all the matching elements of the match array
sheet.getRange('C1').setValue(matchingValues.join());
}
Demo:
If you do not know how to open the script editor, you can access it on your Google Sheets menu bar under Tools-> Script editor.
In our Staff timetable, employees can have an "A"shift (which starts at 9am) a "B" shift (which starts at 10:30am) etc.
I need to know how many shifts in total the employees make so what I use now is a multiple times COUNTIF to count the presents of a few arguments in a range of cells
=countif(D8:BM8;A43)+countif(D8:BM8;A44)+countif(D8:BM8;A45)+countif(D8:BM8;A46)++countif(D8:BM8;A47)
Where field A43 contains "A" field A44 cointains "B" etc.
This works perfect, however, I want to have a smarter way to do this, so I tried to use "COUNTIFS" but the result is always 0 and I can't find why
=COUNTIFS(D8:BM8;A43;D8:BM8;A44;D8:BM8;A45;D8:BM8;A46;D8:BM8;A47)
if looks like all arguments are checked with a logical and, but I need a logical and in this case or a solution with dcounta
You are getting a 0 because there is no cell that will meet ALL conditions.
Instead, maybe try something like
=sum(ArrayFormula(--regexmatch(D8:BM8; textjoin("|"; 1; A43:A47))))
Regexmatch returns a boolean, for all the cells in D8:BM8 (true if a match is found, false otherwise). These booleans are converted into 1 and 0 (see the -- in front of the regex). Then all those values are summed.
Copy and paste the code in a module.(VBA)
Select the cell you want to have the results
Use COUNTIFMATCH Function as the usally functions.
The first Argument is the range you want to count. The second Argument is the range with your criteria. e.g
=COUNTIFMATCH($A$1:$A$20,$C$1:$C$3) or =COUNTIFMATCH($A$1:$A$20,($B$1,$D$1))
whatever you need based on your needs.
Option Explicit
Function COUNTIFMATCH(Range As Range, Criteria As Range) As Integer
'counts the number of cells in one range by the values โโof another range.
Dim datax As Range
Dim rslt As Range
Dim i As Integer
Set rslt = Range
For Each datax In Criteria
i = i + WorksheetFunction.CountIf(rslt, datax)
COUNTIFMATCH = i
Next datax
End Function
I have created a Google spreadsheet for our small business which lists all the invoices. I have uploaded a simplified format in
https://docs.google.com/spreadsheets/d/1zYrRxDm0ahsjWE8aNquz-shHuNY_Eifl3lXLhIBUeTE/edit?usp=sharing.
1.There can be 1-5 products per invoice.
2.The column G is the total of all the products in that invoice. I want to create a formula for this column.
Presently, my formula is very long and inefficient.
The column (G) calculates number of products with this formula:
=IF(B3<>"",IF(OFFSET(B3,1,0)="",IF(OFFSET(B3,2,0)="",if(OFFSET(B3,3,0)="",if(OFFSET(B3,4,0)="",if(OFFSET(B3,5,0)="",5,5),4),3),2),1),0)
Another column (H) sums up the product values with this: =IF(G3>0,SUM(OFFSET(D3,0,0,G3,1)),"")
Help me rework the G column formula which calculates the number of products. If there's any way I can consolidate G and H that would be great too.
Note: the (I) column is just an alternative to (H) column.
P.S. Please don't flag this as an opinion based question. This is purely a problem solving question.
Since you are ok with the option of a helper column off to the side or hidden, we can do the following.
In column K starting in row 3 I placed this formula:
=IF(A3<>"",A3,K2)
You can actually use whatever column suits you just remember to update the column references in subsequent formulas. It generates a column of invoice numbers with no spaces which allows some other formulas to work much easier for us.
In column L startin in row 3 I placed this formula:
IF(COUNTIF($K$3:K3,K3)=1,COUNTIF(K:K,K3),0)
This gives the same results as column G. The first part of the IF statement is checking to see if the invoice number is the first occurrence of the invoice number. If it is count how many times the invoice number occurs, otherwise display 0.
Now if you want to skip counting how many items there are in an invoice you can use the sumproduct formula as follows:
=IF(A3<>"",SUMPRODUCT(($K$3:$K$12=A3)*$D$3:$D$12),"")
now to account for a variable sized list of invoices we will count the number of invoices and adjust our formula with an offset to return the appropriate ranges as follows:
=IF(A3<>"",SUMPRODUCT((OFFSET($K$3,0,0,COUNT(K:K),1)=A3)*OFFSET($D$3,0,0,COUNT(K:K),1)),"")
Since we are using COUNT(K:K) it is imperative that no numbers be entered in this column other than those generated by our formula.
This treats items inside the brackets as an array, without the formula itself being an array. The whole thing is placed inside an IF statement so that empty cells are displayed instead of zeros in the rows that do not correspond to an invoice number in column A.
now if you want to understand how sumproduct works in this case, its basically generating a an array filled with 1 or 0 representing true or false and then multiplying it by an array of the same size that is filled with all your amounts. So anything multiplied by 0 is 0 and anything multiplied by 1 is amount. The final step of sumproduct is to add up all the values. So you will only get the sum of what ever is true or 1.
If you are able to utilise VBA, you could use a User Defined Function. Insert this code into a new module and call it like you would a normal excel function:
Public Function InvoiceDetail(Invoice As Range, ReturnType As Integer)
Dim varCount As Long
Dim varSheet As Worksheet
Dim varInvoiceID As String
Dim varPartyName As String
Dim varInvoiceTotal As Double
Dim varInvoiceCount As Integer
Set varSheet = ThisWorkbook.Sheets(Invoice.Parent.Name)
If varSheet.Range("A" & Invoice.Row).Value <> "" Then
varInvoiceID = varSheet.Range("A" & Invoice.Row).Value
varPartyName = varSheet.Range("B" & Invoice.Row).Value
For varCount = Invoice.Row To 1000000
If varSheet.Range("D" & varCount).Value = "" Then
Exit For
End If
If varInvoiceID = varSheet.Range("A" & varCount).Value And varPartyName = varSheet.Range("B" & varCount).Value Then
varInvoiceTotal = varInvoiceTotal + varSheet.Range("D" & varCount).Value
varInvoiceCount = varInvoiceCount + 1
ElseIf varSheet.Range("A" & varCount).Value = "" And varSheet.Range("B" & varCount).Value = "" Then
varInvoiceTotal = varInvoiceTotal + varSheet.Range("D" & varCount).Value
varInvoiceCount = varInvoiceCount + 1
Else
Exit For
End If
Next
End If
Set varSheet = Nothing
Select Case ReturnType
Case 1 '// Count Only
InvoiceDetail = varInvoiceCount
Case 2 '// Total Only
InvoiceDetail = varInvoiceTotal
Case 3 '// Total [Count]
InvoiceDetail = varInvoiceTotal & " [" & varInvoiceCount & "]"
Case Else
InvoiceDetail = "Error"
End Select
End Function
This code obviously assumes that your Invoice ID is in Column A, your Party Name is in Column B, and your Amount is in Column D. I've implemented a few options for you too:
=InvoiceDetail(A3,1) returns the number of items on the invoice (as integer)
=InvoiceDetail(A3,2) returns the sum of items on the invoice (as double)
=InvoiceDetail(A3,3) returns both sum and [count] (as string)
I was able to solve this with 2 arrayformulas.
Paste this formula in any corresponding cell, let it be O3:
=TRANSPOSE(SPLIT(JOIN("",ArrayFormula(REPT(FILTER(A3:A12,A3:A12>0)&"-",
len(TRANSPOSE(SPLIT(REGEXREPLACE(JOIN(",",A3:A12)&",","\d+","-"),"-")))))),"-"))
And this formula in cell P3:
=ArrayFormula(if(A3:A>0,SUMIF(O3:O,O3:O,D3:D),""))
My sample file.
To make the first formula work with different ranges:
replace A3:A12 to offset(A3,,,counta(C3:C))
I don't know if this is possible to do without scripting, but I would like to have a block of cells that can be modified by the user where they will enter a string in each cell. For each of these string values I would like to retrieve a number from a table that matches that string and SUM together all of the resulting matched numbers.
With the matching table:
The users enters:
I would like to have the resulting SUM = 1 + 4 + 3 = 8
If your matching table is named namedRange1 and your chosen colours are in A9:A11:
=vlookup(A9,NamedRange1,2,0)+vlookup(A10,NamedRange1,2,0)+vlookup(A11,NamedRange1,2,0)
Alternatively, name the values in the matching table by their corresponding colour and:
=Red+Yellow+Blue
I figured it out.
=SUM(ARRAYFORMULA(IF(UserInput <> "", VLOOKUP(UserInput, ColorMatcher, 2, false), 0)))
SUM - Adds all of the values together
ARRAYFORMULA - Allows the use of a "single target" function to work over an array of values, returns each individual lookup value to SUM
IF(UserInput <> "") - Each of these values is compared to empty string first so that I can have blank cells in the range, otherwise VLOOKUP breaks due to a non-match, returns 0 otherwise
VLOOKUP - Each value in UserInput is compared to a value in the first column of ColorMatcher, and takes the matching value in the 2nd column. In this case the index is not sorted