How to shorten multiple If with ascending index of named ranges - google-sheets

I have a Google Spreadsheets formula like this with two named range: RangeA and RangeB.
=(1+VLOOKUP($A2,Test!RangeA,2,0)) * VLOOKUP($A2,Test!RangeA,3,0)
+ if(B2>=11,(index(Test!RangeB,1,2) - 1) * ((1+vlookup($A2,Test!RangeA,2,0))^(index(Test!RangeB,1,1)-1)) * vlookup($A2,Test!RangeA,3,0),0)
+ if(B2>=21,(index(Test!RangeB,2,2) - 1) * ((1+vlookup($A2,Test!RangeA,2,0))^(index(Test!RangeB,2,1)-1)) * vlookup($A2,Test!RangeA,3,0),0)
+ if(B2>=41,(index(Test!RangeB,3,2) - 1) * ((1+vlookup($A2,Test!RangeA,2,0))^(index(Test!RangeB,3,1)-1)) * vlookup($A2,Test!RangeA,3,0),0)
+ if(B2>=61,(index(Test!RangeB,4,2) - 1) * ((1+vlookup($A2,Test!RangeA,2,0))^(index(Test!RangeB,4,1)-1)) * vlookup($A2,Test!RangeA,3,0),0)
+ if(B2>=81,(index(Test!RangeB,5,2) - 1) * ((1+vlookup($A2,Test!RangeA,2,0))^(index(Test!RangeB,5,1)-1)) * vlookup($A2,Test!RangeA,3,0),0)
https://docs.google.com/spreadsheets/d/1_4Xc8PMXjUVuI2SXY3QgkqrYQn3xc922bYJjH0KHX2Q/edit?usp=sharing
The problem is: it contains many long if (it is much longer than the example above) which I think could be shortened since it increases the index 1 row per time. Please help.

Replace the INDEX with:
vlookup(row(indirect("1:"&match(B2,index(Test!RangeB,0,1)))),{row(Test!RangeB)-min(row(Test!RangeB))+1,Test!RangeB},3,false)
Keep the first IF and get rid of the rest:
=(1+VLOOKUP($A2,Test!RangeA,2,0)) * VLOOKUP($A2,Test!RangeA,3,0)
+ if(B2>=11,sumproduct((vlookup(row(indirect("1:"&match(B2,index(Test!RangeB,0,1)))),{row(Test!RangeB)-min(row(Test!RangeB))+1,Test!RangeB},3,false) - 1) * ((1+vlookup($A2,Test!RangeA,2,0))^(vlookup(row(indirect("1:"&match(B2,index(Test!RangeB,0,1)))),{row(Test!RangeB)-min(row(Test!RangeB))+1,Test!RangeB},2,false)-1)) * vlookup($A2,Test!RangeA,3,0)),0)

Related

Convert this array formula for google spreadsheet

