searchRecord isn't rendering correct info or takes several times to display - google-sheets

I'm running a Apps Script to create a data entry form. Sometimes my searchRecord function works if I run it several times. Sometimes it doesn't pull any info, but never gives me the warning screen, sometimes it pulls the info from the last search. I'm not sure where the problem is. I have copied the code below.
//Function to Search the record
function searchRecord(){
var myGoogleSheet=SpreadsheetApp.getActiveSpreadsheet(); //declare a variable and set with active Google Sheet
var shUserForm=myGoogleSheet.getSheetByName("User Form"); //declare a variable and set with the User Form reference
var datasheet=myGoogleSheet.getSheetByName("Existing Customers"); //declare variable and set the reference of database sheet
var str=shUserForm.getRange("B2").getValue(); //get data input for search button
var values=datasheet.getDataRange().getValues(); //getting the entire values from the used range and assigning it to values variable
var valuesFound=false; //variable to store boolean value
for (var i=0; i<values.length; i++)
{
var rowValue=values[i]; //declare a variable and storing the value
//checking the first value of the record is equal to search item
if(rowValue[6]==str){
shUserForm.getRange("B12").setValue(rowValue[0]);
shUserForm.getRange("B15").setValue(rowValue[1]);
shUserForm.getRange("B23").setValue(rowValue[2]);
shUserForm.getRange("B17").setValue(rowValue[3]);
shUserForm.getRange("E17").setValue(rowValue[4]);
shUserForm.getRange("B8").setValue(rowValue[6]);
shUserForm.getRange("E8").setValue(rowValue[7]);
shUserForm.getRange("B19").setValue(rowValue[8]);
shUserForm.getRange("E19").setValue(rowValue[9]);
shUserForm.getRange("B21").setValue(rowValue[10]);
shUserForm.getRange("E21").setValue(rowValue[11]);
shUserForm.getRange("E23").setValue(rowValue[12]);
shUserForm.getRange("E12").setValue(rowValue[13]);
shUserForm.getRange("B25").setValue(rowValue[14]);
shUserForm.getRange("E25").setValue(rowValue[15]);
shUserForm.getRange("B27").setValue(rowValue[16]);
shUserForm.getRange("E27").setValue(rowValue[17]);
shUserForm.getRange("B29").setValue(rowValue[18]);
shUserForm.getRange("E29").setValue(rowValue[19]);
shUserForm.getRange("B31").setValue(rowValue[20]);
shUserForm.getRange("E10").setValue(rowValue[21]);
shUserForm.getRange("B33").setValue(rowValue[22]);
shUserForm.getRange("B35").setValue(rowValue[23]);
shUserForm.getRange("B37").setValue(rowValue[24]);
shUserForm.getRange("E31").setValue(rowValue[25]);
shUserForm.getRange("E35").setValue(rowValue[26]);
shUserForm.getRange("B39").setValue(rowValue[27]);
shUserForm.getRange("E39").setValue(rowValue[27]);
shUserForm.getRange("B41").setValue(rowValue[29]);
shUserForm.getRange("E15").setValue(rowValue[30]);
shUserForm.getRange("B43").setValue(rowValue[31]);
shUserForm.getRange("E43").setValue(rowValue[32]);
shUserForm.getRange("B45").setValue(rowValue[33]);
shUserForm.getRange("E45").setValue(rowValue[34]);
shUserForm.getRange("B47").setValue(rowValue[35]);
shUserForm.getRange("B49").setValue(rowValue[36]);
shUserForm.getRange("B10").setValue(rowValue[37]);
shUserForm.getRange("E37").setValue(rowValue[5]);
valuesFound=true;
return;//come out from the loop
}
if(valuesFound=false){
//to create the instance of the user-interface environment to use the alert function
var ui=SpreadsheetApp.getui();
ui.alert("No Record Found");}
}
}

