php compare/match range of values - php

I have an array to which I need to compare data from mysql. Usually I'm doing a straight comparison so I can do an if ($array[$i]===$mysql[$i]), but I do have one instance where I need to match it against a range of numbers (ex. 18-19, 20-24, etc). I looked into preg_match & preg_grep, but they don't seem to be what I want…
I just need a true/false result from the comparison.
The part of the array I'm trying to match against looks like this:
"age"=>array(
'18-19'=>array('total'=>0,'completed'=>0),
'20-24'=>array('total'=>0,'completed'=>0),
'25-29'=>array('total'=>0,'completed'=>0),
'30-34'=>array('total'=>0,'completed'=>0),
'35-39'=>array('total'=>0,'completed'=>0),
'40-44'=>array('total'=>0,'completed'=>0),
'45-49'=>array('total'=>0,'completed'=>0),
'50-54'=>array('total'=>0,'completed'=>0),
'55-59'=>array('total'=>0,'completed'=>0)
),"race"=>array(
"White"=>array('total'=>0,'completed'=>0),
"Black"=>array('total'=>0,'completed'=>0),
"Hispanic"=>array('total'=>0,'completed'=>0),
"Asian"=>array('total'=>0,'completed'=>0),
"Pacific Islander"=>array('total'=>0,'completed'=>0),
"Multiracial"=>array('total'=>0,'completed'=>0),
"Other"=>array('total'=>0,'completed'=>0)
)
Is there a clean way to do this?
Thanks!

list($min,$max) = explode('-', $array[$i]);
if ($mysql[$i] >= $min && $mysql[$i] <= $max) ...

PHP's range() function might be useful:
foreach ($array['age'] as $ageRange => $something) {
list($start, $limit) = explode('-', $ageRange);
foreach (range($start, $limit) as $age) {
// compare
}
}

Related

PHP - How to check values inside of an array that they don't have the same value? [duplicate]