I found out that you can't use an array formula if you are using the "if" "and" "or" formula on google spreadsheet. Can someone please help me fix this formula I made, if possible I still want it to be an array formula
=ArrayFormula((IF(AND(H7:H="NSW",K7:K>=59,M7:M="Yes",OR(N7:N="Yes",O7:O="Yes"),OR(P7:P="Yes",P7:P="Unsure"),OR(Q7:Q="Yes",Q7:Q="Unsure"),S7:S="Yes",OR(T7:T="Yes",T7:T="Unsure"),OR(U7:U="No",U7:U="Unsure"),OR(V7:V="No",V7:V="Unsure"),OR(W7:W="No",W7:W="Unsure"),OR(X7:X="No",X7:X="Unsure"),OR(Y7:Y="No",Y7:Y="Unsure"),OR(Z7:Z="No",Z7:Z="Unsure")), "Passed", "Failed")))
Here is also the link to the spreadsheet: https://docs.google.com/spreadsheets/d/1F6P7oynTDzckDFMd279ON1udFYWSfo3gUX1Kw2RjDbE/edit?usp=sharing
If you remove the word arrayformula, it work just perfectly fine but I'm using this formula for google sheet responses so I would really need it to be an array formula
You haven't provided a link to the spreadsheet, so what I'll share below is constructed by eye and is not tested. But you can try this:
=ArrayFormula( IF( (H7:H="NSW") * (K7:K>=59) * (M7:M="Yes") * ((N7:N="Yes") + (O7:O="Yes")) * ((P7:P="Yes") + (P7:P="Unsure")) * ((Q7:Q="Yes") + (Q7:Q="Unsure")) * (S7:S="Yes") * ((T7:T="Yes") + (T7:T="Unsure")) * ((U7:U="No") + (U7:U="Unsure")) * ((V7:V="No") + (V7:V="Unsure")) * ((W7:W="No") + (W7:W="Unsure")) * ((X7:X="No") + (X7:X="Unsure")) * ((Y7:Y="No") + (Y7:Y="Unsure")) * ((Z7:Z="No") + (Z7:Z="Unsure")), "Passed", "Failed") )
In array formulas, the equivalent of AND is *; and the equivalent of OR is +.
ADDENDUM (after additional comment and sharing of link to spreadsheet)
Erase everything from Column AB (including the header) and place this in cell AB1:
=ArrayFormula( IF(A:A="",, IF(ROW(A:A)=1, "Eligibility", IF( (H:H="NSW") * (K:K>=59) * (M:M="Yes") * ((N:N="Yes") + (O:O="Yes")) * ((P:P="Yes") + (P:P="Unsure")) * ((Q:Q="Yes") + (Q:Q="Unsure")) * (S:S="Yes") * ((T:T="Yes") + (T:T="Unsure")) * ((U:U="No") + (U:U="Unsure")) * ((V:V="No") + (V:V="Unsure")) * ((W:W="No") + (W:W="Unsure")) * ((X:X="No") + (X:X="Unsure")) * ((Y:Y="No") + (Y:Y="Unsure")) * ((Z:Z="No") + (Z:Z="Unsure")), "Passed", "Failed") ) ) )
This will produce the header and all results. Blank rows will be left blank. Current results are checked and correct.
It's best to use a whole column approach (e.g., A:A in the top cell instead of A2:A in the second cell) since the sheet that contains the formula is using a QUERY to bring in that information and in case you ever decide to use sorting.

How to combine postgres group by count and where