Solution
There are different points in your code that could be improved, I attach the updated function correcting the basics:
Indentation: This is easy to fix, just press ctrl + shft + i to correctly indent the code.
Break statement: Use the word break to break a loop.
Wrong comparison operator: The second if has only one = instead of two ==. Check Expressions and operators
Structural logic: Inside the for loop there are two ifs. Only the second one depends on the value of the valuesFound variable, which when modified breaks the loop, so it is not really doing anything.
Readability: It is difficult to understand what the code is doing and so many setValue boxes with no apparent connection is confusing.
In addition, it is difficult to offer help if the behavior of the function is variable and the reason is not known. Apparently, it should always give the same result.
Updated code
function searchRecord() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //declare a variable and set with active Google Sheet
var shUserForm = ss.getSheetByName("User Form"); //declare a variable and set with the User Form reference
var datasheet = ss.getSheetByName("Existing Customers"); //declare variable and set the reference of database sheet
var str = shUserForm.getRange("B2").getValue(); //get data input for search button
var values = datasheet.getDataRange().getValues(); //getting the entire values from the used range and assigning it to values variable
var valuesFound = false; //variable to store boolean value
var ui = SpreadsheetApp.getui();
for (var i = 0; i < values.length; i++) {
var rowValue = values[i]; //declare a variable and storing the value
//checking the first value of the record is equal to search item
if (rowValue[6] == str) {
shUserForm.getRange("B12").setValue(rowValue[0]);
shUserForm.getRange("B15").setValue(rowValue[1]);
shUserForm.getRange("B23").setValue(rowValue[2]);
shUserForm.getRange("B17").setValue(rowValue[3]);
shUserForm.getRange("E17").setValue(rowValue[4]);
shUserForm.getRange("B8").setValue(rowValue[6]);
shUserForm.getRange("E8").setValue(rowValue[7]);
shUserForm.getRange("B19").setValue(rowValue[8]);
shUserForm.getRange("E19").setValue(rowValue[9]);
shUserForm.getRange("B21").setValue(rowValue[10]);
shUserForm.getRange("E21").setValue(rowValue[11]);
shUserForm.getRange("E23").setValue(rowValue[12]);
shUserForm.getRange("E12").setValue(rowValue[13]);
shUserForm.getRange("B25").setValue(rowValue[14]);
shUserForm.getRange("E25").setValue(rowValue[15]);
shUserForm.getRange("B27").setValue(rowValue[16]);
shUserForm.getRange("E27").setValue(rowValue[17]);
shUserForm.getRange("B29").setValue(rowValue[18]);
shUserForm.getRange("E29").setValue(rowValue[19]);
shUserForm.getRange("B31").setValue(rowValue[20]);
shUserForm.getRange("E10").setValue(rowValue[21]);
shUserForm.getRange("B33").setValue(rowValue[22]);
shUserForm.getRange("B35").setValue(rowValue[23]);
shUserForm.getRange("B37").setValue(rowValue[24]);
shUserForm.getRange("E31").setValue(rowValue[25]);
shUserForm.getRange("E35").setValue(rowValue[26]);
shUserForm.getRange("B39").setValue(rowValue[27]);
shUserForm.getRange("E39").setValue(rowValue[27]);
shUserForm.getRange("B41").setValue(rowValue[29]);
shUserForm.getRange("E15").setValue(rowValue[30]);
shUserForm.getRange("B43").setValue(rowValue[31]);
shUserForm.getRange("E43").setValue(rowValue[32]);
shUserForm.getRange("B45").setValue(rowValue[33]);
shUserForm.getRange("E45").setValue(rowValue[34]);
shUserForm.getRange("B47").setValue(rowValue[35]);
shUserForm.getRange("B49").setValue(rowValue[36]);
shUserForm.getRange("B10").setValue(rowValue[37]);
shUserForm.getRange("E37").setValue(rowValue[5]);
valuesFound = true;
break
}
if (valuesFound == false) {
//to create the instance of the user-interface environment to use the alert function
ui.alert("No Record Found");
}
}
}

Related

How Can I Paste A Google Sheets Range with everything except Values?

Trying to copy a range and paste only formulas, formatting, and data validation (no values) at the end of my sheet so I can enter new values in the freshly pasted range. All is working except I can't figure out how to exclude values when pasting.
Here is my code:
function addCue() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// copy the first cue range as template
var copyCueRange = ss.getRange("A10:AA11");
// Find the end of the sheet and set pasteRange to match copyRange
var l = ss.getDataRange().getValues().length;
var pasteCueRange = ss.getRange( "A"+(l+1)+":AA"+(l+2) );
// This pastes all data, How do I paste without values?
copyCueRange.copyTo(pasteCueRange);
};
I am very new to coding and appreciate the help. Thank you.
Data Validation: the Range class has a couple of methods for getting and setting data validation rules, setDataValidations() and getDataValidations().
Formulas: just as data validation, Range also has get/set methods for formulas, setFormulas() and getFormulas().
Formatting: looks like the Range.copyTo() method has some options specified here. Try using the formatOnly additional parameter.
function addCue() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// copy the first cue range as template
var copyCueRange = ss.getRange("A10:AA11");
// Find the end of the sheet and set pasteRange to match copyRange
var l = ss.getDataRange().getValues().length;
var pasteCueRange = ss.getRange( "A"+(l+1)+":AA"+(l+2) );
//Getting data validation and formulas
var dataValidationRules = copyCueRange.getDataValidations();
var formulas = copyCueRange.getFormulas()
//Setting formatting, data validations, and formulas
copyCueRange.copyTo(pasteCueRange, {formatOnly:true});
pasteCueRange.setDataValidations(dataValidationRules);
pasteCueRange.setFormulas(formulas);
}

Getting all l10n values stored in localized bundle

