Making custom sort in google script faster - google-apps-script

I'm trying to organize a database by a rank in a certain order that I want. Normally, sort would work, but I want a couple of things to happen:
1. raw data so that filters can work for other data (i.e. by name, team, etc)
2. I don't want to always sort by rank
Ranks in order:
Captain, Officer, Chef, Crew, Recruit, Lost, Brig
Right now I'm forcing a filtered sort first alphabetically only when the rank filter is sorted, then when that is done, it sorts the list according to the above sort. The problem that I'm having is that it takes some time to execute the script. I'm thinking that this can be done much more efficiently, but I'm way rusty when it comes to this stuff. Here's my code below:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var members = ss.getSheetByName("Members");
var temp = ss.getSheetByName("sortRank");
var ranks = ["Captain", "Officer", "Chef", "Crew", "Recruit", "Lost", "Brig"];
var rankData = members.getRange(1,5,members.getLastRow(),1);
var search = null;
var query = null;
var x = null;
if(order != null) {
ranks = ranks.reverse();
}
for (var i=0; i < 7; i++) {
search = rankData.createTextFinder(ranks[i]);
x = search.findAll().length;
if(x != 0) {
query = search.findNext().getRow();
members.getRange(query,1,x,37).copyTo(temp.getRange(temp.getLastRow()+1,1));
}
}
}

Sort members by rank:
Assumes rank is in column 5 of members.
var members = ss.getSheetByName("Members");
var rankData = members.getRange(1,5,members.getLastRow(),1).getValues();
var ranks = ["Captain", "Officer", "Chef", "Crew", "Recruit", "Lost", "Brig"];
var rVal={};
ranks.forEach(function(e,i){rVal[e]=i+1;});
members.getDataRange().setValues(members.getDataRange().getValues().sort(function(a,b){return rVal[a[4]]-rVal[b[4]];}));

Related

Calculation in script and send result to a cell of active row

