I have an array of arrays, each array containing details of a scan by a medical device. I'm getting this data from text logs that are dumped nightly. The format of which is this:
$this->scans = array(
array(
'patientid' => (int),
'patientname' => 'John Skeet',
'reviewed' => 0 or 1
//plus more irrelevant
),
array(
//same as above
), //etc
)
The important array key here is reviewed, as each scan may be reviewed if it is of high enough quality. However, the text logs dump out EVERY scan that is acquired, then goes back through and re-lists the ones that are reviewed.
Now in order to prevent duplicates , I figured I could just use an array_filter to filter out scans that have been both acquired and reviewed (keeping the reviewed version). However, the filter function is filtering out the entire array (except in some rare cases). If someone could take a look and let me know why they think it's happening that would be much appreciated.
$this->scans = array_filter($this->scans, array($this, "scan_cleanup"));
.
private function scan_cleanup($scan) {
//only if the scan was not reviewed
if ($scan['reviewed'] == 0) {
//change reviewed status to see if there is a duplicate
$scan['reviewed'] == 1;
//return false to remove this copy (and keep reviewed)
if (in_array($scan, $this->scans)) {
return false;
}
}
return true;
}
$scan['reviewed'] == 1;
vs
$scan['reviewed'] = 1;
One is a conditional, that does nothing in this context, the other is not there.
You are also not running the return false very often. I'd change the logic a little to make it a little clearer, and simpler by a little refactoring (pulling out a condition-check).
if ($scan['reviewed'] and hasDupe($scan)) {
return false; // filter out
}
return true; // it is passed back, and is output
hasDupe() does the best checks you know for a duplicate record and returns true/false.
Simple case of "==" vs. "=" as far as I can see.
$scan['reviewed'] = 1;
That oughta do the trick. Sometimes the simplest problems are the hardest to spot ;-)
Related
I am making a real estate related app and I've been having a hard time figuring out how to set up the query so that it would return "Only Apartments or Duplexes within selected areas" I'd like to user to be able to find multiple types of property in multiple selected quadrants of the city.
I have a database with a column "type" which is either "Apartment", "House", "Duplex", "Mobile"
In another column I have quadrant_main with values: "NW", "SW", "NE", "SE".
My code works when there is only 1 quadrant selected, but when I select multiple quadrants, I seem to get results which includes ALL the property types from the second or third or 4th quadrant, instead of only "Apartment" and "Duplex" or whatever types the user selects... Any help will be appreciated! thx in advance.
My controller function looks like this:
public function quadrants()
{
$input = \Request::all();
$currentPage = null;
$column = "price";
$order = "desc";
//
// Looks like the input is like 0 => { key: value } ...
// (an Array of key/value pairs)
$q = Listing::where('status','=','Active')->where(function($query) {
$input = \Request::all();
$currentPage = null;
$typeCount = 0;
$quadrantCount = 0;
foreach( $input as $index => $object ) {
$tempObj = json_decode($object);
$key = key((array)$tempObj);
$val = current((array)$tempObj);
if ( $key == "type" ) {
if ( $typeCount > 0 ) {
$query->orWhere('type', '=', $val );
}
else {
$query->where('type', '=', $val );
$typeCount++;
}
}
if ( $key == "quadrant_main" ) {
if ( $quadrantCount > 0 ) {
$query->orWhere('quadrant_main', '=', $val );
}
else {
$query->where('quadrant_main', '=', $val );
$quadrantCount++;
}
}
// else {
// $query->orWhere($key,$val);
// }
}
if( $currentPage ) {
//Force Current Page to Page of Val
Paginator::currentPageResolver(function() use ($currentPage) {
return $currentPage;
});
}
});
$listings = $q->paginate(10);
return $listings;
Looking at your question, its a bit confusing and not much is given to answer definitely. Probable causes of your troubles may be bad data in database, or maybe corrupted input by user.
Disclaimer: Please note that chances are my answer will not work for you at all.
In that case please provide more information and we will work things
out.
There is one thing that I think you have overlooked and thus you are getting awry results. First let me assume a few things.
I think a sample user input should look like this:
array(
0: '{type: Apartment}',
1: '{type: Duplex}',
2: '{quadrant_main: NW}',
3: '{quadrant_main: SW}',
)
What the user meant was give me any apartment or duplex which belongs in NW or SW region.
So after your loop is over, the final SQL statement should be something like this:
Oh and while we are at SQL topic, you can also log the actual
generated SQL query in laravel so you can actually see what was the
final SQL getting generated. If you can post it here, it would help a
lot. Look here.
select * from listings where status = 'Active' and (type = 'Apartment' or type = 'Duplex' and quadrant_main = 'NW' or quadrant_main = 'SW');
What this query will actually produce is this:
Select any listing which is active and:
1. Type is an apartment, or,
2. Type is a duplex, or,
3. Quadrant is SW, and,
4. Quadrant is NW
So assuming you have a database like this:
id|type|quadrant_main
=====================
1|Apartment|NW
2|Apartment|SW
3|Apartment|NE
4|Apartment|SE
5|Duplex|NW
6|Duplex|SW
7|Duplex|NE
8|Duplex|SE
9|House|NW
10|House|SW
11|House|NE
12|House|SE
You will only receive 1, and 5 in the result set. This result set is obviously wrong, plus it is depended on NW because that was the and condition.
The correct SQL query would be:
select * from listings where status = 'Active' and (type = 'Apartment' or type = 'Duplex') and (quadrant_main = 'NW' or quadrant_main = 'SW');
So structure your L5 app such that it produces this kind of SQL query. Instead of trying to cram everything in one loop, have two loops. One loop should only handle type and another loop should only handle quadrant_main. This way you will have the necessary and condition in the right places.
As a side note:
Never directly use user input. Always sanitize it first.
Its not a best practice to put all your logic in the controller. Use repository pattern. See here.
Multiple where clauses are generally applied via Criteria. Check that out in the above linked repository pattern.
You code logic is very complicated and utterly un-necessary. Instead of sending JSON objects, simply send the state of checkboxes. Don't try to generalize the function by going in loop. Instead handle all checkboxes one by one i.e. is "Apartments" selected, if yes, add that to your clause, if not, don't add.
Please have a look to below code
function GetAreaName($AreaCode)
{
switch ($AreaCode)
{
case 201: return 'New Jersey';
case 202: return 'Washington';
// this goes on till
case 999: return '';
}
}
Let's say if the AreaCode is 998 then it would have to go through so many cases!
How could we optimize this function? (No using databases.)
I'm thinking to build an array and do a binary search over it? But this means every time the function is called the array will be rebuild? How do we build the array once, cache it and re-use every time this function is called?
Why not just use a hash table?
class Area {
private $areaCodes = array(
201 => 'New Jersey',
202 => 'Washington',
// this goes on till
999 => '';
);
function getStateByAreaCode ($areaCode) {
if (array_key_exists($areaCode, $this->areaCodes)) {
return $this->areaCodes[$areaCode];
} else {
return false;
}
}
}
Call it like this:
$area = new Area();
$city = $area->getStateByAreaCode(303);
Just save your class in a file and include it when you need it.
You Asked How to Prevent the Array From Being Created Every Request:
By putting this in a class you at least keep it clean. It technically still gets created each request, but unless your array is enormous (WAY bigger than the area codes in the U.S.) it shouldn't pose a performance issue. If you are worried about building the array every time you have a request, then take a look at a code optimizer like APC or the Zend Optimizer. This essentially takes the byte code that PHP generates at run time and caches it.
Sounds like you should just store it in your database.
But if you can't do that, either abstract it into a config file of some kind and store it in some kind of persisted object, or just use a static variable:
function foo($key) {
static $cache = array(1 => 'abc', 2 => 'def', 3 => 'ghi');
if (array_key_exists($key, $cache)) {
return $cache[$key];
} else {
//Somehow signal an error (throw an exception, return boolean false, or something)
}
}
In the above, $cache would only exist once. (If you knew that the values would never be null, you could use isset instead of array_key_exists.)
This isn't very flexible though since changing the data requires you to edit your code. You typically want your data and your code to be decoupled.
That could mean storing it in some kind of file (json, xml, php, whatever), and loading it into some kind of structure that you only create once. You would then pass that object or array around wherever it was needed. (Or, if you wanted to be hacky, you could use a static class. I suggest against this though.)
Switch condition is evaluated once only:
In a switch statement, the condition is evaluated only once and the result is compared to each case statement. In an elseif statement, the condition is evaluated again. If your condition is more complicated than a simple compare and/or is in a tight loop, a switch may be faster. ➫➫➫
There is no optimization required. However, read:
In PHP what's faster, big Switch statement, or Array key lookup
If you want to build a config file, you can consider something like:
$areas = array
(
1 => 'abc',
2 => 'def',
..
);
Then simply compare:
if (!isset($areas[$some_code]))
{
// do something
}
else
{
// ok
}
Try below pseudo code
$areas = array('201' => 'New Jersey',
'202' => 'Washington',
......
........
'999' => '');
function GetAreaName($AreaCode)
{
if(isset($areas[$AreaCode])) {
return $areas[$AreaCode];
} else {
// do something
}
}
Ultimate Goal
Is to make something like Magento offers - basically a logic builder, and as shown by this post on Stackoverflow: jQuery (or any web tool) Nested Expression Builder So far I have made jQuery to build a tree and get the data that I want the builder to use, check, and set. Now I just need to parse the checks and add it into various places in a script I'm making - but I am unsure how to process it dynamically so that these checks can be performed, which will lead to some actions occurring/data being changed automatically.
Maybe we can call this dynamic expression processing?
Original Post
Forgive me, I know what I would like to do, but have little idea how to do it - so I'm looking for some inspiration. I have allowed a multidimensional array to be generated, and the array would contain certain 'commands' and logic functions, and when a condition is true it is executed.
In it's most basic form, the array would contain a set of if statements, where if the statement were true, then would would proceed to the next array item and go down a level, if it were false, then you'd proceed to the next array item with no children (an unmarried sibling, i guess we could call it). Once there is nothing left to process, since nothing is true, then nothing would happen.
I'd imagine that maybe the best way to 'feed' the data in would be via XML - though would this be possible, I mean, to keep going deeper, else go down, essentially until there is a true condition?
Basically, the array takes the following form (though I not 100% sure I've written it correctly, but I think it looks right :s):
[0][0] => array('function' => 'if', 'check' => 'day', 'condition' => 'equals', 'value' => '3');
[0][1][0] => array('function' => 'set', 'name' => 'date_day', 'value' => 'wednesday');
[1][0] => array('function' => 'if', 'check' => 'day', 'condition' => 'equals', 'value' => '4');
[1][1][0] => array('function' => 'set', 'name' => 'date_day', 'value' => 'thursday');
So the above would be - if day=3, then set date_day as wednesday; else if day=4, then set date_day as thursday
Which I'd imagine would correspond to (though i have no idea if you can sub item):
<items>
<item>
<function>if</function>
<check>day</check>
<condition>equals</condition>
<value>3</value>
<item>
<function>set</function>
<name>date_day</name>
<value>wednesday</value>
</item>
</item>
<item>
<function>if</function>
<check>day</check>
<condition>equals</condition>
<value>4</value>
<item>
<function>set</function>
<name>date_day</name>
<value>thursday</value>
</item>
</item>
</items>
Which would basically make the following statements in a function of some sort:
function ($current_data){
LOOP
if(FUNCTION == "if"){
if(CHECK CONDITION VALUE){
**go to next item deeper in the chain**
} else {
**go to sibling item**
}
} else if(FUNCTION == "set"){
define(NAME, VALUE);
}
ENDLOOP
}
I know the above can be done using the date() function, but this is a very basic example. Another example could involve check to see if the colour entered was red, and if it were, then set something based on this colour, else do something else if it were blue. Another could be to set the template to be for US visitors if the US flag was clicked on. The point is that it could basically fulfil any action and do a check and give a result - basically like programming - but where the function data is feed in by PHP or XML
I'm sure there must be something out there that can accomplish this, but I just have no idea were to start exactly, so any assistance would be great - and yes I know there could be some security concerns, but I plan on having checks in place checking that the checks, conditions, values, etc are safe (so this needs to be able to be factored in).
Many many thanks!
Okay JSON vs. XML aside, here's how I would process that array...
$array = xmldecode($xml);
$resultFound = false;
$i = 0;
while(!$resultFound && $i < count($array)) {
if (myFunction($array[$i]) {
$resultFound = true;
}
$i++;
}
if (!$resultFound) {
// error condition
}
function myFunction($array) {
$function = $array[0]['function'];
switch($function) {
case 'if':
$checkVariable = $array[0]['check'];
$condition = $array[0]['condition'];
$checkValue = $array[0]['value'];
switch($checkVariable)
case 'day':
switch($condition) {
case 'equals':
if (GLOBAL_DAY == $checkValue) {
return myFunction($array[1]);
} else {
return false;
}
break;
case 'less than':
if (GLOBAL_DAY < $checkValue) {
return myFunction($array[1]);
} else {
return false;
}
break;
}
break;
}
break;
case 'set':
$setVariable = $array[0]['name'];
$setValue = $array[0]['value'];
switch($setVariable) {
case 'date_day':
GLOBAL_DATE_DAY = $setValue;
return true;
break;
}
break;
}
}
Your problem is very similar to that of form validation, so I would look at some popular jQuery form validation plugins or MVC frameworks like CakePHP. These typically all have stock building blocks for the most common validation rules that the user can easily put together and pass specific arguments to to cover most scenarios.
The notable difference between your problem and these examples is that form validation frameworks are aimed at developers, so they can simply write custom functions if they need to glue multiple rules together to form a more complex rule. However, you can still achieve something that works for probably 98% of all use cases by doing something like:
$offers = array(
'pet feed sale' => array(
'category' => array(1, 2, 3)
'total' => '>100',
'weekday' => array('mon', 'wed')
'set' => array(
'discount' => 80
'shipping' => 0
)
),
'medication sale' => array(
'category' => 4
'date' => '2012-1-28',
'set' => array(
'discount' => 50
)
)
);
And if the user needs apply more complex pricing structures then they could, for instance, break the "pet feed sale" rule into 3 offers, one for dog food, one for cat food, and one for fish food. There might be more repetition, but it makes it much easier to implement than a full parser.
Also, most non-programmers probably handle repetition a lot better than complicated logic and control flow.
For several days now I am trying to cope with the algorithm implementation at the online shop which I am writing in PHP. I do not know whether the problem is only the implementation, or perhaps bad algorithm design. Hovewer, for me it seems fine. I only haven`t checked its complexity of it, but it's such a problem.
After a long deliberation on the same algorithm, without thinking on implementation I came up with the use of binary search tree (bst) with additional data inserted into list consist of user defined info (later about it). The whole orders list would be displayed, or returned using inorder method.
I write it like that:
If the input object date is greater than current object go right
If the input object date is less than current object go left
If the dates are the same stay at place
If the field is blank check if the product is in stock
If it is put into place and finish
If there is not do nothing and exit
If the field is full
{Check if on the list is this user id
If yes than check order priority
If no do nothing and exit
Check if there is product on stock
If yes replace record and exit
If no do nothing and exit
}
{If there is not user id on the list check if product is on stock
If yes then put element on the end
If no do nothing and exit
}
Maybe it looks a little bad, but I was not able to do indentation.
Data is transferred into algorithm in a loop until the end of orders list. The list is unordered.
This is my implementation:
class BinaryTree {
private $predescor = array(
'd'=>array('data'=>0),
'p'=>null,
'r'=>null,
'l'=>null
);
private $ancestor = array(
'd'=>array('data'=>0),
'p'=>null,
'r'=>null,
'l'=>null
);
private $i = 0;
public function insertIntoTree(&$root,$element)
{
$this->predescor = $root;
$this->predescor;
while($this->predescor)
{
if($element['d']['data']==$this->predescor['d']['data'])
{
$this->inertIntoList($element,$this->predescor['d']);
return true;
}
$this->predescor = $this->predescor;
if($element['d']['data']<$this->predescor['d']['data'])
{
$this->predescor = $this->predescor['l'];
}
else
{
$this->predescor = $this->predescor['r'];
}
}
$element['p'] = $this->predescor;
if(!$this->predescor)
{
$root = $element;
}
else if($element['d']['data']<$this->predescor['d']['data'])
{
$this->predescor['l'] = $element;
}
else
{
$this->predescor['r'] = $element;
}
return true;
}
public function putList(&$list,$root)
{
if($root!=null)
{
$this->putList($list, $root['l']);
$lista[$this->i] = $root;
$this->i++;
$this->putList($list, $root['r']);
}
return;
}
private function insertIntoList($element,&$position)
{
if($position == null)
{
$position = $element;
return true;
}
foreach($position['user'] as &$key)
{
if($key == $element['d']['user'])
{
if($key['priority']<$element['d']['user']['priority'])
{
return false;
}
else if($key['priority']==$element['d']['user']['priority'])
{
return false;
}
else
{
if(Orders::checkOrder($element['d']['user']['order']))
{
$key['order'] = $element['d']['user']['order'];
return true;
}
else
{
return false;
}
}
}
}
//#todo add at the end
return true;
}
}
I would like to advise whether there is a simpler way than using bst consisting of a quite complex arrays, which would also be easier to implement? Because now I can not inplement it in PHP.
Thank you in advance.
I wouldn't start by coding this in php at all.
I'd start by building this into the database. ("Orders" implies a database.) I'd start by clarifying a couple of points. Assuming that one order can have many line items . . .
The number of days since the last order seems to clearly apply to the order,
not to individual products.
The user can have "only one request carried at a time". Request for
what? Doesn't seem to make sense for this to apply either to an
order or to an order's line item.
The order priority seems to clearly apply to the order, not to line
items. But a line-item priority might make more sense. (What products does the customer need first?)
Whether the product is in stock seems to apply to the line items, not
to the order as a whole.
I'd start by creating two views. (Not because you'll eventually need two views, but because some things are still unclear.)
One view, which has to do with "ranking" as applied to an order, would calculate or display three things.
Number of days since the last order.
Is this order the "one request carried at a time"?
The order priority.
If the numbers assigned to these three things are consistent in scale, you can just sort on those three columns. But that's not likely. You'll probably need to weight each factor, possibly by multiplying by a "weighting" factor. A calculation on the result should let you put these in a useful order. It's not yet clear whether the calculation is best done in the view or in a stored procedure.
The other view would have to do with whether a line item is in stock. It's not clear whether one line item out of stock means the whole order is incomplete, or whether one line item out of stock changes the calculation of a weighted number that scales along with the others above. (You can make a good argument for each of those approaches.)
Attached code taken from cakephp bakery, where someone uploaded a sample about custom validation rules.
class Contact extends AppModel
{
var $name = 'Contact';
var $validate = array(
'email' => array(
'identicalFieldValues' => array(
'rule' => array('identicalFieldValues', 'confirm_email' ),
'message' => 'Please re-enter your password twice so that the values match'
)
)
);
function identicalFieldValues( $field=array(), $compare_field=null )
{
foreach( $field as $key => $value ){
$v1 = $value;
$v2 = $this->data[$this->name][ $compare_field ];
if($v1 !== $v2) {
return FALSE;
} else {
continue;
}
}
return TRUE;
}
}
In the code, the guy used a foreach to access an array member which he had its name already!
As far as I understand - it's a waste of resources and a bad(even strange) practice.
One more thing about the code:
I don't understand the usage of the continue there. it's a single field array, isn't it? the comparison should happen once and the loop will be over.
Please enlighten me.
In the code, the guy used a foreach to access an array member which he had its name already! As far as I understand - it's a waste of resources and a bad(even strange) practice.
The first parameter is always an array on one key and its value, the second parameter comes from the call to that function, in a block named as the key... So, all you need is to send the key and no need to iterate
The code uses foreach to iterate through $field, which is an array of one key value pair. It all starts when the validation routine invokes identicalFieldValues, passing it two values - $field, which would be an array that looks like:
array (
[email] => 'user entered value 1'
)
The second parameter $compare_field would be set to the string confirm_email.
In this particular case, it doesn't look like it makes a lot of sense to use foreach since your array only has one key-value pair. But you must write code this way because CakePHP will pass an array to the method.
I believe the reason why CakePHP does this is because an array is the only way to pass both the field name and its value. While in this case the field name (email) is irrelevant, you might need in other cases.
What you are seeing here is one of the caveats of using frameworks. Most of the time, they simplify your code. But sometimes you have to write code that you wouldn't write normally just so the framework is happy.
One more thing about the code: I don't understand the usage of the continue there. it's a single field array, isn't it? the comparison should happen once and the loop will be over. Please enlighten me.
Indeed. And since there are no statements in the foreach loop following continue, the whole else block could also be omitted.
A simplified version of this would be:
function identicalFieldValues($field=array(), $compare_field=null)
{
foreach ($field as $field) {
$compare = $this->data[$this->name][$compare_field];
if ($field !== $compare) {
return FALSE;
}
}
return TRUE;
}
And I agree with you, the loop only goes through one iteration when validating the email field. regardless of the field. You still need the foreach because you are getting an array though.