in_array() keeps appending values if looping through db rows - php

I need to identify every instance where a value in one array (needle) occurs in another array (haystack). in_array() seems to be my best option, and the code below works perfectly until I need to use it on rows fetched from a db - it keeps appending values instead of setting them each time it's called.
While I can't actually use unset() in this situation, I was surprised to discover that even that didn't seem to resolve the problem.
UPDATE - Example of what's being returned
I temporarily changed the db values so that $needles has only value per row (in order to make it possible to sort through the values filling up my screen ;-))
False;
False; False; True;
False; False; True; False; True;
False; False; True; False; True; False; True;
False; False; True; False; True; False; True; False;
This works correctly
(I've posted a functional example here)
$needles = array('John', 'Alex');
$haystack = array('John','Alexander','Kim', 'Michael');
foreach ($needles as $needle) {
if (in_array($needle, $haystack) ) {
$Match = 'True';
}
else {
$Match = 'False';
}
}
This keeps appending values - Edited to reflect the code I'm using
$Customer_Categories_Arr = array('Casual','Trendy');
if ($stmt->columnCount()) {
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$Product_Categories_Arr[]=$row["Taste_Category"];
// Use when column contains CSV
// $Product_Categories_Arrx = explode(',', trim($Product_Categories_Arr[0]));
foreach ($Product_Categories_Arr as $Product_Category_Arr) {
if (in_array($Product_Category_Arr, $Customer_Categories_Arr)){
$Matches_Product_Category = "True";
} else {
$Matches_Product_Category = "False";
}
echo $Product_Category_Arr, ', ', $Matches_Product_Category, '; ';
}
}
}

It is not really clear what you are trying to do. But maybe this would help:
$customerCategories = array('Casual', 'Trendy');
if( $stmt->columnCount() ){
while( $row = $stmt->fetch( PDO::FETCH_ASSOC )){
$productCategoryRow = $row[ 'Taste_Category' ];
// If it is not working, try uncommenting the next line
// $productCategories = [];
$productCategories = explode( ',', trim( $productCategoryRow ));
$match = "False";
foreach( $productCategories as $productCategory ){
if( in_array( $productCategory, $customerCategories )){
$match = "True";
}
echo $match . ";";
}
}
}
This prints your result on the screen every time a loop is done. Is this what you mean?

If you want the second block of code to do what the first block of code (which works correctly) does, then the second block should look like this -
if ($stmt->columnCount()) {
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$needle =$row["product_var"];
$Match = "False";
if (in_array($needle, $haystack)){
$Match = "True";
}
}
}
You don't need do use the foreach because that is replaced by the while loop in the second block.

