Counting number of occurrences in column - google-apps-script

Example (column A is input, columns B and C are to be auto-generated):
| A | B | C |
+-------+-------+-------+
| Name | Name | Count |
+-------+-------+-------+
| Joe | Joe | 2 |
| Lisa | Lisa | 3 |
| Jenny | Jenny | 2 |
| Lisa | | |
| Lisa | | |
| Joe | | |
| Jenny | | |
I know I can do this with the function below. However, I would like to do it with app scripts. I have tried nesting 2 for loops called (i & j). Where it started with i and j counts until it doesn't match i. Then j equals i so i just jumps to the new start point as to not double count and it kind of worked.
I kept having a problem where it would output a high number on the last 2 iterations or so... I don't think I saved the script after I could not get it to work. any thoughts or help would be appreciated.
The formula I would like to make a script:
=ArrayFormula(QUERY(A1:A16&{"",""},"select Col1, count(Col2) where Col1 != '' group by Col1 label count(Col2) 'Count'",1))

Try this:
It will read column A with header and generate B and C with headers
function pv() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const rg=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn());
const vs=rg.getValues();
let Obj={pA:[]};
vs.forEach(function(r,i){
if(!Obj.hasOwnProperty(r[0])) {
Obj[r[0]]=1;
Obj.pA.push(r[0]);
}else{
Obj[r[0]]+=1;
}
});
let oA=[["Name","Count"]];
Obj.pA.forEach(function(p) {
oA.push([p,Obj[p]]);
});
sh.getRange(1,2,oA.length,2).setValues(oA);
}
This is a simple pivot table

Related

Google script Multiple criteria in same row and validation of value in previous row

I'm starting with a script that someone here graciously helped with and need to build onto it and do not know where to start. Here is the current script:
function yourFunction(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getDataRange();//columns are fruit,status and then cost.
var vA=rg.getValues();
for(var i=1;i<vA.length;i++){
if(vA[i][0].toString()=='Apple' && vA[i][1].toString()=='Ripe' && vA[i][2].toString=='Large' && vA[i][4].toString=''){
vA[i][4]=5.5;
}
}
rg.setValues(vA);//This writes all of the data at one time.
}
What I would like to add to this is a second set of criteria that looks at another column value = Lot Number(Column D). Assuming that the current Lot Number is the same as the previous row's and where all the above match, each additional rows will be a set value 3. But if the value of the Lot Number before the current row is not the same, then the value is 5. In what I've read, there may need to be some looping condition in this so the calculations don't keep going on and on. Any help here would be much appreciated. Thanks!
Here is a link to a basic format of the spreadsheet Test Script
Here's an updated function that demonstrates the general approach for what I believe you're describing.
var COLUMNS = {
FRUIT: 0,
STATUS: 1,
SIZE: 2,
LOT_NUMBERr: 3,
COST: 4,
NEW_VALUE: 5,
}
function updateValues() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet3');
var range = sheet.getDataRange();
var values = range.getValues();
var previousLotNumber = -1;
for(var i = 1; i < values.length; i++){
if (values[i][COLUMNS.FRUIT] == 'Apple'
&& values[i][COLUMNS.STATUS] == 'Ripe') {
values[i][COLUMNS.COST] = 5.5;
}
if (previousLotNumber == values[i][COLUMNS.LOT_NUMBER]) {
values[i][COLUMNS.NEW_VALUE] = 3;
} else {
values[i][COLUMNS.NEW_VALUE] = 5;
}
previousLotNumber = values[i][COLUMNS.LOT_NUMBER];
}
range.setValues(values);
}
After running this function, this sheet looks like the following:
+--------+-----------+--------+------------+------+-----------+
| Fruit | Status | Size | Lot Number | Cost | New Value |
+--------+-----------+--------+------------+------+-----------+
| Apple | Ripe | Large | 101 | 5.5 | 5 |
| Apple | Ripe | Medium | 101 | 5.5 | 3 |
| Apple | Ripe | Large | 103 | 5.5 | 5 |
| Apple | Not Ready | Large | 102 | | 5 |
| Apple | Not Ready | Medium | 101 | | 5 |
| Banana | Ripe | Large | 201 | | 5 |
| Orange | Ripe | Large | 301 | | 5 |
| Orange | Not Ready | Medium | 301 | | 3 |
| Pear | Ripe | Large | 401 | | 5 |
+--------+-----------+--------+------------+------+-----------+
A few notes:
use descriptive variable names
use a set of constants to provide descriptive names for the columns
for your ask, all you need to do is use a variable to store the previous rows lot number, no additional looping complexity required

