I have the following script which runs as required taking email addresses from USERS sheet and matching them with the email address in the JOBS sheet but only when the letter cases of the email addresses are an exact match. I'm trying to add a regex to the script to ignore the letter cases however unsure how to go about this.
I have added the regex at
var companyValue = /[a-z][#][0-9]/i.users[indices[i]][3]
function updateUserData1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var jobSheet = ss.getSheetByName('JOBS');
var userSheet = SpreadsheetApp.openById("1Zog54Yh3r5TyEaLLf3lHUbPGAikiu9HBOmBMtbbwwas").getSheetByName('USERS');
// get products and jobs
var users = userSheet.getRange("B2:O" + userSheet.getLastRow()).getValues()
var jobsRange = jobSheet.getRange("F2:AW" + jobSheet.getLastRow())
var jobs = jobsRange.getValues()
// get the list of products on the Jobs sheet and Product Codes on the products sheet
var jobProducts = jobs.map(function(e){return e[7] })
var prodCodes = users.map(function(e){return e[1]})
// find the indices of every Job in the Product array
var indices = jobProducts.map(
Map.prototype.get,
prodCodes.reduce((m, v, i) => m.set(v, i), new Map)
);
var priceArray = []
// for each job get the Products index and the Price and push the price onto an array
jobs.forEach((v,i) => {
// assign value depending whether currency = Sterling or Euro
if (jobs[i][7] !="" ){
var companyValue = /[a-z][#][0-9]/i.users[indices[i]][3]
}
priceArray.push([companyValue])
})
jobsRange.offset(0, 1,jobs.length,1).setValues(priceArray)
Thanks for any solution
In order to provide a proper response to the question, I'm writing this answer as a community wiki, since the issue was resolved from the comments section.
Not sure how you're doing the comparison but instead of adding regex it would be better to just convert the strings to lowercase before comparing. For this you can use the JavaScript method toLowerCase().
Related
I have a google sheet with the following data
google sheet "formatfruit"
Each user has a fruit and a vegetable associated, I want to know the percentage of similarity between each user in the google sheet "formatfruit"
Today I can compare the first user kevin with all the others and return his percentage of similarity in another google sheet called "matchofruit".
I associated the value "1" when a user has a fruit or a vegetable in common with kevin and the value "0" if the user has no fruit or vegetable in common.
The result that appears in the google sheet matchofruit is here
google sheet matchofruit
The code I used is below
function myFunction() {
var formafruit = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("fruit");
var matchofruit = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("matchofruit");
var n = formafruit.getLastRow();
var user1 = formafruit.getRange(2,1).getValues();// name user 1 : kévin
var user2 = formafruit.getRange(3,1).getValues();// name user 2 : mikael
for (var i = 2;i<4;i++) { // i<4 because we have 3 column in formafruit
for (var z = 2;z<n+1;z++) {
matchofruit.getRange(z,1).setValue(user1); // Return the name of the users in the first column
if(formafruit.getRange(2,i).getValue() === formafruit.getRange(z,i).getValue()){ // Compare the fruits and vegetables associated to kévin with the fruits and vegetables associated to each user
matchofruit.getRange(z,i).setValue(1); // Returns 1 if kevin shares at least one fruit or vegetable in common with a user
}
else {
matchofruit.getRange(z,i).setValue(0);
}
}
}
// Calculate the % of common values
for (var p = 0;p<n-1;p++){}
for (var s = 0;s<n-1;s++) {
var scoreforall = matchofruit.getRange(2,2,p,11).getValues()[s]// get the array of all the matches
let sum = 0;
for (let e = 0; e < scoreforall.length; e++) {
sum += scoreforall[e]; // add each array together
}
var sumTotal= Math.round(sum*(100/2)); // convert in percentage each sum
matchofruit.getRange(s+2,4).setValue(sumTotal); // send match values in column 4
}
// Return the result in a sentence
for (var a = 2;a<n+1;a++) {
var usern = formafruit.getRange(a,1).getValues(); //get all the users' emails in the formafruit
var valeurmatch = matchofruit.getRange (a,4).getValues(); // get value % of matches
matchofruit.getRange(a,5).setValue(user1+" "+"have"+" "+valeurmatch+"%"+" "+"of values in common with"+" "+usern);//Return the % of common value between Kevin and the other users
}
}
I would like to be able to do the same for mikael, gauthier, vanessa and mireille knowing that I only put 5 users to simplify the problem but that in truth there can be more than 100 users and that each user has more than 11 associated values(here we have only 2 different type of values, fruits and vegetables). It's been several weeks that I'm looking for a solution to my problem and I haven't found anything to solve it. Do you have an idea?
Thanks!
I believe your goal is as follows.
You want to achieve the following situations. (The following images are from OP's question.)
From
To
In your situation, for example, when 5 users are used, you want to create 25 rows.
When I saw your script, the methods of setValues and getValues are used in the loop. I think that this becomes the high process cost. Ref So, I would like to propose the following modification.
Modified script:
function myFunction2() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [src, dst] = ["fruit", "matchofruit"].map(s => ss.getSheetByName(s));
const [, ...values] = src.getDataRange().getValues();
const res = values.flatMap(([a1, ...v1]) =>
values.map(([a2, ...v2]) => {
const temp = v1.map((e, i) => e == v2[i] ? 1 : 0);
const sum = (temp.filter(f => f == 1).length / temp.length) * 100;
const matchResult = `${a1} have ${sum}% of values in common with ${a2}`;
return [a1, ...temp, sum, matchResult];
})
);
dst.getRange(2, 1, res.length, res[0].length).setValues(res);
}
In this modification, the values are retrieved from "fruit" sheet. And, an array for putting to "matchofruit" sheet is created. And then, the created array is put into "matchofruit" sheet.
Note:
In this sample script, the header row of "matchofruit" has already been put. If you want to put the header row to "matchofruit" sheet, please add it to my proposed script.
References:
map()
filter()
I have a google spreadsheet data in form of array. Now I want to push the array position I used for loop which is working fine on small data but when the data length is increased it result in delay.
Is there a faster way to push the array position in the array.
Here is the code which I am currently using:-
var ss = SpreadsheetApp.openById('19zxxxxxxxxxxxxxxxxxxxxxxxxOI');
var sheet1 = ss.getSheetByName('Sheet2');
var data = sheet1.getRange("A:H").getValues();
var email = Session.getActiveUser().getEmail();
for(var i = 0; i < data.length; i++)
{
data.unshift(i+1);
} // this for loop takes too much time.
data = data.filter(function(item){return item[7] == email});
var x = data.map(function(val){
return val.slice(0, -7);
})
Logger.log(x)
return x;
}
I believe your goal as follows.
If data.unshift(i+1) is data[i].unshift(i+1) as TheMaster's comment, you want to retrieve the values of the column "A" when the value of column "G" is the same with email. At that time, you want to add the row number to the 1st index of the row value.
From your script, I understood like this.
You want to reduce the process cost of this situation.
For this problem, how about this solution?
Pattern 1:
In this pattern, your script is modified. In this case, the result values are retrieve by one loop.
Sample script:
function myFunction() {
var ss = SpreadsheetApp.openById('19zxxxxxxxxxxxxxxxxxxxxxxxxOI');
var sheet1 = ss.getSheetByName('Sheet2');
var data = sheet1.getRange("A1:H" + sheet1.getLastRow()).getValues(); // Modified
var email = Session.getActiveUser().getEmail();
const res = data.reduce((ar, [a,,,,,,g], i) => { // Modified
if (g == email) ar.push([i + 1, a]);
return ar;
}, []);
Logger.log(res)
return res;
}
Pattern 2:
In this pattern, as other method, TextFinder and Sheets API are used. In this case, the size of base data by searching email with TextFinder can be reduced. And each values are retrieved by one API call using Sheets API.
Sample script:
Before you use this script, please enable Sheets API at Advanced Google services.
function myFunction() {
const spreadsheetId = '19zxxxxxxxxxxxxxxxxxxxxxxxxOI';
const ss = SpreadsheetApp.openById(spreadsheetId);
const sheet = ss.getSheetByName('Sheet2');
const email = Session.getActiveUser().getEmail();
// 1. Retrieve the ranges of rows by searching "email" at the column "G".
const ranges = sheet.getRange("G1:G" + sheet.getLastRow()).createTextFinder(email).findAll();
// 2. Create an object for using with Sheets API.
const reqs = ranges.reduce((o, e) => {
const row = e.getRow();
o.rows.push(row);
o.ranges.push("A" + row);
return o;
}, {rows: [], ranges: []});
// 3. Retrieve values and add the row number.
const res = Sheets.Spreadsheets.Values.batchGet(spreadsheetId, {ranges: reqs.ranges})
.valueRanges
.map((e, i) => ([reqs.rows[i], e.values[0][0]]));
Logger.log(res)
return res;
}
If email is included other string, please use matchEntireCell(true) to TextFinder.
References:
reduce()
Advanced Google services
Method: spreadsheets.values.batchGet
I currently have a half solution to this, but I was wondering if there is a better way to write this. I have the following array with equipment deliveries:
var deliveries = ["14/02/2020, 11:47,cubicles,A32", "13/02/2020,11:48,relays,A59",etc....]
Which I search through using an input:
var pn = document.getElementById("PN").value;
pn = pn.split(" ");
What I'm trying to achieve is to split the search word with spaces, and check if an index in the array contains all of these words. I currently search up to three words, since the code would become too long using my "solution", if I searched for more. This is my code:
var sArray = []//search results
for(var i = 0; i < deliveries.length; i++){
if (pn.length == 2) {
if(new RegExp(pn[0],"i").test(deliveries[i]) && new RegExp(pn[1],"i").test(deliveries[i])) sArray.push(deliveries[i]);
}
else if (pn.length == 3) {
if(new RegExp(pn[0],"i").test(deliveries[i]) && new RegExp(pn[1],"i").test(deliveries[i])&& new RegExp(pn[2],"i").test(deliveries[i])) sArray.push(deliveries[i]);
}
else {if(new RegExp(pn[0],"i").test(deliveries[i])) sArray.push(deliveries[i])};
}
What would be the correct way to search using all the words in the pn array?
I tried saving the if statements as a string, adding code to the string for each index of pn and then using eval. This turned out to slow the search to a crawl though.
Any help would be appreciated.
Example added for a user searching for "cub a32":
pn = "cub a32"
Which turns into:
pn = ["cub, "a32"]
Results in:
sArray = ["14/02/2020, 11:47,cubicles,A32"]
You could filter the array and check with every or some, depending if you want all or just one search string in an item of deliveries.
var deliveries = ["14/02/2020, 11:47,cubicles,A32", "13/02/2020,11:48,relays,A59"],
input = "cub a32",
pn = input.toLowerCase().split(' '),
result = deliveries.filter(s => pn.every(v => s.toLowerCase().includes(v)));
console.log(result);
I have a inventory list sheet and I have a shopping list sheet. I am scripting the onEdit() function that gets the name(code) in a certain cell in shopping list sheet and search through name column in inventory list sheet. When it finds the EXACT MATCH, it will return the row number and I will do my next move.
Problems:
Some names are in Persian language and in this case it is finding
the first row with a text in it ( it doesn't matter if its the same
or not)
With iterating its giving different results with changing the value
(even if the whole column are numbers or text or ...)
After a lot of searching, I found below code it is good but its not
finding the exact match
//sheetss name
var inventory = "inventory"
var shopping = "shopping"
// sheets
var ss = SpreadsheetApp.getActiveSpreadsheet()
var activeSheet = ss.getActiveSheet()
var inventorySheet = ss.getSheetByName(inventory)
var lr = anbardariSheet.getLastRow()
//var sh = getSheet(); //custom function that returns target Sheet;
var rng = activeSheet.getRange(3,3, lr, 1); //change to desired Range boundaries;
//create TextFinder and configure;
var tf = rng.createTextFinder('14'); //
tf.matchCase(false); //{Boolean} -> match target text's case or not;
tf.matchEntireCell(false); //{Boolean} -> check the whole Range or within;
tf.ignoreDiacritics(true); //{Boolean} -> ignore diacretic signs during match;
tf.matchFormulaText(false); //{Boolean} -> search in formulas (if any) or values;
//invoke search;
var res = tf.findNext();
//do something with result;
if(res!==null) {
var vals = res.getvalues();
Logger.log(vals);
}
Some problems i think you may have in your code to achieve what you want:
1) You're doing the search in the activeSheet, are you sure this is the inventory sheet?
2) You're using matchEntireCell [1] with a false parameter, if you want an exact match you have to use a true parameter, otherwise you'll have partial matches like you have now (i.e. 14 could match with 2214).
I fixed the previous problems and here is the code i tested in a onEdit function. I got the value to compare from the last row in the shopping sheet.
//sheetss name
var inventory = "inventory"
var shopping = "shopping"
// sheets
var ss = SpreadsheetApp.getActiveSpreadsheet()
var shoppingSheet = ss.getSheetByName(shopping)
var inventorySheet = ss.getSheetByName(inventory)
var lr = inventorySheet.getLastRow();
//Get value to find
var lrValue = shoppingSheet.getLastRow();
var value = shoppingSheet.getRange(lrValue, 3).getValue();
Logger.log(value)
//var sh = getSheet(); //custom function that returns target Sheet;
var rng = inventorySheet.getRange(3,3, lr, 1); //change to desired Range boundaries;
//create TextFinder and configure;
var tf = rng.createTextFinder(value); //
tf.matchCase(false); //{Boolean} -> match target text's case or not;
tf.matchEntireCell(true); //{Boolean} -> check the whole Range or within;
tf.ignoreDiacritics(true); //{Boolean} -> ignore diacretic signs during match;
tf.matchFormulaText(false); //{Boolean} -> search in formulas (if any) or values;
//invoke search;
var res = tf.findNext();
//do something with result;
if(res!==null) {
var vals = res.getValues();
Logger.log(vals);
Logger.log(res.getA1Notation());
}
[1] https://developers.google.com/apps-script/reference/spreadsheet/text-finder#matchEntireCell(Boolean)
thank you guys both for answering in such short notice
#Cooper
#Andres Duarte
it solved my problem
thanks again
UPDATE: this is NOT a question on javascript alone, but related to the javascript implementation on the MarkLogic platform.
As the title of this question points out it is about the specific behaviour of the ValueIterator that is returned by the xdmp.userRoles() function.
I am trying to see if a user has a certain role in MarkLogic Security database, therefor I have done this :
declareUpdate();
var pid = '7610802';
// TODO validate that user can do this
var spo = 'scc-proj-' + pid + '-owner';
var spm = 'scc-proj-' + pid + '-member';
var spam = 'scc-proj-' + pid + '-adv-member';
// we need the security db Ids of these roles
var spoId = xdmp.role(spo);
var spamId = xdmp.role(spam);
var acceptedRoleIds = [spamId,spoId];
// get roleIds from sec db for this user
var userRoleIds = xdmp.userRoles('scc-user-1');
// map ValueIterator to array
var userRoleIdsArray = userRoleIds.toArray();
Now the userRoleIdsArray holds ids as unnsigned long like this:
[
"1088529792688125909",
"1452323661308702627",
"10258509559147330558",
"10161853410412530308",
"6677433310138437512",
"12773061729023600875",
"7482704131174481508",
"3191093315651213021", <<<<< this is the one!!!
"5126952842460325403",
"7089338530631756591",
"15520654661378671735",
"13041542794130379697"
]
Now indexOf() gives me -1 aka not found
userRoleIdsArray.indexOf(3191093315651213021);
OR
userRoleIdsArray.indexOf("3191093315651213021");
Gives :
-1
While
userRoleIdsArray[7]==3191093315651213021;
Gives :
true
What am I missing here? Is this not the way to use indexOf() ?
UPDATE >>> Stuff below was 'on-the-side' but turns out to be distracting from the above core question. The behaviour below is answered by #DaveCassel's comment.
btw on the created array acceptedRoleIds it is even more strange:
acceptedRoleIds.indexOf(spoId);
works
acceptedRoleIds.indexOf(3191093315651213021);
does not?
Could this large number error in javascript be relevant?
You want to find a String, not a number. Use: userRoleIdsArray.indexOf("3191093315651213021");
This works:
var array = [
"1088529792688125909",
"1452323661308702627",
"10258509559147330558",
"10161853410412530308",
"6677433310138437512",
"12773061729023600875",
"7482704131174481508",
"3191093315651213021",
"5126952842460325403",
"7089338530631756591",
"15520654661378671735",
"13041542794130379697"
];
var n = array.indexOf("13041542794130379697");
document.write(n);
output: 11
The mismatch is that ValueIterator.toArray() returns an array of Values (Value[]). When you call .indexOf, you're passing in a string or an unsignedLong rather than a value. Because the types don't match, .indexOf() doesn't report a match.
You can solve the problem by iterating through the loop. Note that I use the '==' operator rather than '==='; the type conversion is needed.
// get roleIds from sec db for this user
var userRoleIds = xdmp.userRoles('my-user');
// map ValueIterator to array
var userRoleIdsArray = userRoleIds.toArray();
var target = 15520654661378671735;
var index = -1;
for (var i in userRoleIdsArray) {
if (userRoleIdsArray[i] == target) {
index = i;
}
}
index