I am very new at programming in Google Script, and need some guidance here.
I have a loop that is supposed to check the value of a range, then move down to the next row and set the new range's value as "next", which is then compared to "team".
If the condition is met then "team" is given the same value from the range that "next" was given, and "next" is given the value of the range below the current one.
The purpose is to find how many entries there are that meet conditions in a column, then my other parts of the code manipulate that data.
When the "team" value isn't the same as "next" (the value of the range below the current one), then the loop is supposed to stop, but doesn't. Code:
while (team = next) {
team = orange.getValue();
row ++;
range = col + row.toString();
orange = ordered.getRange(range);
next = orange.getValue();
entries ++;
};
I'm not familiar with Google Script either, but most languages require double equal sign for equality tests. From a cursory view I think Google Script uses the same syntax. Try doing this instead:
while (team == next)
Notice the double == to check for equality instead of single = to assign a variable.
Related
Hi I am really new to VBA Macros and would appreciate any help on this matter.
I need to write a macro to change the value of a column to 'checked' based on different search criterias. My requirement is that one of the columns values must be automatically marked as checked based on search criterias which will search for existence of serial nos contained in a string in one column inside string values of other columns.
Please help me in creating a macro to accomplish this task.
I am sorry to say that I cannot share the code or attach a screenshot to further explain the scenario because of confidentiality reasons.
Detailed explaination
Column to be modified : Comments (initially blank)
Columns used for search criteria : Can be any column in the existing table.
Search criteria:
A substring from the reference column (basically a number with 6 digits will be selected as the search key)
Note : There is no specific format followed by this column (and we can do nothing about that) eg: In one column the entry will be 35567890-DEF-GHJ while in another it will be like Ref:35567890-- and in another column field it will be like CEK 35567890.
The substring must be checked not only for the entries in the same coulmn but with the contents of the entire table. Basically it is like the find function in the excel.
If a match is found I need to add up the values in the debit and credit entries and see whether the result is 0. If the result is zero, I need to enter 'checked' in the comments field that allows string values.
This is not one complete macro but still does the work.
Macros where used only for extracting the product code. For the remaining processes I used existing functions as in, performed a comparing function on the net amount and then used a simple if to mark the column as check.
Macro used for extracting product code.
Public Function getProductCode(source As String) As String
Dim rExp As Object, allMatches As Object, match As Object
Dim result As String: result = ""
Set rExp = CreateObject("vbscript.regexp")
With rExp
.Global = True
.MultiLine = False
.Pattern = "(?:^|\D)(\d{7,8})(?!\d)"
End With
Set allMatches = rExp.Execute(source)
For Each match In allMatches
result = result + match.SubMatches.Item(0)
Next
getProductCode = result
End Function
A new column 'Reference' was then created on which the macro was used passing the specific column and the extracted product code is stored here.
After this a function was used to find duplicate entries and sum up the total number of products sold. The formula I used is as follows:
=ROUND(SUMIFS([Total],[Reference],[#Reference]),2)=0
Finally I used a simple if to mark the column as checked if the previously generated result (in column status) is true.
=IF([#Column1]=TRUE, "Checked", "")
This is how I got it done.
There might be better way to do it. But if anyone else is facing the same problem this will help.
I have a spreadsheet formula in which I use exactly the same FILTER function twice inside an INDEX function. Is there any way to avoid repeating myself, and/or a simpler version of the expression?
The expression looks like this (separated into multiple lines for a tiny bit of extra clarity):
=INDEX(
FILTER($A$1:$P$1, ($A$1:$P$1 = "ANIM")+($A$1:$P$1 = "COMP")+($A$1:$P$1 = "SENT TO EDIT"),
NOT (ISBLANK(A2:P2))),
COLUMNS(
FILTER($A$1:$P$1, ($A$1:$P$1 = "ANIM")+($A$1:$P$1 = "COMP")+($A$1:$P$1 = "SENT TO EDIT"),
NOT (ISBLANK(A2:P2)))))`
What the expression does is return the heading of the rightmost non-blank column in the row, so long as the column is one of ANIM, COMP, or SENT TO EDIT.
See the formula in action in column Q of this Google Sheets.
Since the FILTER function is exactly the same both times, if I could use a variable, it could be re-written something like this:
range1 = FILTER(.....); INDEX(range1, COLUMNS(range1));
Is there any way to do something like that - or an equivalent - in order to avoid repeating my FILTER expression?
You have the right idea, the way to avoid repeating long subformulas is to move them out of the formula and call the result someway. On certain cases you could use a reference to the range that displays the result but for formulas that returns a dynamic array, a custom function could be a better alternative.
Example:
Let say that you move out
FILTER($A$1:$P$1, ($A$1:$P$1 = "ANIM")+($A$1:$P$1 = "COMP")+($A$1:$P$1 = "SENT TO EDIT"),
NOT (ISBLANK(A2:P2)))
to dry!A1 (don't forget to prepend it with =)
The following is just a "mcve". It assumes that te dry sheet only contains the above formula:
function headers(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets().getSheeyByName('dry');
return sheet.getDataRange().getValues();
}
Replace your original formula for the following:
=INDEX(headers(dry!A1:P1),COLUMNS(headers(dry!A1:P1))
Note: The use of dry!A1:P1 as arguments of headers() is to force it's recalculation every time that the result for the subformula changes.
See also: Is there a way to evaluate a formula that is stored in a cell?
You could try something like this:
=REGEXEXTRACT(JOIN("|",FILTER($A$1:$P$1, Regexmatch($A$1:$P$1,"ANIM|SENT TO EDIT|COMP"), NOT (ISBLANK(A2:P2)))),"([^|]+)$")
Using regular expressions is a way to handle arrays like a text.
What you are looking for is called a temporary variable, and last I checked, Google sheets doesn't support temporary variables (https://webapps.stackexchange.com/questions/71095/how-to-save-temporary-variables-in-google-sheets-formula). It would be nice if they did!
Is there any way to find the last row you have written in a google spreadsheet in Java?
I tried to do that by having a variable which I keep in another file and I update that every time I do another writing. Is there any other way?
Finding last written row in Google Spreadsheet API
I'll give you a concept first using REST calls but you'll have to apply that in Java.
Exceed the range of cells where you're writing.
So to find last written row between cells A1:A10, use the range A1:A11 or A1:B (using B means all the rows in cell A will be traversed).
Fetch a range of cells, using GET. Parse the response result. And get the length of the parsed values. The length will always indicate the last row since Sheetsv4 ignores blank/empty cells.
So example I have a range of cells between A1:A10. I wrote "hello" in A10. Following the concepts above I do this:
..
xhr.open('GET', 'https://sheets.googleapis.com/v4/spreadsheets/'+myspreadsheetId+'/values/Sheet1!A1:A10);
..
xhr.onload = function (oEvent) {
arrayBuffer = xhr.response;
myArray = JSON.parse(arrayBuffer);
console.log("last row is " + myArray.values.length)
.. //output is 'last row is 10'
This part of my XHR request returns 10. Now I know that the last written row in my sheet is A10.
To do this in Java use the solution based on this SO thread.
mService.spreadsheets().values().get("ID_SHEET", "Sheet1!A1:B").execute();
Also check this thread about writing on data cells using Java. I think it offers additional insight as well.
I seem to have a problem and currently have not found a solution to it, which is why I address this question to you:
Each day I have a list of invoices and orders coming from different suppliers, and the orders are based on part numbers and types.
This list is imported as text and then goes through a macro I made, to arrange everything in cells.
I also need to go through some steps to format this list based on the type of order (ex: windshield, carpets, wheels, etc ). what I usually do is to filter everything and select the order type that I am interested, and then copy on the same row cells with text and formulas from another worksheet, named "template", which is a list of conditions .
Since it varies from day to day, it may not necessarily contain all part types, which is I couldn't use a macro, and I have to continue by hand, and sometimes the list exceeds 200-300 lines.
To give you an example, if E2 has "windshield" I copy in M2 to Q2 a selection of cells from "Template" (M2 to Q2), if "carpets" I copy M3 to Q3, and so on. the list of conditions is around 15 - 20 rows, and sometimes 2 conditions may apply (if order exceeds $5000 I mark it red, if overdue I bold everything, etc) but mainly I copy based on text in coll E.
If this could be copied into a macro, I would really appreciate it, as I need to take some steps every time, like auto-fit, copy header, format the amounts as number (from text), change text color based on order type, etc, and this too takes time.
I hope this information is enough to make an idea about this, and if not, I could post an example of the list I have to work with.
Many thanks in advance for your support
Use Application.Worksheetfunction.Match to find in which row in Template the to-be-copied cells can be found, then copy range M-Q for this row and paste in your file
You are asking too much in one question to get help here. We are best at single issue questions. The text and code below is intended you give you some ideas. If your code does not work, post the relevant part here and explain the difference between what it does and what you want it to do.
The problems you mention do not sound difficult. I would expect basic VBA to be enough to get you started. Are you looking for bits of relevant code without learning VBA. If you are, this is a big mistake. Search the web for "Excel VBA tutorial" or visit a large library and review their Excel VBA Primers. There are many tutorials and books to choose from so select one that is right for you. The time spent learning the basics will quickly repay itself.
Dim RowCrnt As Long
Dim RowLast As Long
With Worksheets("xxxx")
RowLast = .Cells(Rows.Count,"E").End(xlUp).Row
For RowCrnt = 2 to RowLast
' Code to process each row goes here
Next
End With
The above is probably the structure of your code. The For loop will examine each row in turn which will allow you to take relevant actions.
I have used "E" as a column letter because your question suggests column "E" is the most important. However, code that references columns in this way can be very confusing. Worse, if the column positions change, you will have to work carefully through your code changing the column letters. Better to have some statements at the top like this:
Const ColDate As String = "A"
Const ColAmtInv As string = "B"
Const ColAmtPaid As string = "C"
Const ColProdType As String = "E"
With these constants every reference to a column uses a name not a letter. The code is easier to read and, if a column moves, one change to the constant statement will fix the problem.
The Select Case statement is useful:
Select Case .Cells(RowCrnt, ColProdType).Value
Case "carpets"
' code for carpets
Case "windshield"
' code for carpets
Case Else
' Unknown product type
.Cells(RowCrnt, ColProdType).Font.Color = RGB(255, 0, 0)
End Select
You can have a string of If statements so multiple actions can occur on a single row:
If DateAdd("m", 1, .Cells(RowCrnt,ColDate).Value) < Now() And _
.Cells(RowCrnt,ColAmtInv).Value) > .Cells(RowCrnt,ColAmtPaid).Value Then
' Invoice overdue
.Rows(RowCrnt).Font.Bold = True
End If
If .Cells(RowCrnt,ColAmtInv).Value) > 5000 Then
' Large invoice
.Rows(RowCrnt).Font.Color = RGB(255, 0, 0)
End If
You do not have to write the entire macro in one go. If most action is because of the product type then try that first.
Hope this helps get you started.
This Macro I have built works but I am hoping for a faster version or a Formula that will do the same in less time.
What I Have:
For Each cell In Range("Table_Query_1[[#Data],[Reason2]]")
For Each PossibleValue In Range("F2", Range("F2").End(xlDown))
If Len(cell) = 0 Then
If (InStr(UCase(cell.Offset(0, 1)), UCase(PossibleValue)) <> 0) Then
cell.Value = PossibleValue.Value
End If
Else
Exit For
End If
Next
If Len(cell) = 0 Then
cell.Value = cell.Offset(0, -1)
End If
Next
The only other way I could get anything to work way with the following Array Formula
=IF(ISNA(MATCH($F$3:$F$10,[#Extra Info],0)),[#Reason],$F$3:$F$10)
but this doesn't work for Partial matches as in the case of Row 4 and 9. I also have my doubts that this array formula would be that much faster then a vba macro along with the fact it would also require more upkeep with the test values range (F2:f3) in this case as I would have to constantly update that formula OR I wouild have to make the original range like F2:F100 witch would cause it to take that much longer.
So, what i'd like is if ANY value in my range of values (F2:F3 in this case), Is found inside of the Extra Info Column on the current Row , Then Reason2 of that row (Offset(0, -1)) equals the Value that was matched. But if nothing is found then just use the Reason in that row(Offset(0,1)).
And the second Issue is that I need the Macro to Run After the QueryTable refreshes but if I set it as a Cell Change Event on a cell the is in the query that will change, the macro runs and finishes before the Final querytable is imported and sorted.
Solved!
This is post the comment that I posted above which had the initial formula.
=IF(COUNT(FIND($F$2:$F$3,C1)),"What Will Go Here",A1)
The below tells you what has to go in place of "What Will Go Here"
Put this formula in cell B2. Note that this is an Array Formula. You will have to press CTRL + SHIFT + ENTER after you enter the formula.
=IF(COUNT(FIND($F$2:$F$4,C2)),INDEX($F$2:$F$4,MATCH(SUM(IF(ISNUMBER(--FIND($F$2:$F$4,C2,1)),--FIND($F$2:$F$4,C2,1))),FIND($F$2:$F$4,C2,1),0),0),A2)
Screenshot
Explanation:
FIND($F$2:$F$4,C2,1) when used with an array returns an array. To check the values you can highlight it and press F9 and it will tell you the position at which the match is found. See this screenshot
So it tells us that it found the match at the 3rd position in 4532. It yet doesn't tell us with what did it find a match.
Now the next step is to retrieve the position of that number from the array. So in the above example it will be position 2 and to find that position we will use MATCH() and to use MATCH we will need that 3
So to retrieve 3 from the array we use this formula
=SUM(IF(ISNUMBER(--FIND($F$2:$F$4,C2,1)),--FIND($F$2:$F$4,C2,1)))
Now we have that 3 so we will use it in Match to find the position in the Possible Value
=MATCH(SUM(IF(ISNUMBER(--FIND($F$2:$F$4,C2,1)),--FIND($F$2:$F$4,C2,1))),FIND($F$2:$F$4,C2,1),0)
This will give us 2
Now we know the position of the number in the Possible Value. To find that number we will use INDEX
=INDEX($F$2:$F$4,MATCH(SUM(IF(ISNUMBER(--FIND($F$2:$F$4,C2,1)),--FIND($F$2:$F$4,C2,1))),FIND($F$2:$F$4,C2,1),0),0)
SAMPLE Workbook
http://wikisend.com/download/280280/Sample.xlsx
This is a solution I came across that does not have to be Array Entered and seems to run faster then Siddharth Rout's solution. I am using the formula
=IFERROR(LOOKUP(1E+100,SEARCH($F$2:$F$4,C2),$F$2:$F$4),A2)
Where I am looking for any word in C2 that is in the range F2:F4. If none found it will throw an ERROR and in that situation I know nothing was found and simply return the original reason.
Not shown in the picture I also turn F2:F4 into a named range called Reasons and change the formula too:
=IFERROR(LOOKUP(1E+100,SEARCH(Reasons,C2),Reasons),A2)