I'm sure this is an extremely obvious question, and that there's a function that does exactly this, but I can't seem to find it. In PHP, I'd like to know if my array has duplicates in it, as efficiently as possible. I don't want to remove them like array_unique does, and I don't particularly want to run array_unique and compare it to the original array to see if they're the same, as this seems very inefficient. As far as performance is concerned, the "expected condition" is that the array has no duplicates.
I'd just like to be able to do something like
if (no_dupes($array))
// this deals with arrays without duplicates
else
// this deals with arrays with duplicates
Is there any obvious function I'm not thinking of?
How to detect duplicate values in PHP array?
has the right title, and is a very similar question, however if you actually read the question, he's looking for array_count_values.
I know you are not after array_unique(). However, you will not find a magical obvious function nor will writing one be faster than making use of the native functions.
I propose:
function array_has_dupes($array) {
// streamline per #Felix
return count($array) !== count(array_unique($array));
}
Adjust the second parameter of array_unique() to meet your comparison needs.
Performance-Optimized Solution
If you care about performance and micro-optimizations, check this one-liner:
function no_dupes(array $input_array) {
return count($input_array) === count(array_flip($input_array));
}
Description:
Function compares number of array elements in $input_array with array_flip'ed elements. Values become keys and guess what - keys must be unique in associative arrays so not unique values are lost and final number of elements is lower than original.
Warning:
As noted in the manual, array keys can be only type of int or string so this is what you must have in original array values to compare, otherwise PHP will start casting with unexpected results. See https://3v4l.org/7bRXI for an example of this fringe-case failure mode.
Proof for an array with 10 million records:
The top-voted solution by Jason McCreary: 14.187316179276s 🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌
The accepted solution by Mike Sherov: 2.0736091136932s 🐌🐌
This answer's solution: 0.14155888557434s 🐌/10
Test case:
<?php
$elements = array_merge(range(1,10000000),[1]);
$time = microtime(true);
accepted_solution($elements);
echo 'Accepted solution: ', (microtime(true) - $time), 's', PHP_EOL;
$time = microtime(true);
most_voted_solution($elements);
echo 'Most voted solution: ', (microtime(true) - $time), 's', PHP_EOL;
$time = microtime(true);
this_answer_solution($elements);
echo 'This answer solution: ', (microtime(true) - $time), 's', PHP_EOL;
function accepted_solution($array){
$dupe_array = array();
foreach($array as $val){
// sorry, but I had to add below line to remove millions of notices
if(!isset($dupe_array[$val])){$dupe_array[$val]=0;}
if(++$dupe_array[$val] > 1){
return true;
}
}
return false;
}
function most_voted_solution($array) {
return count($array) !== count(array_unique($array));
}
function this_answer_solution(array $input_array) {
return count($input_array) === count(array_flip($input_array));
}
Notice that accepted solution might be faster in certain condition when not unique values are near the beginning of huge array.
You can do:
function has_dupes($array) {
$dupe_array = array();
foreach ($array as $val) {
if (++$dupe_array[$val] > 1) {
return true;
}
}
return false;
}
$hasDuplicates = count($array) > count(array_unique($array));
Will be true if duplicates, or false if no duplicates.
Here's my take on this… after some benchmarking, I found this to be the fastest method for this.
function has_duplicates( $array ) {
return count( array_keys( array_flip( $array ) ) ) !== count( $array );
}
…or depending on circumstances this could be marginally faster.
function has_duplicates( $array ) {
$array = array_count_values( $array );
rsort( $array );
return $array[0] > 1;
}
$duplicate = false;
if(count(array) != count(array_unique(array))){
$duplicate = true;
}
Keep it simple, silly! ;)
Simple OR logic...
function checkDuplicatesInArray($array){
$duplicates=FALSE;
foreach($array as $k=>$i){
if(!isset($value_{$i})){
$value_{$i}=TRUE;
}
else{
$duplicates|=TRUE;
}
}
return ($duplicates);
}
Regards!
To remove all the empty values from the comparison you can add array_diff()
if (count(array_unique(array_diff($array,array("")))) < count(array_diff($array,array(""))))
Reference taken from #AndreKR answer from here
Two ways to do it efficiently that I can think of:
inserting all the values into some sort of hashtable and checking whether the value you're inserting is already in it(expected O(n) time and O(n) space)
sorting the array and then checking whether adjacent cells are equal( O(nlogn) time and O(1) or O(n) space depending on the sorting algorithm)
stormdrain's solution would probably be O(n^2), as would any solution which involves scanning the array for each element searching for a duplicate
Find this useful solution
function get_duplicates( $array ) {
return array_unique( array_diff_assoc( $array, array_unique( $array ) ) );
}
After that count result if greater than 0 than duplicates else unique.
I'm using this:
if(count($array)==count(array_count_values($array))){
echo("all values are unique");
}else{
echo("there's dupe values");
}
I don't know if it's the fastest but works pretty good so far
One more solution from me, this is related to performance improvement
$array_count_values = array_count_values($array);
if(is_array($array_count_values) && count($array_count_values)>0)
{
foreach ($array_count_values as $key => $value)
{
if($value>1)
{
// duplicate values found here, write code to handle duplicate values
}
}
}
As you specifically said you didn't want to use array_unique I'm going to ignore the other answers despite the fact they're probably better.
Why don't you use array_count_values() and then check if the resulting array has any value greater than 1?
Php has an function to count the occurrences in the array http://www.php.net/manual/en/function.array-count-values.php
You can do it like that way also:
This will return true if unique else return false.
$nofollow = (count($modelIdArr) !== count(array_unique($modelIdArr))) ? true : false;
The simple solution but quite faster.
$elements = array_merge(range(1,10000000),[1]);
function unique_val_inArray($arr) {
$count = count($arr);
foreach ($arr as $i_1 => $value) {
for($i_2 = $i_1 + 1; $i_2 < $count; $i_2++) {
if($arr[$i_2] === $arr[$i_1]){
return false;
}
}
}
return true;
}
$time = microtime(true);
unique_val_inArray($elements);
echo 'This solution: ', (microtime(true) - $time), 's', PHP_EOL;
Speed - [0.71]!
function hasDuplicate($array){
$d = array();
foreach($array as $elements) {
if(!isset($d[$elements])){
$d[$elements] = 1;
}else{
return true;
}
}
return false;
}

Alternative way to prevent use of multiple loop / PHP