Using Rails 4.2 and Postgres I've created the following query to give me a uniq list of bird ids in the Sightings table ordered by the number of sightings for each bird. Also note I'm using the Kaminari gem for pagination.
Sighting.select("bird_id, COUNT(sightings.id) as sightings_count").group(:bird_id).order('sightings_count DESC').page(1)
This works great returning the ActiveRecordRelation intended. The problem arises when i try to combine it with the geocoder gems .near method
Sighting.near([-31.0, 151.0], 1000, units: :km).select("bird_id, COUNT(sightings.id) as sightings_count").group(:bird_id).order('sightings_count DESC').page(1)
This generates the query and error
SELECT sightings.*, 6371.0 * 2 * ASIN(SQRT(POWER(SIN((-31.0 - sightings.lat) * PI() / 180 / 2), 2) + COS(-31.0 * PI() / 180) * COS(sightings.lat * PI() / 180) * POWER(SIN((151.0 - sightings.lng) * PI() / 180 / 2), 2))) AS distance, MOD(CAST((ATAN2( ((sightings.lng - 151.0) / 57.2957795), ((sightings.lat - -31.0) / 57.2957795)) * 57.2957795) + 360 AS decimal), 360) AS bearing, bird_id, COUNT(sightings.id) as sightings_count FROM "sightings" WHERE (sightings.lat BETWEEN -39.993216059187304 AND -22.006783940812696 AND sightings.lng BETWEEN 140.50821379697885 AND 161.49178620302115 AND (6371.0 * 2 * ASIN(SQRT(POWER(SIN((-31.0 - sightings.lat) * PI() / 180 / 2), 2) + COS(-31.0 * PI() / 180) * COS(sightings.lat * PI() / 180) * POWER(SIN((151.0 - sightings.lng) * PI() / 180 / 2), 2)))) BETWEEN 0.0 AND 1000) GROUP BY bird_id ORDER BY distance ASC, sightings_count DESC LIMIT 25 OFFSET 0
PG::GroupingError: ERROR: column "sightings.id" must appear in the GROUP BY clause or be used in an aggregate function
Adding id to the group by means the bird counts aren't correct and as far as I understood COUNT within select was an aggregate function which does include sightings.id.
How can I successfully combine the two?
Note: I did try the following but this returns a Hash rather than AR Relation.
Sighting.near([#lat, #lng], #range, units: :km, order:nil).group(:bird_id).order('count_id DESC').page(#page).count(:id)
Thanks for any help!!
Creating a custom near scope was the easiest work around as I wasn't using the bearing or distance attributes that get added to each record and hence the whole select sql generated by near I didn't need. Instead of select(options[:select]) I replaced it with the select I wanted.
scope :birds_by_sighting, lambda{ |location, *args|
latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
if Geocoder::Calculations.coordinates_present?(latitude, longitude)
options = near_scope_options(latitude, longitude, *args)
select("bird_id, COUNT(sightings.id) as sightings_count").group(:bird_id).where(options[:conditions]).order('sightings_count DESC')
else
# If no lat/lon given we don't want any results, but we still
# need distance and bearing columns so you can add, for example:
# .order("distance")
select(select_clause(nil, null_value, null_value)).where(false_condition)
end
}

Ruby Geocoder nearbys is slow

Using Rails 3.2, Ruby 1.9, geocoder gem. The following query I have in my controller:
# 1500+ms to load
nearby_shops = (current_shop.nearbys(10, :order => 'overall_rating DESC')
.where(:shop_type => shop_type).includes(:photos, :active_property_list =>
:hotel_image_lists).limit(5))
# SQL
Shop Load (4045.6ms) SELECT shops.*, 3958.755864232 * 2 * ASIN(SQRT(POWER(
SIN((36.111927 - shops.lat) * PI() / 180 / 2), 2) + COS(36.111927 * PI()
/ 180) * COS(shops.lat * PI() / 180) * POWER(SIN((-115.171229 - shops.lng)
* PI() / 180 / 2), 2))) AS distance, CAST(DEGREES(ATAN2( RADIANS(shops.lng
- -115.171229), RADIANS(shops.lat - 36.111927))) + 360 AS decimal) % 360 AS
bearing FROM `shops` WHERE `shops`.`shop_type` = 'food' AND (shops.lat BETWEEN
35.96719521688915 AND 36.25665878311085 AND shops.lng BETWEEN
-115.3503819353204 AND -114.9920760646796 AND 3958.755864232 * 2 *
ASIN(SQRT(POWER(SIN((36.111927 - shops.lat) * PI() / 180 / 2), 2) +
COS(36.111927 * PI() / 180) * COS(shops.lat * PI() / 180) * POWER(SIN((
-115.171229 - shops.lng) * PI() / 180 / 2), 2))) <= 10 AND shops.id != 85155)
ORDER BY overall_rating DESC LIMIT 5
- shops.lat) * PI() / 180 / 2), 2) + COS(48.8582411618 * PI() / 180) *
COS(shops.lat * PI() / 180) * POWER(SIN((2.2945044899 - shops.lng) * PI() /
180 / 2), 2))) <= 100 AND shops.id != 517) ORDER BY distance ASC LIMIT 25
OFFSET 0
The problem lies in the nearbys which does calculation on the longitude and latitude. I have already added indexes to the longitude and latitude columns, but it doesn't improve anything.
How can I improve this?
P/S: I removed unrelated conditions which doesn't contribute to the speed of the query.
This might not be the most exact or elegant solution, but why not just fudge the math a little. You could write a select clause that did something like this:
.select(["id, name, 69.0975851*sqrt(POWER(shops.lat-?,2) + COS(?*PI()/180)*POWER(shops.lon-?,2)) AS DISTANCE", loc.lat, loc.lat, loc.lon])
You don't really need to use that hairy great circle formula if you're just dealing with small (< 500 mile) distances. You'll end up getting the same ~almost~ answers for a fraction of the computational cost.

What explicit type casts should I add to make a no operator method work

