CONTEXT
I have a source table with multiple columns (Source B table in example spreadsheet).
On column E we have max one name and on column F we can have from 0 to multiple names separated by a comma.
AIM
When there are values in F, add one row per each name (E,F - the last one can have more than one separated by a comma) and duplicate the common values.
It should keep the rows where there are no values in F.
The final result will have one less column than Source table.
WHAT I'VE TRIED
=ARRAYFORMULA(QUERY(IFERROR(SPLIT(FLATTEN(IF(ISBLANK(E3:F6);;
A3:A6&"♦"&B3:B6&"♦"&C3:C6&"♦"&D3:E6&"♦"&E3:E6&"♦"&G3:G6&"♦"&H3:H6&"♦"&I3:I6&"♦"&J3:J6&"♦"&K3:K6&"♦"&L3:L6)); "♦"));
"where Col3 <> 0"; 0))
Problem
This formula was applied to Source A table (in the example spreadsheet and that's = Source B table but without comma separated values in F), which didn't have more than one value in F at the time, and:
duplicates the common values as expected but it's not showing the values from F. Just duplicates the ones from E.
ads blank row if the source row doesn't have values in F
because in column K only one row has a value, it messes up the final data
doesn't do anything different with or without comma separated values in F
Example spreadsheet
--- EDIT ---
I've found this other post with a script that I also tested on the example sheet. I've reduced the number of columns to ease the test and because I think it's aiming for the last column being the one that is supposed to be splitted, if applicable.
It does split but it repeats the "Main" client on the left column and the splited one on the right, where I would like the outcome to have all clients on the same column.
To give you an idea, you can try the following script, I used the "Source for Script" Sheet and created another sheet called "Result". As you can see in the script, I used arrays to collect the data, then added it to the "Result" Sheet.
function splitNewRow() {
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Source for Script");
var data = source.getRange(2, 1, source.getLastRow(), source.getLastColumn()).getValues();
var outcomeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Result");
newArray = [];
newRow = [];
for (i=0; i < data.length; i++){
var claim = source.getRange(i+2,1).getValue();
var mainClient = source.getRange(i+2,2).getValue();
var others = source.getRange(i+2, 3).getValue();
hasComma = others.indexOf(",") != -1;
if (others != "" && hasComma == true){
newArray = [];
newArray = others.split(",");
newArray.push(mainClient);
for (j=0; j < newArray.length; j++){
newRow = [claim, newArray[j]];
outcomeSheet.appendRow(newRow);
}
}
else if(others != "" && hasComma == false){
newRow = [claim, mainClient];
outcomeSheet.appendRow(newRow);
newRow = [claim, others];
outcomeSheet.appendRow(newRow);
}
else{
newRow = [claim, mainClient];
outcomeSheet.appendRow(newRow);
}
}
}
After running the script, you get the following:
If you have any questions, let me know.
Related
Say we have the following spreadsheet in google sheets:
a a
b b
c
d e
e d
How would I build a formula that counts the number of rows in column B that do not match the corresponding row in column A, and are not blank? In other words I want to get the number of rows that changed to a new letter in column B. So in this example the formula should return 2.
Thank you for your help.
UPDATE:
Now suppose I have this spreadsheet:
a a
b b b
c a
d e e
e d e
How would I build on the last formula for the third column, where the value returned is:
(the number of rows in column 3 that don't match the corresponding row in column 2) + (if column 2 is blank, the number of rows in column 3 that do not match the corresponding row in column 1)
and I also don't want to count blanks in the third column.
The value returned in this case should be 2 (rows 3 and 5).
To me it sounds like you could use:
=SUMPRODUCT((B:B<>"")*(B:B<>A:A))
=IFNA(ROWS(FILTER(A:B,
(A:A<>B:B)*
(B:B<>"")
)),0)
FILTER by matching conditions * for AND + for OR.
ROWS counts rows
IFNA returns 0 if nothing was found.
or with QUERY
=INDEX(QUERY(A:B,"select count(B) where B<>A"),2)
Try this:
=ARRAYFORMULA(COUNTA($B$1:$B)-SUM(COUNTIFS($A$1:$A, $B$1:$B,$B$1:$B,"<>")))
I see 2 ways to complete this.
First you could add a function to each row to return 1 or 0 if the value changed and was not blank and then sum results. This unfortunately adds a messy column in your spreadsheet.
=if(A1<>IF(ISBLANK(B1),A1,B1),1,0)
Second you could create a function where you would pass the range as a string.
The call from the worksheet would look like this:
=myFunction("A1:B5")
Then create a script by opening Tools -> Script editor and use something like this
function myFunction(r) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(r);
var numRows = range.getNumRows();
var areDifferent = 0;
for (let i=1; i<= numRows; i++) {
let currentValue = range.getCell(i,1).getValue();
let cmpValue = range.getCell(i,2).getValue();
if ((currentValue != cmpValue) && (cmpValue != "")) {
areDifferent++;
}
}
return areDifferent;
}
I am trying to do a SUM of last 12 rows in the column (I'll be adding more rows into this column so I wanted to automate the calculation).
First of all, I am able to get the value of last cell with some value in this column by =SUMPRODUCT(MAX((B1:B200<>"")*ROW(B1:B200))) - result is stored in C1. However, I am not sure how to use this value inside the SUM formula, I was thinking something like =SUM(B(get value of C1)-12:B(get value of C1).
I tried multiple things but none of them have worked - I also don't mind using a different approach if it gets the job done.
You can create your own custom function to do that using Google Apps Script (GAS).
Try the following:
function onEdit(e){
var row = e.range.getRow();
var col = e.range.getColumn();
if ( col==2 && e.source.getActiveSheet().getName() == "Sheet1" ){
e.source.getActiveSheet().getRange("C1").setValue(sumLast12());
}
}
function sumLast12() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
var sheet_size = sheet.getLastRow();
var elmt = sheet.getRange("B1:B"+sheet_size).getValues().flat([1]);
var elmt12 = elmt.slice(-12);
var sum = 0;
for( var i = 0; i < elmt12.length; i++ ){
sum += parseInt( elmt12[i], 10 );
}
return sum;
}
Explanation:
In order to activate this functionality go the menu bar on top of the
spreadsheet file and click on Tools => Script editor and copy the
aforementioned code into a blank script document (see attached
screenshot for more information) and save the document (cntrl+s).
After the script has been saved, everytime you edit a cell in column
B (either by adding a new value on the bottom or modify an existing value, the script will automatically update the value in
cell C1 with the sum of the last 12 values in column B.
Note that if you don't want to change my code, name the sheet you are working with as Sheet1.
Does this work?
=SUM(FILTER(B:B,ROW(B:B)>=MAX(ROW(B:B))-12)
for example, I have the next table. And I want to query all values which have the first cell "Computer". I have tried QUERY formula "QUERY(A1,B3;"select B where A = 'Computer'")" but it returns only the first B value - Keyboard.
Is it possible to return all values? Thanks.
I understand that you want to read a table composed of different pieces like the one that you posted, and if a match is found on the left column the script will need to return the contents of the right column. If my assumption is incorrect, please forgive me. An example initial table can look like this one:
In the example we will return the rows that contain PRAESENT in the first column. In that case, you can use the following Apps Script:
function so61913445() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var data = sheet.getDataRange().getValues();
var matchData = [];
var indexColumn = 0; // Column A
var targetIndex = "PRAESENT";
for (var r = 0; r < data.length; r++) {
for (var c = 0; c < data[0].length; c++) {
if (c == indexColumn && data[r][c] == targetIndex) {
matchData.push(sheet.getRange(r + 1, c + 2, 3, 1).getValues());
}
}
}
for (var r = 0; r < matchData.length; r++) {
for (var c = 0; c < matchData[0].length; c++) {
sheet.appendRow(matchData[r][c]);
}
}
}
In the code, the first step is to declare some variables to read the sheet (using the methods SpreadsheetApp.getActive() and Spreadsheet.getActiveSheet()), the data (with Sheet.getRange() and Range.getValues() methods) and some settings like the target word to match. After that, the code iterates over the data and, if the target word is found, the code will add the contents of the right column into the final array. Finally, the code will repeat the iteration to write the data just under the table using Sheet.appendRow(). The final result will look like this:
Please, ask me any question if you still need some help.
I have a google form and I would like to sort it's responses in a separate sheet on google sheets. The results of the form look sort of like this.
Id Job
1 Shelving, Sorting
2 Sorting
1 Cleaning, Shelving
3 Customer Service
2 Shelving, Sorting
which I would like to format into
Id Jobs
1 Cleaning, Shelving, Sorting
2 Shelving, Sorting
3 Customer Service
Is there a formula I can use to accomplish this, noting that it ignores duplicates and groups the different ids? Ordering of the jobs does not matter.
Working example here.
The code is like:
=unique(transpose(split(join(", ",filter(B1:B10,A1:A10=1)),", ")))
where
filter(B1:B10,A1:A10=1) gives you all the B values where A = 1
join(",", filter(...)) joins the list with the ", " separator (e.g. "apple, orange" and "kiwi" becomes "apple, orange, kiwi"
split(join(...)) splits the list into an array (e.g. back to [apple, orange, kiwi]
transpose(split(...)) converts the horizontal list to vertical list
unique(transpose(...)) gives you the unique values (unique() only works with vertical list)
After this, you need to transpose then join the list
Note you must keep the separator consistent (e.g. always "," or ", ")
This is Apps Script code instead of a function. To use it, you will need to use the Tools menu, and open the script editor. Then select the function name from the drop down list, and then click the "Run" button.
To use this code, you need to have a source and a destination sheet. You will need to change the sheet names in the code to your sheet names. In this code, the source sheet is named 'Data'. You will need to change that to the name of your source sheet. In this code, the destination sheet is named 'Output', and is at the bottom of the code. This code gets data starting in row two, and writes the output data starting in row two. I tested it with your values and it works.
function concatCellData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Data');
var colOneData = sh.getRange(2, 1, sh.getLastRow()-1, 1).getValues();
var colTwoData = sh.getRange(2, 2, sh.getLastRow()-1, 1).getValues();
var newData = [],
newDataColOne = [],
colOneValue,
existInNewData = false,
colB_One,
colB_Two,
dataPosition,
thisValue,
combinedArrays = [],
arrayTemp = [];
for (var i=0;i<colOneData.length;i+=1) {
colOneValue = colOneData[i][0];
dataPosition = newDataColOne.indexOf(colOneValue);
existInNewData = dataPosition !== -1;
if (!existInNewData) {//If it doesn't exist in the new data, just write the values
newDataColOne.push(colOneValue);
newData.push([colOneValue, colTwoData[i][0]]);
continue;
};
colB_One = [];
colB_Two = [];
combinedArrays = []
arrayTemp = [];
colB_One = colTwoData[i][0].split(",");
colB_Two = newData[dataPosition][1];
colB_Two = colB_Two.split(",");
var combinedArrays = colB_One.concat(colB_Two);
//Get unique values
for (var j=0;j<combinedArrays.length;j+=1) {
thisValue = combinedArrays[j].trim();
if (arrayTemp.indexOf(thisValue) === -1) {
arrayTemp.push(thisValue);
};
};
newData[dataPosition] = [colOneValue, arrayTemp.toString()]; //Overwrite existing data
};
ss.getSheetByName('Output').getRange(2, 1, newData.length, newData[0].length).setValues(newData);
};
Let I have two column, A and B . How I can find duplicates of this two column combination?
As shown in the picture, duplicate of combination of two column will be colored.
You can get the duplicates highlighted, with the help of following script:
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{name : "Check Duplicates",functionName : "duplicates"}];
sheet.addMenu("Scripts", entries);
};
function duplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Sheet1');
var r = s.getRange("A:B");
var v = r.getValues();
r.setBackground('white');
var f = r.getBackgrounds();
var lastrow = getLastPopulatedRow(v);
for( var i=0;i<lastrow;i++){
for( var j=i+1;j<lastrow;j++){
if( ( v[i][0]==v[j][0] && v[i][1]==v[j][1] ) || ( v[i][0]==v[j][1] && v[i][1]==v[j][0] ) ) {
f[i][0]='lightgreen';
f[i][1]='lightgreen';
f[j][0]='lightgreen';
f[j][1]='lightgreen';
}
}
}
r.setBackgrounds(f);
};
function getLastPopulatedRow(data) {
for (var i=data.length-1;i>=0;i--)
for (var j=0;j<data[0].length;j++)
if (data[i][j]) return i+1;
return 0;
};
Run the function "duplicates" from the script editor. You will also be able to run it from the custom menu "Scripts" from your spreadsheet.
Here is the Screenshot
I think #jwilson gave the proper answer.
However, here is a shortcut method, it may help you.
Select a cell and use CONCATENATE function to join two column value with a space between them. Then you can search the current column if they have duplicates.
For example, using your example image, select your column C1 and add =CONCATENATE(A1," ",B1) and drag this to applicable the formula for all cells in column C. Then add a conditional format rules for column C to check duplicates using =countif(C:C,C1)>1.
Here what is happening is, value of column A and B is concatenating first, then compared if those have duplicate entry in the record.
Use the following Custom formula for Conditional formatting:
=countif(arrayformula($A:$A&"|"&$B:$B);$A1&"|"&$B1)>1