Compare PHP arrays - php

I know there are a lot of these, but I'm looking for something slightly different.
A straight diff won't work for me.
I have a list (array) of allowed tags i.e. ["engine","chassis","brakes","suspension"]
Which I want to check with the list the user has entered. Diff won't work, because the user may not enter all the options i.e. ["engine"] but I still want this to pass. What I want to happen is fail if they put something like "banana" in the list.

You can use array_intersect(), and check the size of the resulting array with the size of the input array. If the result is smaller, then the input contains one or more items not in the 'allowed' array. If its size is equal, all items in it are in the user's input, so you can use the array do do whatever you want.

Use array_diff();
$allowed=array("engine","chassis","brakes","suspension");
$user=array("engine","brakes","banana");
$unallowed=array_diff($user, $allowed);
print_r($unallowed);
This will return banana, as it is in $user, but not in $allowed.

array_diff(): http://nl.php.net/array_diff
Returns an array containing all the entries from array1 that are not present in any of the other arrays.
if ( array_diff( $input, $allowed ) ) {
// error
}

$allowed_tags = array("engine","chassis","brakes","suspension");
$user_iput = array("engine", "suspension", "banana");
foreach($user_input as $ui) {
if(!in_array($ui, $allowed_tags)) {
//user entered an invalid tag
}
}

Related

in_array() not working properly using $_POST

I'm trying to let the user select multiple countries and then check if the countries he selected are in the array of allowed countries using PHP
I used javascript for multiple options , this is why it is not found in the tags. Otherwise for testing you can put some custom options between the tags. (country codes in this case)
HTML:
<form method="POST" action="">
<select name="countries[]" class="country" multiple></select>
<button type="submit">Submit</button>
</form>
PHP:
<?php
if(isset($_POST['countrylist'])) {
$arr = array("FR","GF","PF","TF","GE","DE","GI","GR","GL","HK","IS","IN","ID","IE","IL","IT","JP","JO","KZ","KR","LV","LB","LI","LT","LU","MK","MX","MD","MC","MN","MS","MA","NP","NL","NZ","NO","PK"); //These are allowed country codes which are sent by the user the same way as above using POST request. The sent array looks fine as you can see in the var_dump result below.
$array2 = (array) $_POST['countries'];
$array2= array_filter($array2);
if(!in_array($array2, $arr)) {
echo var_dump($array2);
}
}
?>
The in_array() function doesn't seem to work , my var_dump result looks something like this:
array(1) { [0]=> string(2) "FR" } //If I selected France country for example.
$arr is an array of strings. $array2 is an array of strings (just one string in this case).
You are using $array2 as your needle, so you are asking "Does this array appear in this set of strings", which is doesn't, because the array isn't a string.
You would need to do something more like in_array($array2[0], $arr).
That, of course, misses the point since you want to accept multiple values, but it is a place to start.
You need to loop over $array2 and test each value in turn until you get to the end or hit a failure state. You might also look at using array_filter again, this time passing a callback.
in_array() works just fine but you call with wrong arguments. The first argument that it expects is a value to search in the second argument (that must be an array).
Since you have an array of values that you want to search you can either try to search each value at a time using in_array() or you can use array_intersect() to find the values that are present in both arrays.
Your choice will be influenced by the way you decide to handle the unexpected values in the input.
Example using in_array():
$knownValues = ['FR', 'GF', 'PF', 'TF'];
if (! is_array($_POST['countries'])) {
// The input does not look valid; there is nothing to process
// Handle this in the most appropriate way for your application and stop here
// return/exit/throw/whatever
}
$valid = [];
foreach ($_POST['countries'] as $countryId) {
if (! in_array($countryId, $knownValues)) {
// $countryId is not a valid/known country code
// Do something with it (report it, ignore it etc.)
continue;
}
$valid[] = $countryId;
}
// Here $valid contains the input values that are present in $knownValues
Example using array_intersect():
$knownValues = ['FR', 'GF', 'PF', 'TF'];
if (! is_array($_POST['countries'])) {
// The input does not look valid; there is nothing to process
// Handle this in the most appropriate way for your application and stop here
// return/exit/throw/whatever
}
$valid = array_intersect($_POST['countries'], $knownValues);
$invalid = array_diff($_POST['countries'], $knownValues);
// The known/valid and unknown/invalid values have been separated
// Do whatever you want with them.