I'm trying to use the near method of the Ruby geocoder gem in a Rails application with a postgres database.
#users = Contact.near(params[:search], params[:distance])
That line produces the 'operator does not exist error' you see below. The search parameter will be an address and the dist
In the Contact.rb model I do this
geocoded_by :full_address
def full_address
self.address + self.city + self.province + self.postal
end
Can you tell me what it is that I might be doing wrong? The error says, 'You might need to add explicit type casts.'
Address, city, province and postal are all strings.
PG::Error: ERROR: operator does not exist: numeric - character varying LINE 1: ...8.755864232 * 2 * ASIN(SQRT(POWER(SIN((43.7058645 - contacts... ^ HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. : SELECT contacts.*, 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((43.7058645 - contacts.latitude) * PI() / 180 / 2), 2) + COS(43.7058645 * PI() / 180) * COS(contacts.latitude * PI() / 180) * POWER(SIN((-79.3687137 - contacts.longitude) * PI() / 180 / 2), 2))) AS distance, CAST(DEGREES(ATAN2( RADIANS(contacts.longitude - -79.3687137), RADIANS(contacts.latitude - 43.7058645))) + 360 AS decimal) % 360 AS bearing FROM "contacts" WHERE (contacts.latitude BETWEEN 43.676918143377826 AND 43.73481085662217 AND contacts.longitude BETWEEN -79.40875589075141 AND -79.3286715092486 AND 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((43.7058645 - contacts.latitude) * PI() / 180 / 2), 2) + COS(43.7058645 * PI() / 180) * COS(contacts.latitude * PI() / 180) * POWER(SIN((-79.3687137 - contacts.longitude) * PI() / 180 / 2), 2))) <= '2') ORDER BY distance ASC LIMIT 1

print multicolumn formatted text vb6

i need to print formatted text like in the image below, how can i achive this in vb6, given that vb6 print object is not friendly for such this
The data i need to print that represented by the boxes are non related
It is not very difficult. You use the ScaleLeft, ScaleWidth, CurrentX, and CurrentY properties to set where printing begins on the page. In this case you will probably also want to set the Orientation property to vbPROPortrait. Using those positioning properties, and setting the font and style you want you then call Printer.Print
This method will draw 4 boxes onto a page. Play with the (x, y) coordinates or hard code the numbers to alter the sizes. Remove the .EndDoc statement if you don't want the printer to print the page from this method and call Printer.EndDoc from somewhere else. Full Printer object documentation for VB6 can be found at http://msdn.microsoft.com/en-us/library/aa443915%28v=vs.60%29.aspx
Private Sub DrawBox()
With Printer
.ScaleMode = vbTwips
lngScaleWidth = .ScaleWidth
lngScaleHeight = .ScaleHeight
Printer.Line (.ScaleLeft + lngMargin, .ScaleTop + lngMargin)-(lngScaleWidth / 2 - (100 + lngMargin * 2), lngScaleHeight / 2 - (100 + lngMargin * 2)), lngColor, B
Printer.Line (lngScaleWidth / 2 + (100 + lngMargin * 2), .ScaleTop + lngMargin)-(.ScaleWidth - lngMargin, lngScaleHeight / 2 - (100 + lngMargin * 2)), lngColor, B
Printer.Line (.ScaleLeft + lngMargin, lngScaleHeight / 2 + (100 + lngMargin * 2))-(lngScaleWidth / 2 - (100 + lngMargin * 2), .ScaleHeight - lngMargin), lngColor, B
Printer.Line (lngScaleWidth / 2 + (100 + lngMargin * 2), lngScaleHeight / 2 + (100 + lngMargin * 2))-(.ScaleWidth - lngMargin, .ScaleHeight - lngMargin), lngColor, B
.EndDoc
End With
End Sub
The sample code below demonstrates some of the positioning and other properties.
Dim lMargin as Integer
lMargin = 200
With Printer
.FontBold = True
.FontItalic = False
.CurrentY = .CurrentY + (3 * .TextHeight(App.ProductName))
.CurrentX = lLeftMargin
.FontName = "Arial"
.FontSize = 11
Printer.Print "Date " & strTransDate
End With

Resources