Im trying to select the difference between 2(Two) Array. And yes, I have a solution using loop. But I think its a big problem because using so many loops make the process slow. Im asking if there's alternative way or a simple way on how to do it with the same output.
This is my code
$unique = [];
$first_array = [["SERIAL_NUMBER" => "1"]];
$second_array = [["SERIAL_NUMBER" => "1"],["SERIAL_NUMBER" => "2"]];
foreach ($second_array as $second) {
foreach($first_array as $first)
{
if($second['SERIAL_NUMBER'] == $first['SERIAL_NUMBER'])
{
continue 2;
}
}
$unique[] = $second;
}
foreach ($first_array as $first) {
foreach($second_array as $second)
{
if($first['SERIAL_NUMBER'] == $second['SERIAL_NUMBER'])
{
continue 2;
}
}
$unique[] = $first;
}
echo json_encode($unique);
The result will be the difference of 2(two) array.
PS: This is base on specific key of the multidimensional array (SERIAL_NUMBER)
[{"SERIAL_NUMBER":"2"}]
The difference is that most things will use some sort of loop, even if you don't see the loop in your code it may be done internally.
This code uses array_udiff(), mainly to simplify the code, but also as it's a multidimensional array - you can't just use array_diff(). The function just compare the SERIAL_NUMBER values of each elements.
This is done once in each direction (1 => 2 and 2 => 1) and then merges the results...
function udiffCompare($a, $b)
{
return $a['SERIAL_NUMBER'] <=> $b['SERIAL_NUMBER'];
}
$arrdiff1 = array_udiff($first_array, $second_array, 'udiffCompare');
$arrdiff2 = array_udiff($second_array, $first_array, 'udiffCompare');
$difference = array_merge($arrdiff1, $arrdiff2);

Php filter multidimensional array

I have array like this:
arr=[
627=[
'lead_data'=>['name'=>'Name1', 'date'=>'2019-04-09']
],
500=[
'lead_data'=>['name'=>'Name2', 'date'=>'2018-05-07']
],
534=[
'lead_data'=>['name'=>'Name3', 'date'=>'2019-07-10']
],
100=[
'lead_data'=>['name'=>'Name4', 'date'=>'2019-05-12']
],
]
How can I filter this array where date is between 2019-05-01 and 2019-07-12.
So in result there will be elements with ids 534 and 100.
Or date is >= 2019-07-05 or date is <= 2019-01-01 ?
I know there is array_filter function, but cant understand how to use it in thus case? Please help, thanks
The simplest solution would to just iterate over your data like so:
<?php
$begin = date('Y-m-d', strtotime("2019-05-01"));
$end = date('Y-m-d', strtotime("2019-07-12"));
foreach($array as $key => $data)
{
$date = date('Y-m-d', strtotime($$data['date']));
if (($$data > $begin) && ($date < $end)){
unset($array[$key]);
}
}
var_dump($array);
Always make sure you check out official documentation on php.net because it usually have tons of examples and very thorough explanations.
In your case you can compare dates as strings (since they are in Y-m-d format and comparing them alphabetically will do the trick):
$filtered = array_filter($arr, function ($item) {
return ($item['lead_data']['date'] > '2019-05-01') && ($item['lead_data']['date'] < '2019-07-12');
});
By using array_filter(), and using the use keyword, you can supply variables into the filter - this can be the start- and end-dates for your limits.
When using array_filter(), the data will remain in the array if the return value inside the callback is true, otherwise it will be removed. So then compare the dates, and see if they are greater than $from, and less than $to.
$from = '2019-05-01';
$to = '2019-07-12';
$result = array_filter($arr, function ($v) use ($from, $to) {
return $v['lead_data']['date'] > $from && $v['lead_data']['date'] < $to;
});
print_r($result);
Live demo at https://3v4l.org/Cmt8H

PHP - Delete all elements from array with a specific filter

