I want to shuffle a list record , I am create a VF page to show the Question on that. when page is reload then every time want Another Question on the page randomly.
I am using
Integer count = [SELECT COUNT() FROM Question__c ];
Integer rand = Math.floor(Math.random() * count).intValue();
List<Question__c > randomQuestion = [SELECT Id, Question__c
FROM Question__c
LIMIT 1000 OFFSET :rand];
system.debug('<<<<<<<<<>>>>>>>>>'+randomQuestion);
in developer console its going good but on vf page its not working
Question not showing or limited Question showing
Any one suggest me better way to show the Question on the vf page
Thanks In Advance
I built a solution that users the Fishe-Yates shuffle:
list<Question__c> quesLst = [select id, Question__c, RecordType.Name, Answer__c, Option_A__c, Option_B__c, Option_C__c, Option_D__c from
Question__c limit 1000];
randomize(quesLst);
private list<Question__c> randomize(list<Question__c> lst){
integer currentIndex = lst.size();
Question__c Question;
integer randomIndex;
// While there remain elements to shuffle...
while (0 != currentIndex) {
// Pick a remaining element...
randomIndex = integer.valueOf(Math.floor(Math.random() * currentIndex));
currentIndex -= 1;
// And swap it with the current element.
Question = lst[currentIndex];
lst[currentIndex] = lst[randomIndex];
lst[randomIndex] = Question;
}
return lst;
}
Related
This question already has answers here:
Google Script version of VLookup (More Efficient Method?)
(2 answers)
Closed 2 years ago.
I need your help please. I would like to do a for loop or something else that works like a Formula =Vlookup
I have two Sheets. in Sheet1 (Overview) there are ID's like 1000, 1002, 1003,...,100X in Column A;
Sheet2 is a Form Response Sheet (Response), where you need to enter your ID and an Action with 'Ok' and 'Nok'. The ID I enter appears in Sheet2 Column B and the Action (Ok/Nok) apperas in Sheet2 Column C.
Now I would like to Copy the Ok/Nok to the Row with the same ID in the Overview sheet with a onFormSubmit function.
for Example. Person with ID 1005 makes a form response with the Action 'Ok'. Now should the 'Ok' copied to the Overview sheet in Column B and in the exact row (in this case: row with the ID 1005).
Here is my function. but I don't want to have formulars in the sheet. so I aked for another solution.
function vlookup() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var cell = sheet.getRange(1,5);
cell.setFormula('=ARRAYFORMULA(IFS(ROW(Response!B:B)=1,"Action from
User",Response!B:B="","",TRUE,IFERROR(VLOOKUP(A:A,Response!B:C,2,0),"Waiting for Response")))');
}
Hope someone can help me.
Thank you in advance for your help!
Jonas
Thank you for all these answers. I tryed the code from stackoverflow.com/questions/60255775 – TheMaster and that workes fine!
but it seams very complicated for a programming beginner. exspecially the part with the "Hash".
I also added a second compare and copy to get the data from a Reason if the Nok is used in the Form.
const ss = SpreadsheetApp.getActive();
/**
* #param {GoogleAppsScript.Spreadsheet.Sheet} fromSht -Sheet to import from
* #param {GoogleAppsScript.Spreadsheet.Sheet} toSht -Sheet to import to
* #param {Number} fromCompCol -Column number of fromSht to compare
* #param {Number} toCompCol -Column number of toSht to compare
* #param {Number} fromCol -Column number of fromSht to get result
* #param {Number} toCol -Column number of toSht to get result
*/
function copyToOverview(e,response,
fromSht = ss.getSheetByName('Response'),
toSht = ss.getSheetByName('Overview'),
fromCompCol = 2,
toCompCol = 1,
fromCol = 3,
toCol = 2,
fromColRej = 4,
toColRej = 3
) {
const toShtLr = toSht.getLastRow();
const toCompArr = toSht.getRange(2, toCompCol, toShtLr - 1, 1).getValues();
const fromArr = fromSht.getDataRange().getValues();
fromCompCol--;
fromCol--;
fromColRej--;
/*Create a hash object of fromSheet*/
const obj1 = fromArr.reduce((obj, row) => {
let el = row[fromCompCol];
el in obj ? null : (obj[el] = row[fromCol]);
return obj;
}, {});
/*Create a second hash object of fromSheet to copy the Reason why it is Nok (also from filling out the Form) */
const obj3 = fromArr.reduce((obj2, row) => {
let el1 = row[fromCompCol];
el1 in obj2 ? null : (obj2[el1] = row[fromColRej]);
return obj2;
}, {});
//Paste to column first toSht copy the "ok/nok" second toSht for the Reason why Nok
toSht
.getRange(2, toCol, toShtLr - 1, 1)
.setValues(toCompArr.map(row => (row[0] in obj1 ? [obj1[row[0]]] : [null])));
toSht
.getRange(2, toColRej, toShtLr - 1, 1)
.setValues(toCompArr.map(row => (row[0] in obj3 ? [obj3[row[0]]] : [null])));
}
I also tried the Code from "Michiel the Temp" and it seams, that it also works.
The code from "Mateo Randwolf" looks very simple and I tried it too. Works also very good!
I have modified it a bit and it works like I wish! I think I will use this code.
function onFormSubmit(e) {
// Get the sheet where the form responses are submitted and the one where we want to check the IDs
var formSheet = SpreadsheetApp.getActive().getSheetByName('Response');
var destinationSheet = SpreadsheetApp.getActive().getSheetByName('Overview');
// Get the new incoming data (ID and Ok/Nok) with each form submit by accessing
// the trigger object e which is the submited and new form response row
var submittedId = formSheet.getRange(e.range.getRow(), 2).getValue();
var submittedValue = formSheet.getRange(e.range.getRow(), 3).getValue();
var submittedValueReason = formSheet.getRange(e.range.getRow(), 4).getValue();
// get all the ID values we have in the sheet we want to check them. flat will convert all the returning
// 2D array of values in a 1D array with all the IDs
var idRange = destinationSheet.getRange(1, 1, destinationSheet.getLastRow(),1).getValues().flat();
// iterate over all your IDs
for(i=0;i<idRange.length;i++){
// if one ID is the same as the incoming one from the form response
if(idRange[i] == submittedId){
// set its value to the one submitted by the form
destinationSheet.getRange(i+1, 2).setValue(submittedValue);
}
if(idRange[i] == submittedId){
destinationSheet.getRange(i+1, 3).setValue(submittedValueReason);
destinationSheet.getRange(i+1, 2).getValue() == "Nok" ? destinationSheet.getRange(i+1, 4).setValue("Closed") : destinationSheet.getRange(i+1, 4).setValue("Open");
}
}
}
Thank you all for the Help you are amazing!
So I can do my next step in the Project with updating checkboxes in the Form.
I didn't test this with a trigger but this should work
function vlookup() {
var ssOverview = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Overview");
var ssOverviewLr = ssOverview.getLastRow();
var ssOverviewData = ssOverview.getRange(2, 1, ssOverviewLr, 1).getValues(); //assuming you have a header in the first row
var ssResponse = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Response");
var ssResponseLr = ssResponse.getLastRow();
var newResponse = ssResponse.getRange(ssResponseLr, 2, 1, 2).getValues();
var Ids = ssOverviewData.map(function (r){return r[0];});
for(var i = 0; i < newResponse.length; i++)
{
var row = newResponse[i];
var id = row[0];
var action = row[1];
var index = Ids.indexOf(id);
if(index == -1)
{
SpreadsheetApp.getActiveSpreadsheet().toast("No matches", "Be aware")
}
else
{
ssOverview.getRange(index + 2, 2).setValue(action); //this puts the action in column B
}
}
}
In order to check the IDs every time there is a new form submission and change the data in the ID sheet accordingly you will need to use installable triggers. Specifically you should use a FormSubmit trigger which triggers the function every time there is a form submission. Along with this trigger you will use its event object.
To add an installable trigger, in your Apps Script editor go to Edit -> Current project's triggers and create a new trigger by clicking Add trigger. Make sure that you select On form submit as the event type and that you select the function presented below (so please first copy/paste the function below before creating your trigger).
The following function takes use of this trigger event to compare the incoming data to your Column A of IDs and check for matches and if so it adds the relevant Ok/Nok information. It has self explanatory comments:
function onFormSubmit(e) {
// Get the sheet where the form responses are submitted and the one where we want to check the IDs
var formSheet = SpreadsheetApp.getActive().getSheetByName('Form Responses 1');
var destinationSheet = SpreadsheetApp.getActive().getSheetByName('Check');
// Get the new incoming data (ID and Ok/Nok) with each form submit by accessing
// the trigger object e which is the submited and new form response row
var submittedId = formSheet.getRange(e.range.getRow(), 2).getValue();
var submittedValue = formSheet.getRange(e.range.getRow(), 3).getValue();
// get all the ID values we have in the sheet we want to check them. flat will convert all the returning
// 2D array of values in a 1D array with all the IDs
var idRange = destinationSheet.getRange(1, 1, destinationSheet.getLastRow(),1).getValues().flat();
// iterate over all your IDs
for(i=0;i<idRange.length;i++){
// if one ID is the same as the incoming one from the form response
if(idRange[i] == submittedId){
// set its value to the one submitted by the form
destinationSheet.getRange(i+1, 2).setValue(submittedValue);
}
}
}
I have a tableView(purchases cart), which include magazines in multiple sections. In section, we can see cells with stuff from magazine. In the cell, I did display UILabel(price), UILabel(count) and UILabel with summary price (item * count) in one cell. Also, we can see two buttons (plus and minus), for changing count of item. Example -
var count: Float = Float(cell.countLabel.text!)!
guard Int(count) > 1 else { return }
var shortPrice = cell.newPrice.text
shortPrice?.removeLast(2)
let floatPrice = Float(shortPrice!)
count -= 1
let newSumShortPrice = floatPrice! * count
cell.countLabel.text = String(Int(count))
cell.summPrice.text = "\(newSumShortPrice) ₽"
But changes didn't work with an array.
The strcuct of my model -
struct ViewModel {
var name: String?
var offers: [Offers]?
}
struct Offers : Mappable {
var count : Int?
var fullPrice : String?
var shortPrice : String?
}
var purchasesViewModel = [PurchaseList.Fetch.ViewModel]()
I know, that I must pass changed data (count) to my array and use method tableView.reloadData(). But I can't, because I don't know how to do that.
How I can transfer new count value (check struct Offers) to array purchasesViewModel?
You can go to the index of the array and delete the particular data(old data) from purchasesViewModel and recreate new data and insert it on that index. I hope this will work.
long time no see, quick question about multipicklist in Apex.
Here is the condition:
1. Two standard objects: Task and Account, task is linked to account.
2. The subject field in the task contains three values: A, B, C.
3. Also, there is a field (Multipicklist)in the account contains the same values A, B, C
Every time I will create a task under a certain account, If I input subject with A, I hope the field in the account can be updated with A;
Then if I create a task with subject B, the field in the account should be (A;B)
So, here is my code:
if(IsSC && t.Status == PickListValuesStandard.Task_Completed){
Account student = new Account(Id = t.WhatId);
student.LatestCompletedActivity__pc = t.Subject;
student.LatestCompletedActivityDate__pc = t.ActivityDate;
if(t.Subject.contains('Post OC Call')){
student.Center_TouchPoints__c += (';Post OC Call');
}
if(t.Subject.contains('Third Week Call')){
student.Center_TouchPoints__c += (';Third Week Call');
}
update student;
}
The bolded part I attached above should be working like I described, Unfortunately, it didn't.
Can anyone help me understand the scenario? How can I achieve this?
Thanks in advance,
I just realized what a big mistake I've made, here is the thought, if I wanna update the multipicklist in the account, first thing first, I need to check whether it's null or not. Here is the update:
if(IsSC && t.Status == PickListValuesStandard.Task_Completed){
//Account student = new Account(Id = t.WhatId);
Account student = [select Center_TouchPoints__c from Account where Id=:t.WhatId];
student.LatestCompletedActivity__pc = t.Subject;
student.LatestCompletedActivityDate__pc = t.ActivityDate;
if(student.Center_TouchPoints__c==null){
if(t.Subject.contains('Post OC Call')){
student.Center_TouchPoints__c = 'Post OC Call;';
}
if(t.Subject.contains('Third Week Call')){
student.Center_TouchPoints__c = 'Third Week Call;';
}
}else if(student.Center_TouchPoints__c.contains ('Post OC Call') && t.Subject.contains('Third Week Call')){
student.Center_TouchPoints__c += ';Third Week Call';
}
update student;
}
However, if anyone of you has any better idea, please shoot me!
One way that might make this a bit more flexible is to loop through all the available values in the picklist and see if the task's subject contains that picklist value and if it does add that to the selected picklist values. This is assuming the name or label of the picklist entries match the values entered into the subject of the task. This way you will be able to add different entries or change the picklist values without changing the code. Here is an example of getting and looping through all picklist values
here's how I think that would look...
if(IsSC && t.Status == PickListValuesStandard.Task_Completed){
Account student = [select Center_TouchPoints__c from Account where Id=:t.WhatId];
student.LatestCompletedActivity__pc = t.Subject;
student.LatestCompletedActivityDate__pc = t.ActivityDate;
Schema.DescribeFieldResult fieldDescribe = account.Center_TouchPoints__c.getDescribe();
//retrieves picklist values
List<Schema.PicklistEntry> picklistVals = fieldResult.getPicklistValues();
String picklistString = '';
for(Schema.PicklistEntry plv : picklistVals){ //Loop through picklist values
if(t.subject.contains(plv.getValue()) && !student.Center_TouchPoints__c.contains(plv.getValue())) {
//If if this picklist value is in the subject and it hasn't been selected already add it.
picklistString += (plv.getValue() + '; ');
}
}
if(student.Center_TouchPoints__c == null)
student.Center_TouchPoints__c = picklistString.substring(1);
else
student.Center_TouchPoints__c += (';' + picklistString);
update student;
}
Hope this helps!
I have written a custom function for Google Sheets in Apps Script. The goal is to have a sheet which automatically calculates who owes how much money to whom (e.g. to split a bill).
My sheet looks like this:
The first bill (Restaurant) is to be split among all 5 and the second bill is to be split among all 5 except Peter, because there is no 0 in B3.
The input for my Apps Script function will be cells B1 to F3 (thus, values AND names). The function works fine - it calculates the correct results. I open that spreadsheet via browser (sheets.google.com) AND via my phone app (Google Sheets). However, on my phone it often happens that the result cell (with the formula =calc_debt(B1:F3)) only displays "Loading ...". What's the problem?
For the sake of completeness, here is custom function's code:
function calc_debt(input) {
var credit = [0, 0, 0, 0, 0]; // credit[0] = Peter, credit[1] = Mark ...
for (var i = 1; i < input.length; i++) { // starting at i = 1 to skip the first row, which is the names!
// first: calculate how much everybody has to pay
var sum = 0;
var people = 0;
for (var j = 0; j <= 4; j++) {
if (input[i][j] !== "") {
sum += input[i][j];
people += 1;
}
}
var avg_payment = sum / people;
// second: calculate who has payed too much or too little
for (var j = 0; j <= 4; j++) {
if (input[i][j] !== "") {
credit[j] += input[i][j] - avg_payment;
}
}
}
// this function is needed later
function test_zero (value) {
return value < 0.00001;
};
var res = ""; // this variable will contain the result string, something like "Peter to Mark: 13,8 | Katy to ..."
while (!credit.every(test_zero)) {
var lowest = credit.indexOf(Math.min.apply(null, credit)); // find the person with the lowest credit balance (will be minus!)
var highest = credit.indexOf(Math.max.apply(null, credit)); // find the person with the highest credit balance (will be plus!)
var exchange = Math.min(Math.abs(credit[lowest]), Math.abs(credit[highest])); // find out by how much we can equalize these two against each other
credit[lowest] += exchange;
credit[highest] -= exchange;
res += input[0][lowest] + " to " + input[0][highest] + ": " + exchange.toFixed(2) + " | "; // input[0] = the row with the names.
}
return res;
}
I'm having a similar issue in the android app that loading a custom formula sometimes just shows 'Loading...', while in the web it always works fine. I've found a workaround to load the formulas in the android app:
Menu - > Export - > Save as - > PDF.
This will take a moment and behind the modal loading indicator you will see that the formulars eventually resolve. You can wait for the export to finish or cancel it as soon as you see your formular was resolved.
Also making the document available offline via the menu toggle could resolve the formulars.
Another thing you could do is using caching in your script. So whenever you use the web version to render more complex formulars the results are being stored and immediately loaded for the mobile app. Unfortunately, the Google cache is limited in time and does invalidate after a few hours. See here for more information:
https://developers.google.com/apps-script/reference/cache/
This two things work quite well. However, I'm searching for a better solution. Let me know if you find one.
Solved follow the solution provided here ..
Menu - > Export - > Save as - > PDF
This forces the script to run on mobile and be readable by the mobile sheet
I'm working on a to do list with a simple collaboration feature.
Basically a user says that he wants to share a group of todo items.
The structure is that a item belongs to a group and that group is available to a set of users.
Now to get the list of todo items for a specific user, I need to figure out all items in groups the user has access too.
So basically the structure looks somewhat like this:
user - group - item
What i want is given a user id I want the items for all the groups the user has access to.
From my early experiments it seems to be somewhat problematic to produce a view with the desired output.
Is CouchDB a good fit, or should I be looking for another database to work with?
If I understand it correctly, each group has a list of users and a list of items. Then, for each group, you can emit a row for each (user, item) pair:
emit(userId, { _id: itemId });
Querying the view with key="userId", should give the correct result.
For example, if a group document has the following structure:
{
"_id": "Group_gid1",
"userIds": ["User_uid1", ..., "User_uidN"],
"itemIds": ["Item_iid1", ..., "Item_iidM"]
}
Your map function can be something like (not tested!!!):
function (doc) {
var i, ii, userId, j, jj, itemId, ids = doc._id.split('_');
if (ids[0] === 'Group' && doc.userIds && doc.itemIds) {
for(i = 0, ii = doc.userIds.length, jj = doc.itemIds.length; i < ii; i += 1) {
userId = doc.userIds[i];
for(j = 0; j < jj; j += 1) {
itemId = doc.itemIds[j];
emit(userId, { _id: itemId });
}
}
}
}
If you query it with key="User_1"&include_docs=true it should return all items in the groups of the user with _id=="User_1".