I am going to try an solve this. I think the problem is with:
$needles[]=$row["product_var"];
I think this should be:
$needles=$row["product_var"];
The column "product_var" contains an CSV (as you mentioned), so I can make an example like this:
$csv = "jake;john;alex;kim";
An example with brackets ($needles[]):
for($i=0; $i<5; $i++) {
$needles[] = explode(";", $csv);
echo(count($needles).", ");
}
returns:
1, 2, 3, 4, 5,
edit (for more explaining):
if I use print_r I see the array expanding, exactly how it happens in your example:
step 1: it adds an array to $needles with values ('jake','john','alex','kim')
step 2: it adds an array to $needles, so it contains 2x the values ('jake','john','alex','kim')
step 3: it adds an array to $needles, so it contains 3x the values ('jake','john','alex','kim')
etc.
Now without the brackets ($needles):
for($i=0; $i<5; $i++) {
$needles = explode(";", $csv);
echo(count($needles).", ");
}
This returns:
4, 4, 4, 4, 4,
And every time the array simply contains the values ('jake','john','alex','kim') -which is what you want.
Could this explain the "expanding values"? (or am I just doing something really stupid which has nothing to do with your problem??)
edit:
If this is what is going wrong, then you are adding to an array, instead of only using the new array from $row["product_var"] (hope this makes any sense; it seems I am pretty bad at explaining what's happening).

Related

PHP remove duplicate instances of dates in an array

I've tried various permutations of array_unique, and have searched other generic questions here on removing duplicate values from an array, but I can't quite set upon the answer I need. I have an array being passed of dates and values, and only want to view the DATE value once per date.
I'm using this for a Google chart and only want the date labels to show up once for each date. And I don't want to remove it entirely, because I want to be able to plot it on the chart.
So, an example array being passed:
["June 4",30],["June 4",35],["June 5",46],["June 5",38.33],["June 5",12]
And how I want it:
["June 4",30],["",35],["June 5",46],["",38.33],["",12]
Ideas?
Since you're using the data to feed into a google chart, I'm assuming that you know exactly what you need as far as output data. There's already some suggestions above for better ways to structure the data, but that probably won't work directly for a google chart.
How about this?
$data = [["June 4",30],["June 4",35],["June 5",46],["June 5",38.33],["June 5",12]];
$found = array();
foreach ($data as $i => $x) {
if (in_array($x[0], $found)) {
$data[$i][0] = '';
} else {
$found[] = $x[0];
}
}
print_r($data);
Basically, it's just building a list of dates that it's already seen. We loop through the data, and check if we've seen the date... if we have, we clear it from the data, otherwise we save it to the list so it'll be cleared next time.
Here's an alternate solution that only checks for duplicate dates that are consecutive, unlike the first solution that will remove all duplicates. This is probably closer to what you need for charting:
$data = [["June 4",30],["June 4",35],["June 5",46],["June 5",38.33],["June 5",12]];
$last = '';
foreach ($data as $i => $x) {
if ($x[0] == $last) {
$data[$i][0] = '';
} else {
$last = $x[0];
}
}
print_r($data);
In this case, we're just keeping track of the last date we've seen... and if our new date matches that, we clear it.
This is a possible solution for your problem, though I would recommend a re-construction as Patashu & Nikola R stated.
$untrimmed = [["June 4",30],["June 4",35],["June 5",46],["June 5",38.33],["June 5",12]];
$trimmed = stripDates($untrimmed);
function stripDates($dates) {
foreach( $dates as $key=>$date ) {
if ($key>0) {
if ($date[0] === $dates[$key-1][0]) {
$dates[$key][0] = "";
} else if($dates[$key-1][0] === "") {
for ($i = $key-1; $i > -1; $i--) {
if ($date[0] === $dates[$i][0]) $dates[$key][0] = "";
if ($dates[$key] != "") break;
}
}
}
}
return $dates;
}
// Note: This would require dates to be added chronically
//Output: ["June 4",30],["",35],["June 5",46],["",38.33],["",12]
I would recommend something like this:
$unconstructed = [["June 4",30],["June 4",35],["June 5",46],["June 5",38.33],["June 5",12]];
$constructed = constructAssoc($unconstructed);
function constructAssoc($dates) {
$constructed = array();
foreach( $dates as $index=>$date ) {
if (!array_key_exists($date[0], $constructed)) {
$constructed[$date[0]] = array("index"=>$index, "value"=>$date[1]);
} else {
array_push($constructed[$date[0], ["index"=>$index,"value"=>$date[1]]);
}
}
return $constructed;
}
//Output: ["June 4"=> [["index"=>0, "value"=>30], ["index"=>1, "value"=>35]], "June 5"=>[["index"=>2, "value"=>46], ["index"=>3, "value"=>38.33], ["index"=>4, "value"=>12]]]
Note: Added index in recommended solution if a more accurate re-construction is needed.

wildcard array comparison - improving efficiency

I have two arrays that I'm comparing and I'd like to know if there is a more efficient way to do it.
The first array is user submitted values, the second array is allowed values some of which may contain a wildcard in the place of numbers e.g.
// user submitted values
$values = array('fruit' => array(
'apple8756apple333',
'banana234banana',
'apple4apple333',
'kiwi435kiwi'
));
//allowed values
$match = array('allowed' => array(
'apple*apple333',
'banana234banana',
'kiwi*kiwi'
));
I need to know whether or not all of the values in the first array, match a value in the second array.
This is what I'm using:
// the number of values to validate
$valueCount = count($values['fruit']);
// the number of allowed to compare against
$matchCount = count($match['allowed']);
// the number of values passed validation
$passed = 0;
// update allowed wildcards to regular expression for preg_match
foreach($match['allowed'] as &$allowed)
{
$allowed = str_replace(array('*'), array('([0-9]+)'), $allowed);
}
// for each value match against allowed values
foreach($values['fruit'] as $fruit)
{
$i = 0;
$status = false;
while($i < $matchCount && $status == false)
{
$result = preg_match('/' . $match['allowed'][$i] . '/', $fruit);
if ($result)
{
$status = true;
$passed++;
}
$i++;
}
}
// check all passed validation
if($passed === $valueCount)
{
echo 'hurray!';
}
else
{
echo 'fail';
}
I feel like I might be missing out on a PHP function that would do a better job than a while loop within a foreach loop. Or am I wrong?
Update: Sorry I forgot to mention, numbers may occur more than 1 place within the values, but there will only ever be 1 wildcard. I've updated the arrays to represent this.
If you don't want to have a loop inside another, it would be better if you grouped your $match regex.
You could get the whole functionality with a lot less code, which might arguably be more efficient than your current solution:
// user submitted values
$values = array(
'fruit' => array(
'apple8756apple',
'banana234banana',
'apple4apple',
'kiwi51kiwi'
)
);
$match = array(
'allowed' => array(
'apple*apple',
'banana234banana',
'kiwi*kiwi'
)
);
$allowed = '('.implode(')|(',$match['allowed']).')';
$allowed = str_replace(array('*'), array('[0-9]+'), $allowed);
foreach($values['fruit'] as $fruit){
if(preg_match('#'.$allowed.'#',$fruit))
$matched[] = $fruit;
}
print_r($matched);
See here: http://codepad.viper-7.com/8fpThQ
Try replacing /\d+/ in the first array with '*', then do array_diff() between the 2 arrays
Edit: after clarification, here's a more refined approach:
<?php
$allowed = str_replace("*", "\d+", $match['allowed']);
$passed = 0;
foreach ($values['fruit'] as $fruit) {
$count = 0;
preg_replace($allowed, "", $fruit, -1, $count); //preg_replace accepts an array as 1st argument and stores the replaces done on $count;
if ($count) $passed++;
}
if ($passed == sizeof($values['fruit']) {
echo 'hurray!';
} else {
echo 'fail';
}
?>
The solution above does not remove the need for a nested loop, but it merely lets PHP do the inner loop, which may be faster (you should actually benchmark it)

Checking value against an array issue

This is killing me. It seems so simple, but for some reason I cannot get it to work.
Basically what I have is a function that accepts a single string $name as a variable, then it goes through and checks that variable against values in an array. If the variable is found in the array is returns TRUE. This function is called from within a foreach loop in another function (which submits a new $name variable each time.)
I have tried 3 different ways to check the variable against the array but it never works properly.
1.) using another foreach() loop
function check($name) {
$commaSeparatedString = 'First Name, Second Name, Third Name';
$array = explode(",", $commaSeparatedString);
foreach($array as $arrayValue) {
if($arrayValue == $name) {
return TRUE;
}
else {
return FALSE;
}
}
}
2.) using in_array()
function check($name) {
$commaSeparatedString = 'First Name, Second Name, Third Name';
$array = explode(",", $commaSeparatedString);
if(in_array($name, $array, TRUE)) {
return TRUE;
}
else {
return FALSE;
}
}
3.) looping through each value in the array manually and checking for a match
function check($name) {
$commaSeparatedString = 'First Name, Second Name, Third Name';
$array = explode(",", $commaSeparatedString);
$count = count($array);
$i = 0;
while($i<=$count) {
if(isset($array[$i]) && $array[$i] == $name) {
$i++;
echo $i;
return TRUE;
break;
}
else {
$i++;
echo $i;
return FALSE; }
}
}
Here a simplified part of the function where check() is run:
function loopCheck() {
$group = array($obj1, $obj2, $obj3, $obj4, $obj5);
foreach($group as $groupValue) {
$name = $groupValue->name;
$ignore = $this->check($name);
if($ignore == TRUE) { }
else { echo $name; }
}
}
The result for all three variations of check() is the same. It returns TRUE for every $groupValue except the first value of $array. So if the $name of $obj2 was 'First Name' it would return TRUE, but if the $name of $obj3 was 'Second Name' is still returns FALSE. I echoed $i for the third variation at one point and the value consistently stayed at 1, so I know there is some error with that method, but I don't know why the result would be the same using the other 2 methods.
There are several issues with your code. The one that causes the failure of all code, is that you explode by ',', thus leaving a whitespace in your strings. You should explode on ', '.
But the first code is erroneous still: The foreach will return on the first iteration, always, thus checking only the first element. The loop should be:
foreach ($array as $arrayValue) {
if ($arrayValue == $name) {
return true;
}
}
return false;
Same applies to your last code.
The best variant is probably the second, here is a slightly shorter, adjusted variant:
function check($name) {
$commaSeparatedString = 'First Name, Second Name, Third Name';
return in_array($name, explode(', ', $commaSeparatedString), true));
}
You should not put
Return false;
On your else, you should put it after the whole loop.
Also keep in mind that if you wanna explode a, b, c to be [a,b,c] you have to use
explode(", ",$str); //note the space
The problem is that when you make the explode in the check function, some pieces have spaces because there is a space after the comma.
Did you trim() your strings?
It would work on the first string as it starts with "First String" but the second explode()ed string would be "spaceSecond String".
I think any of those techniques will work, but you just have small errors.
I've fixed the first one below. You don't want to return false in an else clause. you want it AFTER the foreach loop, only if it fails to match EVERY time.
function check($name) {
$commaSeparatedString = 'First Name, Second Name, Third Name';
$array = explode(",", $commaSeparatedString);
foreach($array as $arrayValue) {
if($arrayValue == $name) {
return TRUE;
}
}
return false;
}
The in_array version is potentially simpler. Notice that there is no reason for if ($val) {return true;} else {return false;}, just do return $val
function check($name)
{
$csvs = 'First Name, Second Name, Third Name';
return in_array($name, explode(', ', $csv));
//return in_array(trim(strtoupper($name)), array_map('trim', explode(', ', strtoupper($csv))));
//try this second way is kind of over-kill thorough, but try it if you still get false where true is expected
}
You also have to either trim the strings of the array you make or explode(', ' $csv) with a space. Otherwise the elements of array will have a leading space.