One to Many Count with one query?

I haven't touched the backend in a while.. so forgive me if this is super simple. I'm working with Lumen v.5.6.1.
| table.sets | | table.indexed_items |
|----------------| |---------------------------------|
| ID | SET | | ID | setId | itemId | have |
|----|-----------| |----|-------|--------|-----------|
| 1 | set name 1| | 1 | 3 | 1 | 2 |
| 2 | set name 2| | 2 | 3 | 2 | 1 |
| 3 | set name 3| | 3 | 3 | 3 | 4 |
| 4 | 2 | 4 | 1 |
| 5 | 2 | 5 | 3 |
| 6 | 2 | 6 | 1 |
How would I return in one query, groupedBy/distinct by setId (with set name as a left join?) to have a return like this:
[
setId: 2,
name: 'set name 2',
haveTotal: 5,
],
[
setId: 3,
name: 'set name 3',
haveTotal: 7,
]
Here is a raw MySQL query which should work. To convert this to Laravel should not be too much work, though you might need to use DB::raw once or twice.
SELECT
s.ID AS setId,
s.`SET` AS name,
COALESCE(SUM(ii.have), 0) AS haveTotal
FROM sets s
LEFT JOIN indexed_items ii
ON s.ID = ii.setId
GROUP BY
s.ID;
Demo
If you don't want to return sets having no entries in the indexed_items table, then you may remove the call to COALESCE, and you may also use an inner join instead of a left join.
Note that using SET to name your tables and columns is not a good idea because it is a MySQL keyword.
If you are using or want to use eloquent, you can do something like:
$sets = App\Sets::withCount('indexed_items')->get();
This will return a collection with a column name indexed_items_count
Obviously you will need to change depending on your model names.
Here are the docs
I always use in my project for count relation ship record.
$sets->indexed_items->count();

MySQL flexible conversion to numeric data

I have a MySQL database which has several categorical columns. In searching the database, having a conversion of categorical data and data in multiple columns to one numeric variable I could use for sorting would be nice.
Ideally this conversion would be a function and not just another data table since the mapping itself may change. It could probably be as simple the following code, but I’m not sure what the best way to do something like this in SQL would be. Thanks in advance.
a = 0
if b==“val1” {
a += 1
}
if c==2 { a += 2 }
if c==1 { a += 1 }
return a
Where a is the numeric column and b and c are values I’m mapping to a. Same example in table form with everything joined if these columns are in different tables.
+---+------+------+
| a | b | c |
+---+------+------+
| 0 | 3 | xxxx |
| 1 | 1 | xxxx |
| 2 | 2 | xxxx |
| 1 | 3 | val1 |
| 3 | 2 | val1 |
| 2 | 1 | val1 |
+---+------+------+

How do I select records in MySQL with multiple columns matching map of values?

I have the following 3-column table:
+----+---------+------------+
| ID | First | Last |
+----+---------+------------+
| 1 | Maurice | Richard |
| 2 | Yvan | Cournoyer |
| 3 | Carey | Price |
| 4 | Guy | Lafleur |
| 5 | Steve | Shutt |
+----+---------+------------+
If I want to look for everyone in (Maurice,Guy) I can do select * from table where first in (Maurice,Guy).
If I want to find just Maurice Richard, I can do select * from table where first = "Maurice" and last = "Richard".
How do I do a map, an array of multiples?
[
[Maurice, Richard]
[Guy,Lafleur]
[Yvan,Cournoyer]
]
If I have an arbitrary number of entries, I cannot construct a long complex where (first = "Maurice" and last = "Richard") or (first = "Guy" and last = "Lafleur") or .....
How do I do the moral equivalent of where (first, last) in ((Guy,Lafleur),(Maurice,Richard)) ?
You can do it just like you describe it:
SELECT *
FROM mytable
WHERE (first, last) IN (('Guy','Lafleur'),('Maurice','Richard'))
Demo here

can couchdb do loops

