Compare order of elements in two arrays - php

I need PHP function to compare order of elements in two arrays. First array is standard which saves correct order, the second is array to compare.
Elements in array to compare can be repeated.
Array to compare can contain not all elements that are in etalon array.
Example:
<?php
// Standard
$standard = array(
'taxonomy',
'post_meta',
'author',
'date',
'post_meta_num'
);
// Valid order
$valid_order = array(
'taxonomy',
'taxonomy',
'post_meta',
'date'
);
// Invalid order, 'author' is before 'post_meta'
$invalid_order = array(
'taxonomy',
'author',
'author',
'post_meta'
);
?>
I tried to find something on StackOverflow, but already existing answers are not compatible with my task. This function works correctly only if array to compare contains all elements from standard.
<?php
function compare( $standard, $to_compare ){
if( ! is_array( $standard ) || ! is_array( $to_compare ) ){
return false;
}
$i = 0;
foreach ( $to_compare as $value ) {
if( $value === $standard[$i] ){
$i++;
}
}
return ( $i == count( $standard ) );
}
?>
In the end the function should return true if order in standard and array to compare is equal and false if is not equal.
Thank you.

Assuming that there can be no boolean values in the arrays, you can do the following:
<?php
/**
* #param string[] $standard
* #param string[] $to_compare
* #return bool
*/
function compare(array $standard, array $to_compare) : bool {
$standard_item = reset($standard);
foreach ($to_compare as $item) {
// Skip standard items assuming they are all optional
while ($item !== $standard_item) {
$standard_item = next($standard);
if ($standard_item === false) {
return false;
}
}
if ($standard_item === false || $item !== $standard_item) {
return false;
}
}
return true;
}
If you want to support false values in the standard array, the code above might be modified so that the the items are referred to by indices, e.g. $standard[$i]. But this approach has its drawback as well -- the keys must be numeric and sequential. For a more generic solution I would probably use an iterator such as ArrayIterator.

you want to use usort()
https://www.php.net/manual/fr/function.usort.php
it would look something like
function custom_sort(&$my_array) {
return usort($my_array, function ($a, $b) {
global $etalon;
$a_key = array_search($a, $etalon);
$b_key = array_search($b, $etalon);
if (($a_key === FALSE) || ($b_key === FALSE) || ($a_key == $b_key)) {
return 0;
}
($a_key < $b_key) ? -1 : 1;
});
}
custom_sort($valid_order);
custom_sort($invalid_order)

Another solution that can help :
// Etalon
$etalon = array(
'taxonomy',
'post_meta',
'author',
'date',
'post_meta_num'
);
// Valid order
$valid_order = array(
'taxonomy',
'taxonomy',
'post_meta',
'date'
);
// Invalid order, 'author' is before 'post_meta'
$invalid_order = array(
'taxonomy',
'author',
'author',
'post_meta'
);
function checkOrder($array , $etalon)
{
$array = array_values(array_unique($array));
$array = array_intersect($array, $etalon );
foreach($array as $key => $value){
if(!in_array($array[$key],$etalon) || array_search($array[$key], $etalon)<$key){
return false;
}
}
return true;
}
var_dump(checkOrder($valid_order,$etalon)); // true
var_dump(checkOrder($invalid_order,$etalon)); // false