I'm building a FF extension, and I'm processing some xhtml for myself in order to supporn subforms loading, so I have to identify the elements with l10n attributes defined and add them the string value. Because the l10n can't be shared from main code to content scripts (because isn't a simple JSON object), I managed the situation by getting the loaded keys values and defining an "localized array bundle", like this:
lStrings = ["step_title", ........ ];
for (var i = 0; i < lStrings.length; i++) {
bundle[lStrings[i]] = this.locale(lStrings[i]);
}
The thing is, I have to write here every entry in the .properties files... SO, do you know how to access this key values? I already tryed with .toString .toLocalString and inspecting the object, but can't find the way the object to be capable of returning all the key collection.
Do you have a better idea for improvement?
var yourStringBundle = Services.strings.createBundle('chrome://blah#jetpack/content/bootstrap.properties?' + Math.random()); /* Randomize URI to work around bug 719376 */
var props = yourStringBundle.getSimpleEnumeration();
// MDN says getSimpleEnumeration returns nsIPropertyElement // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIStringBundle#getSimpleEnumeration%28%29
while (props.hasMoreElements()) {
var prop = props.getNext();
// doing console.log(prop) says its an XPCWrappedObject but we see QueryInterface (QI), so let's try QI'ing to nsiPropertyElement
var propEl = prop.QueryInterface(Ci.nsIPropertyElement);
// doing console.log(propEl) shows the object has some fields that interest us
var key = propEl.key;
var str = propEl.value;
console.info(key, str); // there you go
}
See comments for learning. Nice quesiton. I learned more about QI from replying.

Fastest way to check Map for duplicate values?

Given a Map, assignment, what is the fastest way to check if it contains any duplicate values in Dart? I am currently using a Set formed from the Map's values and checking its length against the original Map, which works of course, but I'm wondering if there's an especially performant alternative.
Set d = new Set.from(assignment.values);
if (d.length < assignment.length) {
return false; // indicates has duplicates in this context
}
EDIT:
Tried #mezoni's solution modified for my program, but it actually ran a bit slower than my original version. It probably has more to do with constant times than anything else.
List values = new List.from(assignment.values);
Set set = new Set();
for (var i = 0; i < assignment.length; i++) {
if (!set.add(values[i])) {
return false;
}
}
Complexity wise you won't be able to get anything faster. Creating the Set and filling it with the values of the Map is linear in the number of elements. Clearly you have to run through all the values, so you can't do any better than that.
Maybe you could find a solution with a smaller constant factor, but that's not clear. In particular for larger sets I think the Set solution is pretty efficient.
This is more of a algorithms question than a Dart question. In any case, you have to check every value against the others, giving n-1 + n-2 + ... + n-(n-1) checks, or n^2/2. Programmatically, it's easy to create a set, but you could also generate an array, sort the array, and then iterate once to check for duplicates. That finishes in O(n log n).
Fastets way (if you realy need better performance):
void main() {
// Values from map
var values = [1,2,3,2,1,3,2,1];
var length = values.length;
var set = new Set();
var duplicate = false;
// Only for statistics purpose
var statistics = 0;
for(var i = 0; i < length; i++) {
statistics++;
if(!set.add(values[i])) {
duplicate = true;
break;
}
}
print("Duplicate: $duplicate");
print("Performed in ${statistics} iteration(s) from $length possible");
}
Output:
Duplicate: true
Performed in 4 iteration(s) from 8 possible
P.S.
The first example can be recommended to use with List values.
But because Map.values not a List but Iterable then it would be more efficient do not convert them to List but use as is.
Here is modified sample for use with Iterable objects.
It will be work faster because in this algorithm not required convert all values to the List object because it not want using of all elements without exception.
Instead it wants use as less as possible access operation on original source. If the source supports lazy operation of the access to values (as Iterable) this will be even better.
void main() {
// Values from map
var values = [1,2,3,2,1,3,2,1];
var assignment = {};
var length = values.length;
var key = 0;
for(var value in values) {
assignment[key++] = value;
}
var set = new Set();
var duplicate = false;
// Only for statistics purpose
var statistics = 0;
for(var value in assignment.values) {
statistics++;
if(!set.add(value)) {
duplicate = true;
break;
}
}
print("Duplicate: $duplicate");
print("Performed in ${statistics} iteration(s) from $length possible");
}

Entry state showing different values when read directly and when read from a variable

