Calculate distances and sort them - php

I wrote a function that can calculate the distance between two addresses using the Google Maps API.
The addresses are obtained from the database. What I want to do is calculate the distance using the function I wrote and sort the places according to the distance. Just like "Locate Store Near You" feature in online stores.
I'm going to specify what I want to do with an example:
So, lets say we have 10 addresses in database. And we have a variable $currentlocation. And I have a function called calcdist(), so that I can calculate the distances between 10 addresses and $currentlocation, and sort them. Here is how I do it:
$query = mysql_query("SELECT name, address FROM table");
while ($write = mysql_fetch_array($query)) {
$distance = array(calcdist($currentlocation, $write["address"]));
sort($distance);
for ($i=0; $i<1; $i++) {
echo "<tr><td><strong>".$distance[$i]." kms</strong></td><td>".$write['name']."</td></tr>";
}
}
But this doesn't work very well. It doesn't sort the numbers.
Another challenge:
How can I do this in an efficient way? Imagine there are infinite numbers of addresses; how can I sort these addresses and page them?

$query = mysql_query("SELECT name, address FROM table");
$rows = array();
while ($row = mysql_fetch_array($query)) {
$row['distance'] = array(calcdist($currentlocation, $row['address']));
$rows[$row['name']] = $row;
}
function cmp_distances($a, $b) {
if($a['distance'] > $b['distance']) return 1;
elseif($a['distance'] < $b['distance']) return -1;
else return 0;
}
// sort distances while preserving key=>value associations
uasort($rows, 'cmp_distances');
// iterate over the sortest list and displaythe entries
foreach($rows as $name => $row) {
echo '<tr><td><strong>'.$row['distance'].' km</strong></td><td>'.$name.'</td></tr>';
}

In your example you calculate the distance to one address at the time:
$distance = array(calcdist($currentlocation, $write["address"]));
And when you do this
sort($distance);
you only have one item in your array. Basically you are printing the values exactly in the same order they are coming from the db, before the distance calculation.
You could:
1) Calculate all the addresses and put them into an array
2) Sort the array
3) Print out the results
About the another challenge you mentioned. This is a bit more tricky and I'm sure there are plenty of options. I would start by thinking how many addresses you really need to compare with each other? Is it really infinite? :)
Is this inside one country or world wide? In your db addresses, you most likely have the postal code. You can use this to narrow the search. Use only the postal codes near by and make the calculations only for those addresses.
But rule of thumb usually is that we worry about the performance too soon. Before it's even a problem.

I think you got some nice answers about your first question.
Concerning the second problem, it depends little bit how your database looks like. Do you store just a string with an address? I assume that you use some geocoding service to convert the address to a (lat,lon) position and then you calculate the distance, is this right?
In case you do something like this, you could start saving the coordinates for each geocoded address in your dataabse. In this way you would geocode the address only once (maybe you later will be willing to update this information now and then, but this is another issue).
Once you have in your table "Address,lat,lon" you can use SQL to narrow down your search imposing some conditions on (lat,lon) or you may even try to make SQL do the whole job for you, defining a new column (in the result set) like for example distance = sqrt((lat-x)^2 + (lon-y)^2)) where (x,y) is the point you start from (the point where the user is) and later return the first N results sorted by distance

Related

PDO using MySql Case for ordering with bound variables

I've been researching this and I think I've found the best solution but I'm not 100% sure. I have a good handle on PDO but this is the first time I've encountered CASE in mysql. This code is working but I'm wondering if it's efficient? If I were to have multiple keys, I would have to write many arrays to be able to search and order. Is there a shorter way to write this code or is this the most efficient? Thanks!
$filters = "
AND (name LIKE :keys
OR note LIKE :keys
OR tagnum = :skeys)
";
$order = "
ORDER BY
CASE
WHEN tagnum = :skeys THEN 0
WHEN name = :skeys THEN 1
WHEN name LIKE :lkeys THEN 2
WHEN name LIKE :rkeys THEN 3
ELSE 4
END
ASC
";
$arr[':keys'] = "%$keys%"; // both wild cards
$arr[':skeys'] = $keys; // stripped keys, no wild cards
$arr[':lkeys'] = "$keys%"; // left key, right wild card
$arr[':rkeys'] = "%$keys"; // right key, left wild card
If you want to have complete control over how you order your resultset, then your solution is perfect. However, if you have multiple keys, then I would definitely consider using fulltext search because it will be faster and lot less comlicated to code. However, it has a different ranking algorithm than you have now.

Compare one by one characters from a mysql db with php

