I have a multidimensional array. I need a function that checks if a specified key exists.
Let's take this array
$config['lib']['template']['engine'] = 'setted';
A function should return true when I call it with:
checkKey('lib','template','engine');
//> Checks if isset $config['lib']['template']['engine']
Note that my array isn't only 3 dimensional. It should be able to check even with only 1 dimension:
checkKey('genericSetting');
//> Returns false becase $c['genericSetting'] isn't setted
At the moment I am using an awful eval code, I would like to hear suggest :)
function checkKey($array) {
$args = func_get_args();
for ($i = 1; $i < count($args); $i++) {
if (!isset($array[$args[$i]]))
return false;
$array = &$array[$args[$i]];
}
return true;
}
Usage:
checkKey($config, 'lib', 'template', 'engine');
checkKey($config, 'genericSetting');
I created the following two functions to do solve the same problem you are having.
The first function check is able to check for one/many keys at once in an array using a dot notation. The get_value function allows you to get the value from an array or return another default value if the given key doesn't exist. There are samples at the bottom for basic usage. The code is mostly based on CakePHP's Set::check() function.
<?php
function check($array, $paths = null) {
if (!is_array($paths)) {
$paths = func_get_args();
array_shift($paths);
}
foreach ($paths as $path) {
$data = $array;
if (!is_array($path)) {
$path = explode('.', $path);
}
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
$key = intval($key);
}
if ($i === count($path) - 1 && !(is_array($data) && array_key_exists($key, $data))) {
return false;
}
if (!is_array($data) || !array_key_exists($key, $data)) {
return false;
}
$data =& $data[$key];
}
}
return true;
}
function get_value($array, $path, $defaultValue = FALSE) {
if (!is_array($path))
$path = explode('.', $path);
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0')
$key = intval($key);
if ($i === count($path) - 1) {
if (is_array($array) && array_key_exists($key, $array))
return $array[$key];
else
break;
}
if (!is_array($array) || !array_key_exists($key, $array))
break;
$array = & $array[$key];
}
return $defaultValue;
}
// Sample usage
$data = array('aaa' => array(
'bbb' => 'bbb',
'ccc' => array(
'ddd' => 'ddd'
)
));
var_dump( check($data, 'aaa.bbb') ); // true
var_dump( check($data, 'aaa.bbb', 'aaa.ccc') ); // true
var_dump( check($data, 'zzz') ); // false
var_dump( check($data, 'aaa.bbb', 'zzz') ); // false
var_dump( get_value($data, 'aaa.bbb', 'default value') ); // "bbb"
var_dump( get_value($data, 'zzz', 'default value') ); // "default value"
Related
I have a list of strings that each contain dot notated values. I want to convert this list into an associative array with each segment of the dot notation as a key in the appropriate nesting level. The deepest level of nesting should have a value of boolean true. There's no limit to the number or dot segments a string can contain so the code needs to do some kind of recursion to achieve the goal.
Input example:
[
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
]
Required output:
[
'foo' => [
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => true,
'quux' => true,
],
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => [
'quux' => true
]
]
foreach only solution.
function convert(array $input)
{
$r = [];
foreach ($input as $dotted) {
$keys = explode('.', $dotted);
$c = &$r[array_shift($keys)];
foreach ($keys as $key) {
if (isset($c[$key]) && $c[$key] === true) {
$c[$key] = [];
}
$c = &$c[$key];
}
if ($c === null) {
$c = true;
}
}
return $r;
}
If you call your starting array of strings $input, the $output array in the code below is what you want
$input=[...]; //original array
$output=[]; //holds the results;
foreach ($input as $line){
$keys = explode('.',$line);//break each section into a key
$val = true; //holds next value to add to array
$localArray = []; //holds the array for this input line
for($i=count($keys)-1; $i>=0; $i--){ //go through input line in reverse order
$localArray = [$keys[$i]=>$val]; //store previous value in array
$val = $localArray; //store the array we just built. it will be
//the value in the next loop
}
$output = array_merge_recursive($output,$localArray);
}
Live demo
You can do this using pointer and some logic.
<?php
$test = [
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
];
class Test
{
public $result = [];
public function check($array){
foreach ($array as $value) {
$this->change($value);
}
}
public function change($string)
{
$explodedValues = explode('.',$string);
$pointerVariable = &$this->result;
foreach ($explodedValues as $explodedValue) {
if(!is_array($pointerVariable)){
$pointerVariable = [];
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
} else if(isset($pointerVariable[$explodedValue])){
$pointerVariable = &$pointerVariable[$explodedValue];
} else {
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
}
}
}
}
$obj = new Test();
$obj->check($test);
print_r($obj->result);
Hope this will help
function convert($aa) {
$zz = [];
foreach( $aa as $value ) {
$bb = explode('.',$value);
$cc = count($bb);
for( $ii = 0, $yy = &$zz; $ii < $cc ; $ii++ ) {
$key = $bb[$ii];
if ( array_key_exists($key,$yy) === false || is_array($yy[$key]) === false ) {
$yy[$key] = [];
}
$yy = &$yy[$key];
}
$key = $bb[$cc-1];
if ( array_key_exists($key,$yy) === false ) {
$yy[$key] = true;
}
}
return $zz;
}
Search all navigation items:
$navitems_locations_array = array_unique(array_column($projects, 'location'));
asort($navitems_locations_array);
This works on my localhost but not on live. My server doesn't support it. And I'm using lower version of PHP.
And this is the error:
Fatal error: Call to undefined function array_column()
Any alternative with the same output?
Here is my sample arrray list:
Array( [ name ] => P1 [ description ] => Lorem Ipsum [ location ] => air city[ type ] => high rise[ status ] => new [ tags ] => [ page_url ] => project-1[ image ] => projects/images/p1/project-image.jpg[ timeline ] => )
Try this hope it will help you out. Here we are using array_map for gathering locations.
$locations=array();
array_map(function($value) use (&$locations) {
$locations[]=$value["location"];//we are gathering index location
}, $yourArray);//add your array here in place of $yourArray.
print_r(array_unique($locations));
It is usually useful to read User Contributed Notes on the PHP Manual.
For example below the array_column article, there are several variants of implementations for versions lower than PHP 5.5. Here is the most popular one:
if(!function_exists("array_column"))
{
function array_column($array,$column_name)
{
return array_map(function($element) use($column_name){return $element[$column_name];}, $array);
}
}
But it doesn't support $index_key, so you can find another one there, which does:
if (!function_exists('array_column')) {
function array_column($input, $column_key, $index_key = null) {
$arr = array_map(function($d) use ($column_key, $index_key) {
if (!isset($d[$column_key])) {
return null;
}
if ($index_key !== null) {
return array($d[$index_key] => $d[$column_key]);
}
return $d[$column_key];
}, $input);
if ($index_key !== null) {
$tmp = array();
foreach ($arr as $ar) {
$tmp[key($ar)] = current($ar);
}
$arr = $tmp;
}
return $arr;
}
}
You can find a couple more alternatives in the User Contributed Notes.
Add your own function array_column if you PHP version does not support it:
if (!function_exists('array_column')) {
function array_column($input = null, $columnKey = null, $indexKey = null)
{
$argc = func_num_args();
$params = func_get_args();
if ($argc < 2) {
trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
return null;
}
if (!is_array($params[0])) {
trigger_error(
'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
E_USER_WARNING
);
return null;
}
if (!is_int($params[1])
&& !is_float($params[1])
&& !is_string($params[1])
&& $params[1] !== null
&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
) {
trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
return false;
}
if (isset($params[2])
&& !is_int($params[2])
&& !is_float($params[2])
&& !is_string($params[2])
&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
) {
trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
return false;
}
$paramsInput = $params[0];
$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
$paramsIndexKey = null;
if (isset($params[2])) {
if (is_float($params[2]) || is_int($params[2])) {
$paramsIndexKey = (int) $params[2];
} else {
$paramsIndexKey = (string) $params[2];
}
}
$resultArray = array();
foreach ($paramsInput as $row) {
$key = $value = null;
$keySet = $valueSet = false;
if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
$keySet = true;
$key = (string) $row[$paramsIndexKey];
}
if ($paramsColumnKey === null) {
$valueSet = true;
$value = $row;
} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
$valueSet = true;
$value = $row[$paramsColumnKey];
}
if ($valueSet) {
if ($keySet) {
$resultArray[$key] = $value;
} else {
$resultArray[] = $value;
}
}
}
return $resultArray;
}
}
Reference:
I have a list of strings that each contain dot notated values. I want to convert this list into an associative array with each segment of the dot notation as a key in the appropriate nesting level. The deepest level of nesting should have a value of boolean true. There's no limit to the number or dot segments a string can contain so the code needs to do some kind of recursion to achieve the goal.
Input example:
[
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
]
Required output:
[
'foo' => [
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => true,
'quux' => true,
],
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => [
'quux' => true
]
]
foreach only solution.
function convert(array $input)
{
$r = [];
foreach ($input as $dotted) {
$keys = explode('.', $dotted);
$c = &$r[array_shift($keys)];
foreach ($keys as $key) {
if (isset($c[$key]) && $c[$key] === true) {
$c[$key] = [];
}
$c = &$c[$key];
}
if ($c === null) {
$c = true;
}
}
return $r;
}
If you call your starting array of strings $input, the $output array in the code below is what you want
$input=[...]; //original array
$output=[]; //holds the results;
foreach ($input as $line){
$keys = explode('.',$line);//break each section into a key
$val = true; //holds next value to add to array
$localArray = []; //holds the array for this input line
for($i=count($keys)-1; $i>=0; $i--){ //go through input line in reverse order
$localArray = [$keys[$i]=>$val]; //store previous value in array
$val = $localArray; //store the array we just built. it will be
//the value in the next loop
}
$output = array_merge_recursive($output,$localArray);
}
Live demo
You can do this using pointer and some logic.
<?php
$test = [
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
];
class Test
{
public $result = [];
public function check($array){
foreach ($array as $value) {
$this->change($value);
}
}
public function change($string)
{
$explodedValues = explode('.',$string);
$pointerVariable = &$this->result;
foreach ($explodedValues as $explodedValue) {
if(!is_array($pointerVariable)){
$pointerVariable = [];
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
} else if(isset($pointerVariable[$explodedValue])){
$pointerVariable = &$pointerVariable[$explodedValue];
} else {
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
}
}
}
}
$obj = new Test();
$obj->check($test);
print_r($obj->result);
Hope this will help
function convert($aa) {
$zz = [];
foreach( $aa as $value ) {
$bb = explode('.',$value);
$cc = count($bb);
for( $ii = 0, $yy = &$zz; $ii < $cc ; $ii++ ) {
$key = $bb[$ii];
if ( array_key_exists($key,$yy) === false || is_array($yy[$key]) === false ) {
$yy[$key] = [];
}
$yy = &$yy[$key];
}
$key = $bb[$cc-1];
if ( array_key_exists($key,$yy) === false ) {
$yy[$key] = true;
}
}
return $zz;
}
How to know if a given string starts with a defied set of words?
$allowed = array("foo", "bar");
pseudocode:
$boolean = somefunction($allowed,'food');
$boolean should be TRUE
function doesStringStartWith($string, $startWithOptions)
{
foreach($startWithOptions as $option)
{
if(substr($string, 0, strlen($option)) == $option) // comment this for case-insenstive
// uncomment this for case-insenstive: if(strtolower(substr($string, 0, strlen($option))) == strtolower($option))
{
return true;
}
}
return false;
}
$result = doesStringStartWith('food', array('foo', 'bar'));
function somefunction($allowed, $word) {
$result = array_filter(
$allowed,
function ($value) use ($word) {
return strpos($word, $value) === 0;
}
);
return (boolean) count($result);
}
$boolean = somefunction($allowed,'food');
If you know that all of your prefixes are the same length you could do this:
if ( in_array( substr($input,0,3), $allowed ) {
// your code
}
I came up with the following function:
function testPos($allowed,$s) {
$a = 0;
while($a < count($allowed)) {
if(strpos($s,$allowed[$a]) === 0) {
return true;
}
$a++;
}
}
Now you can try:
$allowed = array('foo','bar');
echo testPos($allowed,'food');
I have a multidimensional array. I need a function that checks if a specified key exists.
Let's take this array
$config['lib']['template']['engine'] = 'setted';
A function should return true when I call it with:
checkKey('lib','template','engine');
//> Checks if isset $config['lib']['template']['engine']
Note that my array isn't only 3 dimensional. It should be able to check even with only 1 dimension:
checkKey('genericSetting');
//> Returns false becase $c['genericSetting'] isn't setted
At the moment I am using an awful eval code, I would like to hear suggest :)
function checkKey($array) {
$args = func_get_args();
for ($i = 1; $i < count($args); $i++) {
if (!isset($array[$args[$i]]))
return false;
$array = &$array[$args[$i]];
}
return true;
}
Usage:
checkKey($config, 'lib', 'template', 'engine');
checkKey($config, 'genericSetting');
I created the following two functions to do solve the same problem you are having.
The first function check is able to check for one/many keys at once in an array using a dot notation. The get_value function allows you to get the value from an array or return another default value if the given key doesn't exist. There are samples at the bottom for basic usage. The code is mostly based on CakePHP's Set::check() function.
<?php
function check($array, $paths = null) {
if (!is_array($paths)) {
$paths = func_get_args();
array_shift($paths);
}
foreach ($paths as $path) {
$data = $array;
if (!is_array($path)) {
$path = explode('.', $path);
}
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
$key = intval($key);
}
if ($i === count($path) - 1 && !(is_array($data) && array_key_exists($key, $data))) {
return false;
}
if (!is_array($data) || !array_key_exists($key, $data)) {
return false;
}
$data =& $data[$key];
}
}
return true;
}
function get_value($array, $path, $defaultValue = FALSE) {
if (!is_array($path))
$path = explode('.', $path);
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0')
$key = intval($key);
if ($i === count($path) - 1) {
if (is_array($array) && array_key_exists($key, $array))
return $array[$key];
else
break;
}
if (!is_array($array) || !array_key_exists($key, $array))
break;
$array = & $array[$key];
}
return $defaultValue;
}
// Sample usage
$data = array('aaa' => array(
'bbb' => 'bbb',
'ccc' => array(
'ddd' => 'ddd'
)
));
var_dump( check($data, 'aaa.bbb') ); // true
var_dump( check($data, 'aaa.bbb', 'aaa.ccc') ); // true
var_dump( check($data, 'zzz') ); // false
var_dump( check($data, 'aaa.bbb', 'zzz') ); // false
var_dump( get_value($data, 'aaa.bbb', 'default value') ); // "bbb"
var_dump( get_value($data, 'zzz', 'default value') ); // "default value"