I have an array of users, as follows;
<?php
$users = array(
array(
"id" => 1,
"last_updated" => 1398933140,
"weight" => 2.0
),
array(
"id" => 2,
"last_updated" => 1398933130,
"weight" => 0
),
array(
"id" => 3,
"last_updated" => 1398933120,
"weight" => 1.0
),
array(
"id" => 4,
"last_updated" => 1398933110,
"weight" => 0
)
);
?>
I want to (asynchronously) refresh some stats on users (for which I'm using a crobjob) ordered on last_updated, in essence the user with the most stale stats.
However, I want to add weights to users and calculate that into the equation. Is it good enough to just convert the weight to some amount of seconds and substract that from the last_updated timestamp?
I know my example array has time differences of 10 seconds, but I only want to start adding the weight criteria after 3600 seconds.
// Start substracting weight
if ($timediff > 3600) {
// The longer the timediff, the heavier the weight becomes
$weight_severity = (($timediff/1000) * $weight) * SOME_CONSTANT;
// Putting the 'last_updated' back farther in the past
$substract = $timestamp - $weight_severity;
}
Is this a good 'algorithm' or will this go horribly wrong when the differences become pretty large?
At the moment I have nearly 2000 users (expected will become 10.000), so theoretically a full loop takes 2000 minutes. My concern is, will a user with a weight of 2.0 be buried under 500 'insignificant' users?
Update: I have enhanced my code a bit.
<?php
$users = array(
array(
"id" => 1,
"last_updated" => 1399281955,
"weight" => 2.0
),
array(
"id" => 2,
"last_updated" => 1399281955 - 15000,
"weight" => 0
),
array(
"id" => 3,
"last_updated" => 1399281955 - 30000,
"weight" => 1.0
),
array(
"id" => 4,
"last_updated" => 1399281955 - 45000,
"weight" => 0
)
);
$results = array();
foreach ($users as $index => $user) {
$factor = 3;
$timestamp = $user['last_updated'];
$substract = $timestamp;
// Start substracting weight
$timediff = time() - $timestamp;
if ($timediff > 3600) {
// The longer the timediff, the heavier the weight becomes
$weight_severity = pow((($timediff/1000) * $user['weight']), $factor);
// Putting the 'last_updated' back farther in the past
$substract = $timestamp - $weight_severity;
}
$users[$index]['weight_updated'] = floor($substract);
$users[$index]['timediff'] = $timediff;
$users[$index]['diff'] = $users[$index]['last_updated'] -
$users[$index]['weight_updated'];
}
echo '<pre>';
print_r($users);
usort($users, function($a, $b) {
return (($a['weight_updated'] == $b['weight_updated'])) ?
0 : ($a['weight_updated'] < $b['weight_updated']) ? -1 : 1;
});
print_r($users);
So without weights, the user IDs would be: 4,3,2,1. But with my 'algorithm' it's now 3,4,2,1. User ID 3 because of it's weight, is getting done before 4. This is with a time difference of 15000 seconds (a little over 4 hours).
Related
I need to do a query and get certain kind of data. I have 2 tables, users and connections, I need to get per user how many times he/she connected per month and year.
users connections
........... ................
john 10/02/2014
john 15/02/2014
john 03/01/2015
john 06/02/2015
Is there a chance to get this info in this format:
john=>
[0]=>2014
[0]=>02
'total' =>2
[1]=>2015
[0]=>01
'total' => 1
[1]=>02
'total' => 2
[2]=>03
'total'=> 1
I'm using Codeigniter and also PHP.
Answering to #CodeGodie what I've done so far is:
public function getPeriodicity(){
$this->db->select('u.vusr_user, extract (MONTH from (to_timestamp(c.vuc_log_in))) as month, extract (YEAR from (to_timestamp(c.vuc_log_in))) as yearly, COUNT(c.vuc_log_in)');
$this->db->from('vts_users_conn c');
$this->db->join('vts_users u', 'c.vuc_vusr_id = u.vusr_id');
$this->db->group_by('u.vusr_user, month, yearly','asc');
$query = $this->db->get();
return $query->result_array();
}
Assuming you are using Codeigniter's $this->db->result_array() to obtain your database results, your initial array will look like this:
$res = array(
array(
"name" => "john",
"date" => "10/02/2014"
),
array(
"name" => "john",
"date" => "15/02/2014"
),
array(
"name" => "john",
"date" => "03/01/2015"
),
array(
"name" => "john",
"date" => "06/02/2015"
),
array(
"name" => "john",
"date" => "06/03/2015"
)
);
In order to change this array to your desired output, I would do the following:
foreach ($res as $row) {
$date_arr = explode("/", $row['date']);
$n = $row['name'];
$y = $date_arr[2];
$m = $date_arr[1];
if (!isset($final[$n]))
$final[$n] = array();
if (!isset($final[$n][$y]))
$final[$n][$y] = array();
if (!isset($final[$n][$y][$m])) {
$final[$n][$y][$m] = array("total" => 1);
} else {
$final[$n][$y][$m]["total"] = $final[$n][$y][$m]["total"] + 1;
}
}
If you var_dump your final result (var_dump($final)), you will get the following:
array (size=1)
'john' =>
array (size=2)
2014 =>
array (size=1)
'02' =>
array (size=1)
'total' => int 2
2015 =>
array (size=3)
'01' =>
array (size=1)
'total' => int 1
'02' =>
array (size=1)
'total' => int 1
'03' =>
array (size=1)
'total' => int 1
Hope this helps.
As a general rule, if you can access the data and see in your mind how you want that data to look, then it's pretty much possible to get it to do that. It's just a matter of working out the process.
In your case, I would do the following steps:
Order the data by users, then by date so everything is nicely together
Loop through the data and each time, check that the current user is the same as the last one. if it's not, create a new array key
split the date into the parts you want
check the user array for the key relating to year for that user. If the year exists, search for the month. If the month exists, add 1 to the total for that month. If the year and/or month don't exist, create the keys and set the total to be 1 for that month
Once the records have been processed, you should have the data in the format you need.
I'm trying to use the Indeed.com XML Feed API's in a PHP website. I use this script https://github.com/indeedlabs/indeed-php, see how it works on the Github page (very good script, thanks to the author).
It works but Indeed always returns only 25 results for jobs, even when I set the 'limit', 'start' and 'end' parameters.
Here are the parameters I send :
$aParams = array(
"q" => "php",
"l" => "paris",
"co" => "FR",
"limit" => 10000,
"sort" => "date",
"start" => 0,
"end" => 100,
"userip" => $_SERVER["REMOTE_ADDR"],
"useragent" => $_SERVER["HTTP_USER_AGENT"],
"v" => 2,
"format" => "json",
"publisher" => "123456789"
);
An array is returned and contains :
[version] = 2
[query] = 'php'
[location] = 'paris'
[dupefilter] = 'true'
[highlight] = 'true'
[start] = 1
[end] = 25
[totalResults] = 2068
[pageNumber] = 0
[results] = an array which contains the jobs informations
As we can see, totalResults is equal to 2058 but real the jobs results array always contains only 25 entries.
It seems to be a pagination issue (read here : http://forums.appthemes.com/report-jobroller-bugs/indeed-integration-api-37420) but I don't understand the goal : why proceed like this and not more simply ? So I have to do many requests : one to know first the 'totalResults' and save it (in session for example) and other requests to paginate the results 25 by 25 until the last?
Are there any developers who use this API and how do you proceed?
Thanks
Right Indeed limits the feed to 25 at a time. I have written script to get round this. In the Indeed $params you can specify a 'start' which as default is 0.
I have created a script which using the job count creates a foreach loop and loops the API changing the 'start' to keep getting different results until theres no more left. Then it puts it all into a single PHP array.
Single API request just so we can get totalResults (count of total jobs)
$client = new Indeed("YOUR_ID");
$args_count = array(
"q" => "YOUR SEARCH QUERY",
"l" => "",
"co" => "GB",
"userip" => "1.2.3.4",
"limit" => 10000,
"useragent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2)"
);
Once we have the total job count we divide it by 25 and round the result up. This is so we know how many times we need to run our foreach
$totalResults = $client->search($args_count);
$totalCount = $totalResults['totalResults'] / 25;
$loop_to_count = ceil($totalCount);
We create a array starting with 0 and going up in 25s to as many as you require. My below will return 150 results.
$counter = 0;
$loop_options = array('0', '25', '50', '75', '100', '125', '150');
Then we start the main foreach:
$results = '';
foreach ($loop_options as $options) {
$params = array(
"q" => "YOUR SEARCH QUERY",
"l" => "",
"co" => "GB",
"userip" => "1.2.3.4",
"limit" => 10000,
"start" => $options,
"useragent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2)"
);
$getResults = $client->search($params);
foreach ($getResults['results'] as $rawResults) {
$Subresults[] = array(
'jobtitle' => $rawResults['jobtitle'],
'company' => $rawResults['company'],
'city' => $rawResults['city'],
'state' => $rawResults['state'],
'country' => $rawResults['country'],
'language' => $rawResults['language'],
'formattedLocation' => $rawResults['formattedLocation'],
'source' => $rawResults['source'],
'date' => $rawResults['date'],
'snippet' => $rawResults['snippet'],
'url' => $rawResults['url'],
'onmousedown' => $rawResults['onmousedown'],
'jobkey' => $rawResults['jobkey'],
'sponsored' => $rawResults['sponsored'],
'expired' => $rawResults['expired'],
'indeedApply' => $rawResults['indeedApply'],
'formattedLocationFull' => $rawResults['formattedLocationFull'],
'formattedRelativeTime' => $rawResults['formattedRelativeTime'],
'stations' => $rawResults['stations']
);
}
$counter++;
if ($counter == $loop_to_count) { break; }
}
Finally all our results are inside this array:
$results = array ('results' => $Subresults);
$results will contains all the jobs you have posted on Indeed
please give limit attribute i gave 50 eg :
http://api.indeed.com/ads/apisearch?publisher=1772xxxxxxxxx&q=java&l=austin%2C%20tx&sort=&radius=&st=&jt=&start=&limit=50&fromage=&filter=&latlong=1&co=us&chnl=&userip=1.2.3.4&useragent=Mozilla/%2F4.0%28Firefox%29&v=2
Here you are using a library so you have to modify library function process_request() , in that function add a line $args["limit"] = 50; . here i just gave 50 you can initialize whatever number you want.
I have a following table display
Task No. Person Company Project Task Date Tracked Total Time Comments
14.1 vaish, arpit ABC-TecHolding Relaunch Websitnalysis 2013-02-12 19:21:03
14.2 vaish, arpit Sugarion New Opensource Server 2013-02-12 19:21:03
14.6 vaish, arpit Sugarion New Opensource Server 2013-02-12 19:21:03
5.26 Projectmanager, Paul ABC-Tec Holding Relaunch Website 2010-03-13 03:40:16
5.27 Projectmanager, Paul ABC-Tec Holding Relaunch Website 2010-03-12 03:40:16
5.27 Projectmanager, Paul ABC-Tec Holding Relaunch Website 2010-03-13 03:40:16
8.2 Worker, Willi Customers ACME Hosting Data transfer 2010-03-13 00:21:18
14.2 Worker, Willi Sugarion New Opensource Server 2013-02-06 00:21:18 fyhbvghjukjjkhhlhh uuiuijujj jookljh
This is my foreach loop
foreach($result123 as $key=>$task) {
$time=$this->addtime($task['name'],$range['start'],$range['end']); //to calculate total time per user in time range
$diff = $task['estimated']+$task['tracked'];
$diffSign = $diff > 0 ? '-' : '+';
$table->addRow(array(
'number' => $task['tasknumber'].' '.$task['id_project'] ,
'person'=> $task['name'],
'customer' => $task['company'],
'project' => $task['project'],
'task' => $task['task'],
'date' => $task['date_tracked'],
'tracked' => $task['workload_tracked'],
'Total_Time' => $time,
'comment'=>$task['comment']
));
}
The column total time shows total of time tracked per person.
I want the total time should show only once per Person and not display again and again for each row.
Thanks in advance.
That's how I'd do it (Pseudocode) :
Set an array of "checked" people
Foreach record :
Is person in the "checked" array?
If yes, go on.
If no, display total AND add person to "checked" people.
And in PHP : (if I got it right)
$checked = array();
foreach($result123 as $key=>&$task) {
$time=$this->addtime($task['name'],$range['start'],$range['end']); //to calculate total time per user in time range
$diff = $task['estimated']+$task['tracked'];
$diffSign = $diff > 0 ? '-' : '+';
if (in_array($task['name'],$checked) $time =0;
else {
$checked[] = $task['name'];
}
$table->addRow(array(
'number' => $task['tasknumber'].' '.$task['id_project'] ,
'person'=> $task['name'],
'customer' => $task['company'],
'project' => $task['project'],
'task' => $task['task'],
'date' => $task['date_tracked'],
'tracked' => $task['workload_tracked'],
'Total_Time' => $time,
'comment'=>$task['comment']
));
}
$name = '';
foreach($result123 as $key=>$task) {
if ($name == $task['name']) {
$time = '';
}
elseif ($name != $task['name']) {
$time=$this->addtime($task['name'],$range['start'],$range['end']);
$name = $task['name'];
}
$diff= $task['estimated']+$task['tracked'];
$diffSign= $diff > 0 ? '-' : '+';
$table->addRow(array(
'number' => $task['tasknumber'].' '.$task['id_project'] ,
'person'=> $task['name'],
'customer' => $task['company'],
'project' => $task['project'],
'task' => $task['task'],
'date' => $task['date_tracked'],
'tracked' => $task['workload_tracked'],
'Total_Time' => $time,
'comment'=>$task['comment']
));
}
That should do it, I think.
I have an array:
$data = array(
1 => array(
"time" => 1,
"parent" => array(4)
),
2 => array(
"time" => 3,
"parent" => array(4, 5)
),
3 => array(
"time" => 2,
"parent" => array(6)
),
4 => array(
"time" => 1,
"parent" => array(6)
),
5 => array(
"time" => 1,
"parent" => array(4)
),
6 => array(
"time" => 1,
"parent" => array()
)
);
Key is the ID of an element, parent is an array of elements, which refers to element id and time is just an integer.
This is an illustrated example of a given array:
Schema
The integer on the bottom-left is the "id" and the integer in the middle is "time".
My goal here is find the most time-consuming path of this array. In the given example, the path would be 2->5->4->6 (id wise) resulting in 6 "time" overall. It looks pretty easy on paper, however I can't really seem to code an algorythm to get the elements of the most time-consuming path. I would appreciate any kind of help.
I think the algorythm should be kind of bruteforce-ish and check through all of the options available. Thus with the given array it would go like:
1 -> 4 -> 6 = 3
2 -> 4 -> 6 = 5
2 -> 5 -> 4 -> 6 = 6
3 -> 6 = 3
4 -> 6 = 2
5 -> 4 -> 6 = 3
Thanks in advance.
Note that this will only work if there are no loops in the array.
// Note: built this in the SO editor, might have bugs
$cached = [];
$arrays = []; // Do this yourself
function get_path($num) {
global $arrays, $cached;
if (isset($cached[$num])) return $cached[$num];
$array = $arrays[$num];
$maxtime = $array['time'];
$bestpath = array($num);
foreach ($array['parent'] as $i) {
$path = get_path($i);
if ($path['time']+$array['time'] > $maxtime) {
$maxtime = $path['time'] + $array['time'];
$bestpath = array_merge(array($num),$path['path']);
}
}
$cached[$num] = array('path' => $bestpath, 'time' => $maxtime);
return $cached[$num];
}
var_dump(get_path(5));
Not really a bruteforce way, should be close enough to O(n). The basic idea is that you just cache the paths it can take.
Note: I used a bit of C-style syntax here, but ideally you wouldn't actually write the code like this
Is there any way to optimize this piece of code to work faster? I'd appreciate any suggestions!
This piece of code processes the transferring of edges during graph creation.
foreach($times_arrival as $city_id => $time_points) {
// if city is not prohibited for transfers and there is and exists any departure times for this city
if (isset($times_departure[$city_id]) && isset($cities[$city_id]))
{
foreach($times_arrival[$city_id] as $t1_info)
{
foreach($times_departure[$city_id] as $t2_info)
{
if ($t1_info[0] != $t2_info[0]) //transfers are allowed only for different passages
{
$t1 = $t1_info[1];
$t2 = $t2_info[1];
$vertex_key = new Vertex($city_id, $t1, 1);
$vertex_key = $vertex_key->toString();
//minimum transfer time is 10 min.
if (date('H:i', strtotime($t2)) > date('H:i', strtotime('+ 10 minutes', strtotime($t1))))
{
$this->graph[$vertex_key][] = new Edge(
NULL,
$vertex_key,
new Vertex($city_id, $t2, 0),
(float) 0,
$f((strtotime($t2) - strtotime($t1)) / 60, 0, 1) //edge weight
);
}
//if transfer is on the bound of the twenty-four hours
else if (date('H:i', strtotime('+ 24 hours', strtotime($t2))) > date('H:i', strtotime('+ 10 minutes', strtotime($t1))))
{
$this->graph[$vertex_key][] = new Edge(
NULL,
$vertex_key,
new Vertex($city_id, $t2, 0),
(float) 0,
$f(strtotime('+ 24 hours', strtotime($t2)) - strtotime($t1) / 60, 0, 1) //edge weight
);
}
}
}
}
}
}
example of variables:
var_dump($times_arrival); //$times_departure have the same structure
array
3 =>
array
0 =>
array
0 => string '1' (length=1)
1 => string '08:12' (length=5)
1 =>
array
0 => string '2' (length=1)
1 => string '08:40' (length=5)
41 =>
array
0 =>
array
0 => string '21' (length=2)
1 => string '12:40' (length=5)
Thank you all!
The reason of slow speed was coz of using functions strtotime() and date().
In that case only you can say whether you chose a good or bad algorithm. In my point of view your code not has no extra computations.
Only one recommendation - use Xdebug to profile your code and find out where the bottleneck is, if possible.