Can couchdb do loops?
Let's say I have a database of interests that have 3 fields
subject1,subject2,subject3. example, cats,nutrition,hair or space,telescopes,optics etc.
A person (A) has 10 interests composed of 3 fields each.
10 more people B,C,D...have 10 interests each composed of 3 subjects each.
When person A logs in I want the system to search for all people with matching interests.
In javascript I would normally loop through all the interests and then find matching ones I guess using
two loops. Then store the matches in another database for the user like "matchinginterests".
Is there any easy way to do this in couchdb compared to mysql -- which seems very complicated.
Thanks,
Dan
I think I understand what you are asking. The answer is pretty straightforward with Map/Reduce.
Say you have the following people documents:
{
"name": "Person A",
"interests" [ "computers", "fishing", "sports" ]
}
{
"name": "Person B",
"interests" [ "computers", "gaming" ]
}
{
"name": "Person C",
"interests" [ "hiking", "sports" ]
}
{
"name": "Person D",
"interests" [ "gaming" ]
}
You would probably want to emit your key as the interest, with the value as the person's name (or _id).
function (doc) {
for (var x = 0, len = doc.interests.length; x < len; x++) {
emit(doc.interests[x], doc..name);
}
}
Your view results would look like this:
computers => Person A
computers => Person B
fishing => Person A
gaming => Person B
gaming => Person D
hiking => Person C
sports => Person A
sports => Person C
To get a list of people with computers as an interest, you can simply send key="computers" as part of the query string.
If you want to add a reduce function to your map, you can simply use _count (shortcut to use a compiled reduce function) and you can retrieve a count of all the people with a particular interest, you can even use that to limit which interests you query to build your relationships.
When person A logs in I want the system to search for all people with matching interests.
SELECT i_them.* FROM interests AS i_me
INNER JOIN interests AS i_them ON (i_them.person != i_me.person) AND
((i_them.subject1 IN (i_me.subject1, i_me.subject2, i_me.subject3)) OR
(i_them.subject2 IN (i_me.subject1, i_me.subject2, i_me.subject3)) OR
(i_them.subject3 IN (i_me.subject1, i_me.subject2, i_me.subject3)))
WHERE i_me.person = 'A'
Is that what you wanted to do?
If you design your tables a little smarter though you'd do it like
SELECT DISTINCT them.* FROM person AS me
INNER JOIN interest AS i_me ON (i_me.person_id = me.id)
INNER JOIN interest AS i_them ON (i_them.subject = i_me.subject)
INNER JOIN person AS them ON (them.id = i_them.person.id AND them.id != me.id)
WHERE me.name = 'A'
Using the following tables
table interest
id integer primary key autoincrement
person_id integer //links to person table
subject varchar //one subject per row.
+-----+-----------+---------+
| id | person_id | subject |
+-----+-----------+---------+
| 1 | 3 | cat |
| 2 | 3 | stars |
| 3 | 3 | eminem |
| 4 | 1 | cat |
| 5 | 1 | dog |
| 6 | 2 | dog |
| 7 | 2 | cat |
table person
id integer primary key autoincrement
name varchar
address varchar
+-----+------+---------+
| id | name | address |
+-----+------+---------+
| 1 | A | here |
| 2 | Bill | there |
| 3 | Bob | everyw |
result
+-----+------+---------+
| id | name | address |
+-----+------+---------+
| 2 | Bill | there |
| 3 | Bob | everyw |
This is how (what you call) 'looping' in SQL works...
First you take person with name 'A' from the table.
me.id me.name me.address
| 1 | A | here |
You look up all the interests
me.id me.name me.address i_me.subject
| 1 | A | here | cat
| 1 | A | here | dog
Then you match everyone elses interests
me.id me.name me.address i_me.subject i_them.subject i_them.person_id
| 1 | A | here | cat | cat | 3
| 1 | A | here | cat | cat | 2
| 1 | A | here | dog | dog | 2
And then you match the person to them's interest (except for me of course)
me.id me.name me.address i_me.subject i_them.subject i_them.person_id them.name
| 1 | A | here | cat | cat | 3 | Bob
| 1 | A | here | cat | cat | 2 | Bill
| 1 | A | here | dog | dog | 2 | Bill
Then you return only the data from them and 'throw' the rest away, and remove duplicate rows DISTINCT.
Hope this helps.