Check if any values in array are equal to each other

I'm validating a form that submits up to 3 different id's depending on what the user selects.
I've put them into an array:
$submitted_genres = array($_POST['genre1'], $_POST['genre2'], $_POST['genre3']);
How I can check to make sure that none of the array values are equal each other?
You could use array_unique() to get an array of all unique values and then compare the size against the original array:
if (count(array_unique($submitted_genres)) !== count($submitted_genres)) {
// there's at least one dupe
}

Sort MySQL query result by a alphanumeric field

I'm querying a table in a db using php. one of the fields is a column called "rank" and has data like the following:
none
1-bronze
2-silver
3-gold
...
10-ambassador
11-president
I want to be able to sort the results based on that "rank" column. any results where the field is "none" get excluded, so those don't factor in. As you can already guess, right now the results are coming back like this:
1-bronze
10-ambassador
11-president
2-silver
3-gold
Of course, I would like for it to be sorted so it is like the following:
1-bronze
2-silver
3-gold
...
10-ambassador
11-president
Right now the query is being returned as an object. I've tried different sort options like natsort, sort, array_multisort but haven't got it to work the way I'm sure it can. I would prefer keeping the results in an object form if possible. I'm passing the data on to a view in the next step. although, it's perfectly acceptable to pass the object to the view and then do the work there. so it's not an issue after all. :)
thank you for your help. i'm hoping I'm making sense.
How about using this function to sort. I assume if you are getting an object you should convert object to array then use this
function arraySubSort($array, $subkey, $sort = 'asort')
{
foreach($array as $key => $value)
{
$temp[$key] = strtolower($value[$subkey]);
}
$sort($temp);
foreach($temp as $key => $value)
{
$result[] = $array[$key];
}
return $result;
}
$data = $your_array;
$field = 'ranks';
arraySubSort($data,$field);
It will sort the array with the field you assign. If you are getting multiple records its a good thing to use to sort.
Get the values in an array an sort it in PHP using natsort()
Natsort
This will work -
SELECT alphanumeric, integer FROM sorting_test ORDER BY LENGTH(alphanumeric), alphanumeric
source - Natural Sort in MySQL

Autofill array with empty data to match another array size