I'm trying to compare in my DB a row with another character by character and give as a result the id which best fits the given data. For example I have on my DB the user David with a AAA sequence and I want to compare it with one I give in which is a ABA so I'd like to receive a percentage (66.6% in this case) of match,
I have done until here but don't know how to go on:
$uname = $_POST['sequence'];
$query = "SELECT name FROM dna WHERE sequence = '$uname'";
$result = mysql_query($query);
while($row = mysql_fetch_array($result))
{
echo $row['name'];
}
In order to get the similarity in percent, you might use the PHP function similar_text().
The two strings are compared and the similarity percentage is returned, if the third parameter is passed to the function.
$string_1 = 'AAA';
$string_2 = 'ABA';
similar_text($string_1, $string_2, $percent);
echo $percent;
// 66.666666666667
The database part is a bit more work. A very basic implementation could look like this.
Keep in mind, that the real problem is, that you compare a string against 1 million rows.
In general: one wouldn't do that, because instead of chars, there a bits. And to compare bits, you would use simply bit-shifts. Anyway...
Here, when working with chars/strings, a rolling row requests or limited query could help, too.
That would mean, that you ask the db for chunks of let's say 500 rows and do the calc work.
It depends on the number of rows and the memory use of the dataset.
// incomming via user input
$string_1 = $_POST['sequence'];
// temporary var to store the highest similarity percentage and it's row_id
$bestValue = array('row_id' => 0, 'similarity' => '0');
// iterate over the "total number of rows" in the database
foreach($rows as $id => $row)
{
// get a new string_2 from db
$string_2 = $row['name'];
// calculate similarity
similar_text($string_1, $string_2, $percent);
// if calculated similarity is higher, then update the "best" value
if($percent > $bestValue['similarity']) {
$bestValue = array('row_id' = $id, 'similiarity' = $percent);
}
}
var_dump($bestValue);
After all db rows are processed, bestValue will containg the highest percentage and it's row id.
You can do all kinds of things here, for instance:
switch from first match update (<) to last match update (<=)
stop iteration on first match
store row_id's, which have the same similarity (multi row match)
if you don't need multi row match, you might drop the array and use two vars for row and percent
proper error handling, escaping, mysqli usage
Be warned: this isn't the most efficient approach, especially not, when working with large datasets. If you need this on a level, which is not hobby or homework, then simply pull a tool, which is optimized for this job, like EMBOSS (http://emboss.sourceforge.net/).

Random dosieid number that is not used