I am building an AJAX powered smart search engine.
I work with keywords, so in the back-end script I explode the string provided by the AJAX.
( the string is the value of the search input field )
$keywords = $_POST["keywords"];
$keywords = strtolower($keywords);
$keyword = explode(" ", $keywords);
As You can see now, I have an array named $keyword containing all the keywords.
Now, I have to remove the values from the array whiches would result irrelevant results. To be more defined: I would like to remove all the rows with a value lenght less than 3.
I tried it in many ways, the last one was a simple foreach loop:
foreach ($keyword as $key => $value) {
if ( strlen($key[ $value ]) < 3 ) {
unset($keyword[$key]);
}
}
I read about array_filter, and functions like these, but - I mean - I can't imagine why it doesn't work this way.
Please if You have any ideas, write me an answer! Thanks for Your attention.
You're nearly there with your loop, however $value is not a key within the $key variable. If you print_r your $keywords you will see that the $key variable will be the string on the left side of the => and the $value on the right.
Quick Fix:
if( strlen($value) < 3 )
Better fix:
$keywords = array_filter($keywords, function($x) { return strlen($x) >= 3; });
Nested fix
$keywords = array_filter(explode(' ',strtolower($_POST)), function($x) { return strlen($x) >= 3; });
Alternate fix (if you have a too old version of PHP that you cant do the anonymous functions above)
$keywords = array_filter(explode(' ',strtolower($_POST)), create_function('$x', 'return strlen($x) >= 3;'));
The value of the array will be in $value not $key[$value].
Try
if ( strlen( $value ) < 3 ) {
You can use array_filter for that:
$keywords = array_filter($keywords, function($keyword) {
return strlen($keyword) >= 3;
});
It will return an array with all keywords greater than 2 chars. Haven't tested it, but I think you get the idea.
Of course a plain foreach will do it to, but array_filter is less code and therefore easier to maintain and read. In terms of speed I guess there won't be any big differences.

Regex for number comparison?

I would like to perform regex to return true/false if the input 5 digit from input matching data in database, no need to cater of the sequence, but need the exact numbers.
Eg: In database I have 12345
When I key in a 5 digit value into search, I want to find out whether it is matching the each number inside the 12345.
If I key in 34152- it should return true
If I key in 14325- it should return true
If I key in 65432- it should return false
If I key in 11234- it should return false
Eg: In database I have 44512
If I key in 21454- it should return true
If I key in 21455- it should return false
How to do this using php with regex
This is a way avoiding regex
<?php
function cmpkey($a,$b){
$aa = str_split($a); sort($aa);
$bb = str_split($b); sort($bb);
return ( implode("",$aa) == implode("",$bb));
}
?>
Well, it's not going to be a trivial regex, I can tell you that. You could do something like this:
$chars = count_chars($input, 1);
$numbers = array();
foreach ($chars as $char => $frequency) {
if (is_numeric(chr($char))) {
$numbers[chr($char)] = $frequency;
}
}
// that converts "11234" into array(1 => 2, 2 => 1, 3 => 1, 4 => 1)
Now, since MySQL doesn't support assertions in regex, you'll need to do this in multiple regexes:
$where = array();
foreach ($numbers AS $num => $count) {
$not = "[^$num]";
$regex = "^";
for ($i = 0; $i < $count; $i++) {
$regex .= "$not*$num";
}
$regex .= "$not*";
$where[] = "numberField REGEXP '$regex'";
}
$where = '((' . implode(') AND (', $where).'))';
That'll produce:
(
(numberField REGEXP '^[^1]*1[^1]*1[^1]*$')
AND
(numberField REGEXP '^[^2]*2[^2]*$')
AND
(numberField REGEXP '^[^3]*3[^3]*$')
AND
(numberField REGEXP '^[^4]*4[^4]*$')
)
That should do it for you.
It's not pretty, but it should take care of all of the possible permutations for you, assuming that your stored data format is consistent...
But, depending on your needs, you should try to pull it out and process it in PHP. In which case the regex would be far simpler:
^(?=.*1.*1})(?=.*2)(?=.*3)(?=.*4)\d{5}$
Or, you could also pre-sort the number before you insert it. So instead of inserting 14231, you'd insert 11234. That way, you always know the sequence is ordered properly, so you just need to do numberField = '11234' instead of that gigantic beast above...
Try using
^(?=.*1)(?=.*2)(?=.*3)(?=.*4)(?=.*5).{5}$
This will get much more complicated, when you have duplicate numbers.
You really should not do this with regex. =)

Categories