PHP, will comparing MD5s of print_r output work for testing results?

I'm using code igniter and trying to write tests for the data returned by my models; CI's testing library is very limited in what it will check for, so I've been doing this:
$test = "My Test Name";
$expected = md5(print_r(array(array('val','other'), array('new','old','blue')), 1));
$result = md5(print_r($this->model->method($data), 1));
if($expected == $result) {
echo "$test [pass]";
}
else {
echo "$test [fail]";
}
Are there any pitfalls to testing this way? Is there a preferred, simple library that works with CI that I didn't find? The built-in testing library won't allow you to check results with this fine a grain so I find it not terribly helpful.
Yes, that should work just fine, as if both are identical the hashes should be identical as well.
However, why not just examine the output of print_r directly? Or, for that matter, use a completely separate tool like PhpUnit for testing?
Assuming the two variables you are comparing are both arrays, have a look at the inbuilt array_diff() function (http://php.net/manual/en/function.array-diff.php). If you need to compare the indexes of the arrays also, use array_diff_assoc() instead.
However, if you want a complete check for testing multi-dimentional arrays (as yours is), here's a function that should do that for you:
function compare_arrays($first_array, $second_array, $compare_keys = false){
/* Test if arrays are actually arrays */
if(!is_array($first_array)){
// Elements are not arrays
if(is_array($second_array)){
// Second array is not array
return false;
}
if($first_array == $second_array){
return true;
}
} elseif(!is_array($second_array)){
// Second array is not array (first is)
return false;
}
/* Test same number of elements */
if(count($first_array) != count($second_array)){
return false;
}
$size = count($first_array);
if($compare_keys){
// Load keys to compare
$first_keys = array_keys($first_array);
$second_keys = array_keys($second_array);
}
for($i = 0; $i < $size; $i++){
if($compare_keys && $first_keys[$i] != $second_keys[$i]){
// Keys do not match
return false;
}
if(is_array($first_array[$i])){
// Element is array
if(!is_array($second_array[$i])){
// Matching element in other array is not array
return false;
}
$result = compare_arrays($first_array[$i], $second_array[$i], $compare_keys);
if(!$result){
// Sub-arrays do not match
return false;
}
// Match - skip iteration
continue;
} elseif(is_array($second_array[$i])){
// Second array element is array (first is not)
return false;
}
// Elements are not arrays
if($first_array[$i] != $second_array[$i]){
return false;
}
}
// Match
return true;
}
Usage:
$br = '<br />';
$array_one = array(4, 5, array(2, 5), 'hello', 'element');
if(compare_arrays($array_one, $array_one)){
echo 'Match 1'.$br;
} else {
echo 'No Match 1'.$br;
}
$array_two = array(5, 5, array(2, 5), 'hello', 'element'); // Slightly different
if(compare_arrays($array_one, $array_two)){
echo 'Match 2'.$br;
} else {
echo 'No Match 2'.$br;
}
/* Output:
Match 1
No Match 2
*/
if($array1 === $array2) {
# the arrays are matched with identical keys and values
# no matter how deeply nested
}
No hashing required.

Check Sessions in an array?

hmm i got a homework, its 2 hours and i still have no clue on it :|
like this
$sessions['lefthand'] = 'apple';
$sessions['righthand'] = '';
$sessions['head'] = 'hat';
$sessions['cloth'] = '';
$sessions['pants'] = '';
// here is the homework function
CheckSession('lefthand,righthand,head,cloth,pants');
we have some string "lefthand,righthand,head,cloth,pants"
question is : " how can we check if the five session is not null or exist and display which session is empty ( if there is an empty session ) if all exist then returns a true ?
empty righthand , pants, and cloth.
this is how i think about it
explode it to arrays
check one bye one if !null id there is a
here is the progress that ive made *edit4 , :)
function CheckSession($sessions){
$all_sessions_exist = true;
$keys = explode(',',$sessions);
$error = array();
// Search for Session that are not exist
foreach ($keys as $key) {
if (!isset($_SESSION[$key]) && empty($_SESSION[$key])) {
echo "no $key</br>";
$all_sessions_exist = false;
}
}
return $all_sessions_exist;
}
Thanks for taking a look
Adam Ramadhan
Seeing as it's homework, you won't get the solution. You're on the right track though. explode() it by the delimiter. You can the loop through it using foreach and use empty() to check if they're set. You can access the sessions like $_SESSION[$key]. Keep an array of the ones that match.
function CheckSession($string){
$all_sessions_exist = true; #this will change if one of the session keys does not exist
$keys = explode(',', $string); #get an array of session keys
foreach($keys as $key){
if(isset($_SESSION[$key])) {
if(!empty($_SESSION[$key]))
echo '$_SESSION['.$key.'] is set and contains "'.$_SESSION[$key].'".'; #existing non-empty session key
else echo '$_SESSION['.$key.'] is set and is empty.' ;
}else {
echo '$_SESSION['.$key.'] is not set.'; #key does not exist
$all_sessions_exist = false; #this will determine if all session exist
}
echo '<br />'; #formatting the output
}
return $all_sessions_exist;
}
Just cleaning up your function
function CheckSession($sessions)
{
$session = explode(',',$sessions);
$return = array();
foreach ($session as $s)
{
$return[$s] = (isset($_SESSION[$s]) && !empty($_SESSION[$s]) ? true : false)
}
return $return;
}
$sessions['lefthand'] = 'apple';
$sessions['righthand'] = '';
$sessions['head'] = 'hat';
$sessions['cloth'] = '';
$sessions['pants'] = '';
And the checking part
// here is the homework function
$Results = CheckSession('lefthand,righthand,head,cloth,pants');
Edit
//$value1= false; // Commented out so does not get set
$value2= false; //Still set to a bool
error_reporting(E_ALL);
empty($value1); // returns false but still strikes E_NOTICE ERROR
empty($value2); // returns false with no error
Note, empty does not trigger an error but this example would take affect an a lot of other php functions.

Categories