A possible solution which I find easy to follow:
class Transition
{
private string $fromValue;
private array $toValues;
public function __construct(string $fromValue, array $toValues)
{
$this->fromValue = $fromValue;
$this->toValues = $toValues;
}
public function getFromValue(): string
{
return $this->fromValue;
}
public function getToValues(): array
{
return $this->toValues;
}
}
function prepareTransitionsMap(array $orderDefinitions): array
{
$transitions = [];
$definitionsCount = count($orderDefinitions);
foreach ($orderDefinitions as $i => $fromValue) {
$toValues = [];
for ($j = $i; $j < $definitionsCount; ++$j) {
$toValues[] = $orderDefinitions[$j];
}
$transitions[$fromValue] = new Transition($fromValue, $toValues);
}
return $transitions;
}
function isArrayOrderValid(array $orderDefinitions, array $valuesToCheck): bool
{
$valuesCount = count($valuesToCheck);
if ($valuesCount === 0) {
return true;
}
$definitionsCount = count($orderDefinitions);
if ($definitionsCount === 0) {
return false;
}
$transitionsMap = prepareTransitionsMap($orderDefinitions);
foreach ($valuesToCheck as $i => $iValue) {
$valueToCheck = $iValue;
// value is no defined at all
if (!array_key_exists($valueToCheck, $transitionsMap)) {
return false;
}
// value is the last in the array
if (!array_key_exists($i + 1, $valuesToCheck)) {
return true;
}
$nextValue = $valuesToCheck[$i + 1];
$transition = $transitionsMap[$valueToCheck];
if (!in_array($nextValue, $transition->getToValues(), true)) {
return false;
}
}
return true;
}
isArrayOrderValid($standard, $valid_order); // true
isArrayOrderValid($standard, $invalid_order); // false

Related

Recursive PHP Function

I am trying to solve this problem to learn logic formulations, but this one's really taken too much of my time already.
Rules are simple, No loops and no built in PHP functions (eg. print_r, is_array.. etc).
This is what I have come up with so far.
function displayArray(array $inputArray, $ctr = 0, $tempArray = array()) {
//check if array is equal to temparray
if($inputArray != $tempArray) {
// check if key is not empty and checks if they are not equal
if($inputArray[$ctr]) {
// set current $tempArray key equal to $inputArray's corresponding key
$tempArray[$ctr] = $inputArray[$ctr];
if($tempArray[$ctr] == $inputArray[$ctr]) {
echo $tempArray[$ctr];]
}
$ctr++;
displayArray($inputArray, $ctr);
}
}
}
This program outputs this:
blackgreen
The problem starts when it reaches the element that is an array
$array = array(
'black',
'green',
array(
'purple',
'orange'
)
);
displayArray($array);
Any tips?
This what the return value is supposed to be: blackgreenpurpleorange
This was fun. I decided to make it work with most data types while I was at it. Just don't throw it any objects or nulls and things should work.
No more # error suppression. Now returns the string instead of echoing it.
I realized too late that isset() is actually a language construct rather than a function, and went with a null termination strategy to determine the end of the arrays.
function concatinateRecursive($array, $i = 0) {
static $s = '';
static $depth = 0;
if ($i == 0) $depth++;
// We reached the end of this array.
if ($array === NULL) {
$depth--;
return true;
}
if ($array === array()) return false; // empty array
if ($array === '') return false; // empty string
if (
$array === (int)$array || // int
$array === (float)$array || // float
$array === true || // true
$array === false || // false
$array === "0" || // "0"
$array == "1" || // "1" "1.0" etc.
(float)$array > 1 || // > "1.0"
(int)$array !== 1 // string
)
{
$s .= "$array";
return false;
}
// Else we've got an array. Or at least something we can treat like one. I hope.
$array[] = NULL; // null terminate the array.
if (!concatinateRecursive($array[$i], 0, $s)) {
$depth--;
return concatinateRecursive($array, ++$i, $s);
}
if ($depth == 1) {
return $s;
}
}
$array = array(
'black',
'green',
array(
'purple',
'orange'
)
);
echo concatinateRecursive($array);
blackgreenpurpleorange
Live demo
What about this? You must check if it is array or not.
function display_array(&$array, $index=0) {
if (count($array)<=$index) return;
if (is_array($array[$index])) {
echo '[ ';
display_array($array[$index]);
echo '] ';
}
else
echo "'" . $array[$index] . "' ";
display_array($array, $index+1);
}
// Try:
// $a = ['black', 'green', ['purple', 'orange'], 'beer', ['purple', ['purple', 'orange']]];
// display_array($a);
// Output:
// 'black' 'green' [ 'purple' 'orange' ] 'beer' [ 'purple' [ 'purple' 'orange' ] ]
try this have to use some inbuilt function like isset and is_array but its a complete working recursive method without using loop.
function displayArray(array $inputArray, $ctr = 0) {
if(isset($inputArray[$ctr]))
{
if(is_array($inputArray[$ctr]))
{
return displayArray($inputArray[$ctr]);
}
else
{
echo $inputArray[$ctr];
}
}
else
{
return;
}
$ctr++;
displayArray($inputArray, $ctr);
}
$array = array(
'black',
'green',
array(
'purple',
'orange'
)
);
displayArray($array);
OUTPUT :
blackgreenpurpleorange
DEMO
complete answer
$myarray = array(
'black',
'green',
array(
'purple',
'orange'
)
);
function printAll($a) {
if (!is_array($a)) {
echo $a, ' ';
return;
}
foreach($a as $k => $value) {
if($k<10){
//printAll($k);
printAll($value);
}
}
}
printAll($myarray);