I have 2 sets of arrays:
$dates1 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data1 = array('5','3','7','7','22','18');
// for this dataset, the value on 9/12 is 5
$dates2 = array('9/14','9/15');
$data2 = array('12','1');
As you can see the 2nd dataset has fewer dates, so I need to "autofill" the reset of the array to match the largest dataset.
$dates2 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data2 = array('','','12','1','','');
There will be more than 2 datasets, so I would have to find the largest dataset, and run a function for each smaller dataset to properly format it.
The function I'd create is the problem for me. Not even sure where to start at this point. Also, I can format the date and data arrays differently (multidimensional arrays?) if for some reason that is better.
You can do this in a pretty straightforward manner using some array functions. Try something like this:
//make an empty array matching your maximum-sized data set
$empty = array_fill_keys($dates1,'');
//for each array you wish to pad, do this:
//make key/value array
$new = array_combine($dates2,$data2);
//merge, overwriting empty keys with data values
$new = array_merge($empty,$new);
//if you want just the data values again
$data2 = array_values($new);
print_r($data2);
It would be pretty easy to turn that into a function or put it into a for loop to operate on your array sets. Turning them into associative arrays of key/value pairs would make them easier to work with too I would think.
If datas are related will be painful to scatter them on several array.
The best solution would be model an object with obvious property names
and use it with related accessor.
From your question I haven't a lot of hint of what data are and then I have to guess a bit:
I pretend you need to keep a daily log on access on a website with downloads. Instead of using dates/data1/data2 array I would model a data structure similar to this:
$log = array(
array('date'=>'2011-09-12','accessCount'=>7,'downloadCount'=>3),
array('date'=>'2011-09-13','accessCount'=>9), /* better downloadsCount=>0 though */
array('date'=>'2011-09-15','accessCount'=>7,'downloadCount'=>3)
...
)
Using this data structure I would model a dayCollection class with methods add,remove,get,set, search with all methods returning a day instance (yes, the remove too) and according signature. The day Class would have the standard getter/setter for every property (you can resolve to magic methods).
Depending on the amount of data you have to manipulate you can opt to maintain into the collection just the object data (serialize on store/unserialize on retrieve) or the whole object.
It is difficult to show you some code as the question is lacking of details on your data model.
If you still want to pad your array than this code would be a good start:
$temp = array($dates, $data1, $data2);
$max = max(array_map('count',$temp));
$result = array_map( function($x) use($max) {
return array_pad($x,$max,0);
}, $temp);
in $result you have your padded arrays. if you want to substitute your arrays do a simple
list($dates, $data1, $data2) = array_map(....
You should use hashmaps instead of arrays to associate each date to a data.
Then, find the largest one, cycle through its keys with a foreach, and test the existence of the same key in the small one.
If it doesn't exist, create it with an empty value.
EDIT with code (for completeness, other answers seem definitely better):
$dates_data1 = array('9/12'=>'5', '9/13'=>'3', '9/14'=>'7' /* continued */);
$dates_data2 = array('9/14'=>'12', '9/15'=>'1');
#cycle through each key (date) of the longest array
foreach($dates_data1 as $key => $value){
#check if the key exists in the smallest and add '' value if it does not
if(!isset( $date_data2[$key] )){ $date_data2[$key]=''; }
}

Help with getting first value of array in PHP

Stumped by this one:
I have a function that returns an array of folders in a given directory. When I iterate through the array, I can see all the items. However, if I try to print the first item, I get nothing:
function get_folders($dir) {
return array_filter(scandir($dir), function ($item) use ($dir) {
return (is_dir($dir.'/'.$item) && $item != "." && $item != "..") ;
});
}
$folders = get_folders(".");
$first_folder = $folders[0];
echo $first_folder; // returns blank.
Interestingly, if I don't filter out the "." and "..", then $first_folder does print ".". Can anyone explain this?
As noted in the manual: Array keys are preserved when using array_filter().
The keys are preserved from the call to scandir() which will include the . and .. directories, meaning your filtered array will start at 2 or more (whatever the key is for the first directory).
A simple fix would be to wrap the whole thing in array_values() to get the resultant array having keys starting from 0 and incrementing by one.
return array_values(array_filter(...));
If you don't actually care about the keys, then basic array functions could be of use like key or current.
From the array_filter manual:
Iterates over each value in the input array passing them to the callback function. If the callback function returns true, the current value from input is returned into the result array. Array keys are preserved.
So, scandir($dir) gets you an array with . at key 0, thus if you later filter . out, there will be nothing at key 0 in the result.
If you want to know what the first key of the resulting array is, try the array_keys function.
EDIT: actually, salathe's suggestion to use array_values is better in your case than getting the keys from array_keys.
can you print the folders and see if there is some key you can get the first folder with as opposed to selecting the index of zero? It seems lke $folders[0] would work unless the entries are stored with a different set of indexes / keys.
Try doing a
print_r($folders)
to see if it outputs your first folder and all other folders as expected with the corresponding indexes you're looking for.
array_filter preserves array keys, so if the first two elements have been removed, then the "new first" element has an index of 2. To convert that into an array indexed starting from 0, use array_values:
return array_values(array_filter(scandir($dir), function ($item) use ($dir) {
...
As others have said, array_filter preserves the keys in the array. Given that initially the item '.' has key 0, the filtered array ends up not having any item with key 0 (you can verify this with print_r($folders). So this line:
$first_folder = $folders[0];
ends up generating an E_NOTICE saying that there is no key 0 in the array (having notices turned on would alert you to the cause of the problem immediately, I highly recommend it) and giving $first_folder the value null.
To get the first value of the filtered array, regardless of key, use reset:
$first_folder = reset($folders);
if($first_folder === false) {
echo 'No folders!';
}
else {
echo 'First folder: '.$first_folder;
}

Categories