The "stock/average" sheet calculates the average purchase price. F is Products and H is the purchase price.
I need to calculate and save profit/loss of each sale in the E column after every sale. Like: the purchase price of the first product is 14200 (First image # stock/average, column H, second product) so 2800 should be put here. I can do it using vlookup or query but the purchase price gets changed with each purchase so I need to do it using the script and preserve the result.
Link to Spreadsheet:
Please help.
The code in this answer is presented in two parts:
Section One - Add this to your IF statement.
var thissheet = range.getSheet();
var edditedCol = range.columnStart;
var edittedRow = range.rowStart;
var productdetails = sheet.getRange(edittedRow,1,1,5).getValues();
var product = productdetails[0][0];
var qty = productdetails[0][1];
var sellprice = productdetails[0][2];
// Logger.log("DEBUG: product = "+product+", qty:"+qty+", sellprice="+sellprice);
var avg = getcost(product);
var net = +sellprice-(+avg*qty);
var netprofit = Math.round(net * 100) / 100
sheet.getRange(edittedRow,5).setValue(netprofit);
// Logger.log("DEBUG: net profit = "+netprofit);
This identifies the edited row and column, and get the values for the appropriate
- Product
- Qty
- Total sales value
- calls a separate function to return the average cost for the relevant product,
- calculates the net profit,
- updates the value in the "Profit" Column on the sheet.
Section Two - a sub-routine gets the average cost from the "stock/average" Sheet.
function getcost(product){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "stock/average";
var sheet = ss.getSheetByName(sheetname);
var Fvals = sheet.getRange("F5:F").getValues();
var Flast = Fvals.filter(String).length;
var sarange = sheet.getRange(5,6,Flast,7);
// Logger.log("DEBUG: sarange = "+sarange.getA1Notation());
var data = sarange.getValues();
// convert the 2D array to a 1D array for the "Product" column.
var colA = new Array()
for(i=0;i<data.length;++i){
colA.push(data[i][0]) ;// taking index 0 means I'll get column A of each row and put it in the new array
}
// find the posityion of the editted product on the "stack/average" sheet
var pos = colA.indexOf(product);
// get the average cost for this line.
var avgcost = data[pos][2];
// Logger.log("DEBUG: the average cost = "+avgcost);
return avgcost;
}

Want to have a select list for a form pull from a sheet

I am trying to pre-populate a drop down list in a Google Form from a list generated by a range in a Google Sheet. I can pull the list into an array, but can't seem to create the choice in the form.
//Define sheet location (where vendor list range lives) and form location
var se = SpreadsheetApp.openById("sheet_ID_that_works");
var vendorList = se.getSheetByName("vendorList");
var vendorPullIn = sub.getRange("vendorListRange");
var form = FormApp.openById('form_ID_that_works');
var item = form.addListItem();
item.setTitle('Select Vendor')
var choiceList = vendorPullIn.getValues();
item.setChoiceValues(choiceList);
choices need to be in a single array...
.getValues() returns a 2d array...
one way to flatten values out...
var choiceList = [];
var values = vendorPullIn.getValues();
for ( var row in values )
for ( var col in values[row] )
if ( values[row][col] )
choiceList.push( values[row][col] );

If, else statement to override a null in webmatrix

I thought this would be the proper code to replace a null but it gives me an error "The name 'subt2' does not exist in the current context"
var SQLSELECT2 = "SELECT SUM(Price) FROM ProductEntered WHERE TransID=#0";
var sub2 = db.QueryValue(SQLSELECT2, transaction_id);
if(sub2 == null){
var subt2 = 0.00;
}
else{
var subt2 = sub2;
}
and in the page I have #subt2
I decided to try to explain what I am doing to hopefully get a better response. I have a page that you can add products to. It makes an entry into the table with the TransID as a reference to this page. This page pulls the SUM from Price where rows have this TransID. The problem lies when there is not a product entered in the table with this TransID. I need it to output a 0.00 when there is not data to be displayed.
took me forever but I found a few articles and put them together. This is the code:
var SQLSELECT2 = "SELECT SUM(Price) FROM ProductEntered WHERE TransID=#0";
var sub2 = db.QueryValue(SQLSELECT2, transaction_id);
if(String.IsNullOrEmpty(Convert.ToString(sub2)))
{
sub2 = 0.00;
}
I would try to replace it as follows:
var sub2 = db.QueryValue(SQLSELECT2, transaction_id);
var result = sub2 == null ? 0 : sub2;
return result;
When you define a variable by using a var (or an int, double, string, etc) it only exists in within the braces {} where it is defined. So since you are defining it inside the braces, it's not available later in the page. Better would be:
var SQLSELECT2 = "SELECT SUM(Price) FROM ProductEntered WHERE TransID=#0";
var sub2 = db.QueryValue(SQLSELECT2, transaction_id);
var subt2 = 0;
if(sub2 == System.DBNull){
subt2 = 0.00;
}
else
{
subt2 = sub2;
}

Entity Framework multiple where conditions, looking for correctness or better way

I am attempting to load customer objects from my database where the customer's name can contain a specific string as well as any associated addresses having particular values in particular fields. I have done a good bit of research, etc. to put this together. Is this the proper way to construct such a query and if not, what is the "more correct" approach or more efficient approach? Thanks for your time.
I own LINQPad and this query was crafted using it.
var customerName = "J";
var street = "Rd";
var city = "asdf";
var state = "TN";
var zip = "27613";
var query = (
from c in customers
.Include("locations")
.Include("locations.address")
where
c.name.Contains(customerName) ||
c.locations.Any(l => l.address.street1.Contains(street)) ||
c.locations.Any(l => l.address.street2.Contains(street)) ||
c.locations.Any(l => l.address.city.Contains(city)) ||
c.locations.Any(l => l.address.state.Contains(state)) ||
c.locations.Any(l => l.address.zip.Contains(zip))
select new
{
c.id,
c.name,
c.locationId,
c.location,
Locations =
from l in c.locations
where
l.address.street1.Contains(street) ||
l.address.street2.Contains(street) ||
l.address.city.Contains(city) ||
l.address.state.Contains(state) ||
l.address.zip.Contains(zip)
select new
{
l.id,
l.address,
l.description,
l.locationType
}
}
);
query.Dump();
This does give me the desired results, but I want to make sure I am doing this the best way that is the most polite to the database.
I'm sure this is a pretty typical pattern of searching using the Entity Framework.
Rather than write this big predicate twice, I'd flip it around. Also, the Includes are totally unnecessary when you're projecting.
var query =
from l in c.locations
where
l.customer.name.Contains(customerName) ||
l.address.street1.Contains(street) ||
l.address.street2.Contains(street) ||
l.address.city.Contains(city) ||
l.address.state.Contains(state) ||
l.address.zip.Contains(zip)
group l by l.customer into g
select new
{
id = g.Key.id,
name = g.Key.name,
// etc.
Locations = from l2 in g
select new
{
l2.id,
l2.address,
l2.description,
l2.locationType
}
};

Help to build LINQ query

I have SQL database as follows
alt text http://img97.imageshack.us/img97/5774/dbimage.jpg
Now I want to filter the restaurant_detail table for the parameters:
1. cuisine 2. area
Can you help me to build LINQ query?
I presume you have a model generated either with LINQ to SQL or Entity Framework. Also, I'm assuming foreign key relationships have been set.
var details = db
.Cuisines
.Where(c => c.Cuisine=="something")
.SelectMany(c => c.RestaurantCuisines)
.Select(rc => rc.Restaurant.RestaurantDetails)
.Where(rd => rd.Area=="something")
;
Done with the linq query using following lines of code :
c = from q in dc.restaurant_cuisines
where q.cuisine.cuisine1.Contains(cuisine)
&& q.restaurant.price.ToString().Length == price.Length
select new NearBy { NearById = q.restaurant.id, NearByLongitude = (double)q.restaurant.longitude, NearByLatitude = (double)q.restaurant.latitude };
}
int[] ids = new int[c.Count()];
var lon = from q1 in dc.area_maps where q1.area.ToLower() == area.ToLower() select q1.longtitude;
var lat = from q1 in dc.area_maps where q1.area.ToLower() == area.ToLower() select q1.latitude;
foreach(NearBy n in c)
{
result = calcDistNew((double)lat.FirstOrDefault(), (double)lon.FirstOrDefault(), n.NearByLatitude, n.NearByLongitude);
ids[i++] = n.NearById;
}
var r = from q in dc.restaurant_details
where 1 == 1 &&
(ids).Contains(q.restaurant_id)
select new Restaurant
{
Restora_id = q.restaurant_id.ToString(),
Name = q.restaurant.name,
Foodtype = q.restaurant.foodtype.foodtype1,
Avg_rating = q.restaurant.avg_rating.ToString(),
Featured = q.restaurant.featured.ToString(),
CuisineList = getCuisine(q.restaurant_id),
Restora_type = q.type,
Distance = Math.Round(calcDistNew((double)lat.FirstOrDefault(), (double)lon.FirstOrDefault(), (double)q.restaurant.latitude, (double)q.restaurant.longitude), 2),
Newarrival = q.restaurant.newarrival.ToString(),
CountRecord = ids.Length.ToString()
};
var d = r.AsEnumerable().OrderBy(t => t.Distance);
var g = d.Take(recordSize + 10).Skip(recordSize);
return g.ToList();
Please note that above displayed code generated with some changes from the initial requirements.