check if association keys exists in a particular array

I'm trying to build a function that checks if there's a value at a particular location in an array:
function ($array, $key) {
if (isset($array[$key]) {
return true;
}
return false;
}
but how can I accomplish this in a multi array? say I want to check if a value is set on $array[test1][test2]
Pass an array of keys, and recurse into the objects you find along the way:
function inThere($array, $keys)
{
$key = $keys; // if a single key was passed, use that
$rest = array();
// else grab the first key in the list
if (is_array($keys))
{
$key = $keys[0];
$rest = array_slice($keys, 1);
}
if (isset($array[$key]))
{
if (count($rest) > 0)
return inThere($array[$key], $rest);
else
return true;
}
return false;
}
So, for:
$foo = array(
'bar' => array( 'baz' => 1 )
);
inThere($foo, 'bar'); // == true
inThere($foo, array('bar')); // == true
inThere($foo, array('bar', 'baz')); // == true
inThere($foo, array('bar', 'bazX')); // == false
inThere($foo, array('barX')); // == false
Here is a non-recursive way to check for if a multi-level hashtable is set.
// $array it the container you are testing.
// $keys is an array of keys that you want to check. [key1,key2...keyn]
function ($array, $keys) {
// Is the first key set?
if (isset($array[$key]) {
// Set the test to the value of the first key.
$test = $array[$key];
for($i = 1; $i< count($keys); $i++){
if (!isset($test[$keys[$i]]) {
// The test doesn't have a matching key, return false
return false;
}
// Set the test to the value of the current key.
$test = $test[$keys[$i]];
}
// All keys are set, return true.
return true;
} else {
// The first key doesn't exist, so exit.
return false;
}
}
While I probably wouldn't build a function for this, perhaps you can put better use to it:
<?php
function mda_isset( $array )
{
$args = func_get_args();
unset( $args[0] );
if( count( $args ) > 0 )
{
foreach( $args as $x )
{
if( array_key_exists( $x, $array ) )
{
$array = $array[$x];
}
else
{
return false;
}
}
if( isset( $array ) )
{
return true;
}
}
return false;
}
?>
You can add as many arguments as required:
// Will Test $array['Test1']['Test2']['Test3']
$bool = mda_isset( $array, 'Test1', 'Test2', 'Test3' );
It will first check to make sure the array key exists, set the array to that key, and check the next key. If the key is not found, then you know it doesn't exist. If the keys are all found, then the value is checked if it is set.
I'm headed out, but maybe this. $keys should be an array even if one, but you can alter the code to check for an array of keys or just one:
function array_key_isset($array, $keys) {
foreach($keys as $key) {
if(!isset($array[$key])) return false;
$array = $array[$key];
}
return true;
}
array_key_isset($array, array('test1','test2'));
There's more universal method but it might look odd at first:
Here we're utilizing array_walk_recursive and a closure function:
$array = array('a', 'b', array('x', 456 => 'y', 'z'));
$search = 456; // search for 456
$found = false;
array_walk_recursive($array, function ($value, $key) use ($search, &$found)
{
if ($key == $search)
$found = true;
});
if ($found == true)
echo 'got it';
The only drawback is that it will iterate over all values, even if it's already found the key. This is good for small array though

Returning array inside function that returns another value

Why does the following script does not work?
$arr = array();
function collect( $array , $val) {
$array[] = $val;
return $array;
}
function checkFoo( $s ) {
$valid = true;
if ( strlen( $s ) === 0 ) {
$isValid = false;
collectFoo( $arr , $s );
}
return $valid;
}
function checkBar( $t ) {
$valid = true;
if ( strlen( $s ) != 10 ) {
$isValid = false;
collectFoo( $arr , $t );
}
return $valid;
}
if ( checkFoo( $that ) && checkBar( $this ) ) {
echo "success";
} else {
print_r( $error );
}
I always get
Notice: Undefined variable: error in /my.php on line 12
where line 12 resembles the second occurrence of collect(...);
I know that a function can only return one value, but what if a function returns something in a function that returns something? Because collect returns $array inside checkBar that returns $valid.
you are using a global variable ($arr), so you need do declare it as such. example:
function checkFoo( $s ) {
global $arr; // declare $arr to be usable inside this function scope
$valid = true;
if ( strlen( $s ) === 0 ) {
$isValid = false;
collectFoo( $arr , $s );
}
return $valid;
}
You aren't declaring your $arr variable as global inside the functions that use it. You can find some info about that here.
Also, it doesn't look like you are actually using the return value of collect, so I'd say that your problem is that either checkBar or checkFoo are returning false, which makes you fall through to the print_r function, with a $error variable that you have not initialized.
Finally, it seems to me like your collect function is not actually doing anything to the $arr variable, because you are not passing it to collect by reference. you can read up on passing variables by reference here. There is also a S.O. question about this here.
Edited
You need more practice, because totally your code is incorrect !
you must changes this parts of your codes :
$arr = array();
function collectFoo( $arr , $val) {
global $arr;
$arr[] = $val;
}
function checkFoo( $s ) {
global $arr;
$valid = false;
if ( strlen( $s ) === 0 ) {
$valid = false;
collectFoo( $arr , $s );
}
return $valid;
}
function checkBar( $t ) {
global $arr;
$valid = true;
if ( strlen( $t ) != 10 ) {
$valid = false;
collectFoo( $arr , $t );
}
return $valid;
}
$a = checkFoo( $that );
$b = checkBar( $this );
if ( $a && $b ) {
echo 'Success !';
} else {
print_r( $err );
}

best way to check a empty array?

How can I check an array recursively for empty content like this example:
Array
(
[product_data] => Array
(
[0] => Array
(
[title] =>
[description] =>
[price] =>
)
)
[product_data] => Array
(
[1] => Array
(
[title] =>
[description] =>
[price] =>
)
)
)
The array is not empty but there is no content. How can I check this with a simple function?
Thank!!
function is_array_empty($InputVariable)
{
$Result = true;
if (is_array($InputVariable) && count($InputVariable) > 0)
{
foreach ($InputVariable as $Value)
{
$Result = $Result && is_array_empty($Value);
}
}
else
{
$Result = empty($InputVariable);
}
return $Result;
}
If your array is only one level deep you can also do:
if (strlen(implode('', $array)) == 0)
Works in most cases :)
Solution with array_walk_recursive:
function empty_recursive($value)
{
if (is_array($value)) {
$empty = TRUE;
array_walk_recursive($value, function($item) use (&$empty) {
$empty = $empty && empty($item);
});
} else {
$empty = empty($value);
}
return $empty;
}
Assuming the array will always contain the same type of data:
function TestNotEmpty($arr) {
foreach($arr as $item)
if(isset($item->title) || isset($item->descrtiption || isset($item->price))
return true;
return false;
}
Short circuiting included.
function hasValues($input, $deepCheck = true) {
foreach($input as $value) {
if(is_array($value) && $deepCheck) {
if($this->hasValues($value, $deepCheck))
return true;
}
elseif(!empty($value) && !is_array($value))
return true;
}
return false;
}
Here's my version. Once it finds a non-empty string in an array, it stops. Plus it properly checks on empty strings, so that a 0 (zero) is not considered an empty string (which would be if you used empty() function). By the way even using this function just for strings has proven invaluable over the years.
function isEmpty($stringOrArray) {
if(is_array($stringOrArray)) {
foreach($stringOrArray as $value) {
if(!isEmpty($value)) {
return false;
}
}
return true;
}
return !strlen($stringOrArray); // this properly checks on empty string ('')
}
If anyone stumbles on this question and needs to check if the entire array is NULL, meaning that each pair in the array is equal to null, this is a handy function. You could very easily modify it to return true if any variable returns NULL as well. I needed this for a certain web form where it updated users data and it was possible for it to come through completely blank, therefor not needing to do any SQL.
$test_ary = array("1"=>NULL, "2"=>NULL, "3"=>NULL);
function array_empty($ary, $full_null=false){
$null_count = 0;
$ary_count = count($ary);
foreach($ary as $value){
if($value == NULL){
$null_count++;
}
}
if($full_null == true){
if($null_count == $ary_count){
return true;
}else{
return false;
}
}else{
if($null_count > 0){
return true;
}else{
return false;
}
}
}
$test = array_empty($test_ary, $full_null=true);
echo $test;
$arr=array_unique(array_values($args));
if(empty($arr[0]) && count($arr)==1){
echo "empty array";
}
Returns TRUE if passed a variable other than an array, or if any of the nested arrays contains a value (including falsy values!). Returns FALSE otherwise.
Short circuits.
function has_values($var) {
if (is_array($var)) {
if (empty($var)) return FALSE;
foreach ($var as $val) {
if(has_values($val)) return TRUE;
}
return FALSE;
}
return TRUE;
}
Here's a good utility function that will return true (1) if the array is empty, or false (0) if not:
function is_array_empty( $mixed ) {
if ( is_array($mixed) ) {
foreach ($mixed as $value) {
if ( ! is_array_empty($value) ) {
return false;
}
}
} elseif ( ! empty($mixed) ) {
return false;
}
return true;
}
For example, given a multidimensional array:
$products = array(
'product_data' => array(
0 => array(
'title' => '',
'description' => null,
'price' => '',
),
),
);
You'll get a true value returned from is_array_empty(), since there are no values set:
var_dump( is_array_empty($products) );
View this code interactively at: http://codepad.org/l2C0Efab
I needed a function to filter an array recursively for non empty values.
Here is my recursive function:
function filterArray(array $array, bool $keepNonArrayValues = false): array {
$result = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$value = $this->filterArray($value, $keepNonArrayValues);
}
// keep non empty values anyway
// otherwise only if it is not an array and flag $keepNonArrayValues is TRUE
if (!empty($value) || (!is_array($value) && $keepNonArrayValues)) {
$result[$key] = $value;
}
}
return array_slice($result, 0)
}
With parameter $keepNonArrayValues you can decide if values such 0 (number zero), '' (empty string) or false (bool FALSE) shout be kept in the array. In other words: if $keepNonArrayValues = true only empty arrays will be removed from target array.
array_slice($result, 0) has the effect that numeric indices will be rearranged (0..length-1).
Additionally, after filtering the array by this function it can be tested with empty($filterredArray).

How to check if a certain part of the array exists in another array?

I have an two associative arrayes and I want to check if
$array1["foo"]["bar"]["baz"] exists in $array2["foo"]["bar"]["baz"]
The values doesn't matter, just the "path".
Does array_ intersect_ assoc do what I need?
If not how can I write one myself?
Try this:
<?php
function array_path_exists(&$array, $path, $separator = '/')
{
$a =& $array;
$paths = explode($separator, $path);
$i = 0;
foreach ($paths as $p) {
if (isset($a[$p])) {
if ($i == count($paths) - 1) {
return TRUE;
}
elseif(is_array($a[$p])) {
$a =& $a[$p];
}
else {
return FALSE;
}
}
else {
return FALSE;
}
$i++;
}
}
// Test
$test = array(
'foo' => array(
'bar' => array(
'baz' => 1
)
),
'bar' => 1
);
echo array_path_exists($test, 'foo/bar/baz');
?>
If you only need to check if the keys exist you could use a simple if statement.
<?php
if (isset($array1["foo"]["bar"]["baz"]) && isset($array2["foo"]["bar"]["baz"]
)) {
//exists
}

Categories