I'm trying to generate a unique "dosieid" number for my web site. My web site is a human resources program solution, in that program users create dosie of their workers in their firm ...random dosieid needs me so when user creating dosie in field dosieid automatically show the dosieid-s that are not used before...the dosieid that don't exist in database. In other case I would use auto increment but in this case dosie is not created yet. And in form dosieid must be option to change the number if random is not fine with a user. One more hint the numbers must bee from 1 to 9999. Can someone help me? I have try many codes but I have not find something like one with this spec.
This is what I have do so far. It gets the random number but I don't know how to compare that random number with database row "dosieid" ?
$id_num = mt_rand(1,9999);
$query = "SELECT dosjeid FROM albums";
$result = mysql_query($query) or die(mysql_error());
while($account = mysql_fetch_array($result)){
if ($id_num == $account['id']){
$id_num = mt_rand(1,9999);
}
}
echo"$id_num<br>";
This is extraordinarily convoluted... why is an auto-incrementing number not enough? This code would also never work properly. If for whatever reason you HAVE to use a random number, then you'd do it like this:
while(true) {
$id_rand = mt_rand(1,9999);
$result = mysql_query("SELECT count(*) FROM albums WHERE dosjeid=$id_rand") or die(mysql_error());
$row = mysql_fetch_row($result);
if ($row[0] == 0) {
break; // our random number isn't in the database, so exit the loop
}
}
However, here's some problems with this:
1) You'll get an infinite loop when you reach 9999 dosie records
2) The more records there are in the database, the longer this loop will take to find a "vacant" slot. As you get closer and closer to 9999 records, you'll be taking a LONG time to find that one empty slot
3) If you're trying to "cloak" the IDs of anyone member so that users can't simply increment an ID parameter somewhere to see other people's records, there's FAR FAR FAR better/easier ways of doing this, such as encrypting the ID value before sending it out to clients.
Use a auto-increment number as your primary key and an additional display id with the UNIQUE attribute as the ID shown to the user. This way you have a unique ID for your internal processing and a display ID that can be easily changed.
This is a terrible design. You should either:
not let users create the dosieid (create it yourself, give it to them after record created)
Try to create a stub record first with an assigned dosieid, and then update it with information
or use UUIDs, which requires a much bigger range than 1-9999
Even if you check that the number was unique, in between the time when you check it and the time you insert the record someone else may have taken it.
And under no circumstances should you find an empty id by picking numbers at random. This makes your program execution time non-deterministic, and if you eventually get 5000 employees you could be waiting a long time.
Also, This range is way too small for a randomness requirement.
You may also want to read about number only hashes (check upon the algorithm's collision rate) - php: number only hash?
function doesIdExists($id)
{
$query = "SELECT dosjeid FROM albums";
$result = mysql_query($query) or die(mysql_error());
while($account = mysql_fetch_array($result))
{
if ($id_num == $account['id'])
return true; /* The id is taken */
}
return false; /* Not taken */
}
$recNotAdded = true;
while($recNotAdded)
{
$rand = mt_rand(1,1000); //Whatever your numbers
$doesExist = doesIdExists($rand);
if(!$doesExist)
{
/* Add to DB */
$recNotAdded = false;
}
}

group mysql results into two separate divs

I have a mysql query that retrieves all my topic results. I then have a pagination system where results are separated into pages and the query's limit #,# changes based on what page you are on.
What I want to do is put all those results into two separate div containers. I want 21 results on each page. The first 9 I will put in one div. The next 12 will go in the other. Does anyone know an efficient way to do this? Should I use two queries, or javascript, or another way? I am just looking for the best most efficient way to do this. Unfortunately the pagination system makes two queries difficult. Any suggestions highly appreciated.
$sql = "SELECT * FROM topics LIMIT ?,?";
$stmt2 = $conn->prepare($sql);
$result=$stmt2->execute(array(somenumber,somenumber2));
I don't see any reason why you can't do a single MySQL query and use JavaScript to sort the results. Understand that I don't understand here what your data is coming back looking like, so any example I provide will have to remain pretty agnostic in this regard.
I will, however, assert as an assumption that you have a JavaScript array of length 21 with some data that is the basis for your display.
Assuming that we're just talking about the first 9, and the last 12, the sorting code is as simple as:
// assume my_array is the array mentioned above
for (var i = 0; i < 9; i += 1) {
var html = code_to_transform_data_from_array(array[i]);
$('.div1').append($(html));
}
for (var i = 9; i < 21; i += 1) {
var html = code_to_transform_data_from_array_b(array[i]);
$('.div2').append($(html));
}
If your sorting condition is any more complicated, then you'd be better off with something like...
while (my_array.length > 0) {
var item = my_array.pop();
if (sorting_condition) {
$('.div1').append(f1(item));
}
else {
$('.div2').append(f2(item));
}
}
(In the second example, I became a lazy typist and assumed f1 and f2 to be complete transformation functions. sorting_condition is your criteria for determining in which bucket something goes.
Hope that sets you off on the right track.

Numbers and calculations in php/sql

ok im new on the scene with php and sql, i read the basics and was able to produce a DB driven shoutbox.
This is my website http://www.teamdelta.byethost12.com/
What i want to do next im not sure what i should be using java? php? sql? i have no idea...
but i assume php and sql if sql can do what i think it can..
What i want to do next is to A extract data from data at certain positions for example
data in the format "DAB:CD:EF:GH" eg "D03:57:16:23" to be turned into AB, DF, CE. Eg "3","76","51".
then i want to store these Numbers (Not VARCHARS) in the database.
that is part 1.
the sceond part that i want to make sure is possible before i put to much effort in is to do calculations on all the entries in the database with respect to a 3rd peice of data and display all the entries in a decending ordered list ordered by the output of the calculation..
i think calculations can be added to querys...but as i said im new and the tutorial i read only coved reading values out of the db..
and just if it helps to clarify what im trying to do even though this is not part of the question... here is what im trying to do
* set up a database and entry system that records the player name, base name, location,econ, and comment and stored this as a entry in the database..(i have done this)
*on entry i wish to extract numeric values(AB,DF,CE) from a text string(location) (dont know how)
*then i wish to display the list(i have done this)
*a new column should be added on display containing the resuly of a calculation involving the numberic values from each entry with a global value that is entered on page(dont know how)
*then the list should be sorted in decending order of the output of the calculation.
*lastly i wish to be able to remove items from the list with a click.
thats all :) ,, seeking part advice and guidence
here is the page its php but i renamed it as text so its readble.
cant past is as code as it has escape chars in it
http://www.teamdelta.byethost12.com/trade/postroute3.txt
currently trying to use
$values = array();
$string = $location;
$values[0]= $string[1].$string[2];
$values[1]= $string[5].$string[8];
$values[2]= $string[4].$string[7];
$x = intval($values[1], 10);
$y = intval($values[2], 10);
$g = intval($values[0], 10);
print_r($x);
echo(' : '); print_r($y); echo(' : ');
print_r($g);
ok.. for the first part.
$values = array();
$values = split(":", 'DAB:CD:EF:GH');
$int_values = array();
foreach($values as $v) {
$int_values[] = intval($v, 10);
}
// values in base 10
print_r($int_values);

Categories