I am using entity framework 4.1 and code first in asp.net mvc. Just to test learn i wrote below code (A controller).
public ActionResult Foo()
{
StringBuilder sb = new StringBuilder();
using (var db = new DemoDataBase1Context())
{
//get person from db
var person = db.Persons.FirstOrDefault();
//get entry
var entry = db.Entry(person);
//now change the person object
person.Name = "Some New Value";
//print entity state
//this is showing unchanged
sb.Append("<br>State: " + entry.State);
//this is showing changed
sb.Append("<br>State: " + db.Entry(person).State);
}
return Content(sb.ToString());
}
In above code you can see, when iam doing entry.State its saying unchanged, if i do db.Entry(person).State its saying changed. Can any one explain why??
If you have auto change detection enabled (which is the default in EF 4.1) Entry calls DetectChanges internally. The method starts probably similar to this:
if (Configuration.AutoDetectChangesEnabled)
ChangeTracker.DetectChanges();
//...
In your second call of db.Entry(person) the object has changed and the DetectChanges method detects this by comparing a snapshot which was made when you loaded the entity with the current values. Since there is a difference the state changes from Unchanged to Modified.
Also the State of the entry object you have created before the change will go to Modified because DbEntityEntry.State is likely a property which just propagates the State value of the inner _internalEntityEntry which remains the same instance in both DbEntityEntry objects.
If you really want to save the former state of an entity you need to save the State itself, not only the entry object:
var state = db.Entry(person).State;
This is just an enum and won't change with a later call to Entry.
You can compare this behaviour with the behaviour when you disable automatic change detection:
db.Configuration.AutoDetectChangesEnabled = false;
Both sb.Append... lines will receive the state Unchanged in that case because EF doesn't notice now anymore that one of your POCO properties has changed because DetectChanges isn't called.
I think the Entry method gives you the state of the object as it is when you call Entry. I don't think it has anything to do with reading it from a variable versus calling it directly.
When you get your reference to the first entry, your object isn't changed. The very next line you change it and call Entry again, at which point it is changed. If you store a reference to that then compare the two I am guessing they are different references:
var person = db.Persons.FirstOrDefault();
// get reference to entry - unchanged at this point
var entry1 = db.Entry(person);
// make a change to the object
person.Name = "Changed";
// get reference to entry - changed now
var entry2 = db.Entry(person);
// these will not be equal: probably false
var equalOrNot = entry1 == entry2;

What exactly does var x:* mean in actionscript?

Its a little tricky to search for 'var:*' because most search engines wont find it.
I'm not clear exactly what var:* means, compared to say var:Object
I thought it would let me set arbitrary properties on an object like :
var x:* = myObject;
x.nonExistantProperty = "123";
but this gives me an error :
Property nonExistantProperty not found on x
What does * mean exactly?
Edit: I fixed the original var:* to the correct var x:*. Lost my internet connection
Expanding on the other answers, declaring something with type asterisk is exactly the same as leaving it untyped.
var x:* = {};
var y = {}; // equivalent
However, the question of whether you are allowed to assign non-existant properties to objects has nothing to do with the type of the reference, and is determined by whether or not the object is an instance of a dynamic class.
For example, since Object is dynamic and String is not:
var o:Object = {};
o.foo = 1; // fine
var a:* = o;
a.bar = 1; // again, fine
var s:String = "";
s.foo = 1; // compile-time error
var b:* = s;
b.bar = 1; // run-time error
Note how you can always assign new properties to the object, regardless of what kind of reference you use. Likewise, you can never assign new properties to the String, but if you use a typed reference then this will be caught by the compiler, and with an untyped reference the compiler doesn't know whether b is dynamic or not, so the error occurs at runtime.
Incidentally, doc reference on type-asterisk can be found here:
http://livedocs.adobe.com/labs/air/1/aslr/specialTypes.html#*
(The markup engine refuses to linkify that, because of the asterisk.)
It's a way of specifying an untyped variable so that you can basically assign any type to it. The code
var x:* = oneTypeObject;
creates the variable x then assigns the oneTypeObject variable to it. You can assign an entirely different type to it as well as follows:
var x:* = anotherTypeObject;
However, you still can't arbitrarily set or access properties; they have to exist in the underlying type (of either oneTypeObject or anotherTypeObject).
Both types may have identically named properties which means you can access or set that property in x without having to concern yourself with the underlying type.
It's the "untyped" type. It just means that the variable can be of any type. Basically the same effect as using this:
var x = myObject;
It means that the type is not specified and can be used with any type. However, you can't set random properties on it. It will behave like whatever type you set it to. The exact syntax is:
var x:*;
As they said before, it's untyped, so it may hold any kind of data. However, you cannot treat it as such in operations. For example, this is valid code:
var untyped:* = functionThatReturnsSomeValue();
But if you go one step farther, you have to watch out or you might get bitten:
var name:String = untyped.name;
Now, if the object returned by that function happens to be an object with a field with an id of "name," you are in the clear. However, unless you know for sure that this is the case, it's better to use typed objects. In this way, the compiler will alert you if you do something that might throw an error at runtime:
(elsewhere)
public class TypedObject()
{
public var name:String = "";
}
(and the code at hand)
var typed:TypedObject = functionThatReturnsTypedObject();
var name:String = typed.name;

Resources