Shortcut function to populate associative array - php

I'm extracting data and then creating an array like:
$foo = $_POST[ 'foo' ];
$bar = $_POST[ 'bar' ];
$val = array( 'foo' => $foo, 'bar' => $bar );
Is it possible to have a shortcut function?
Something like the following:
$val = array( someFunction( 'foo' ), someFunction( 'bar' ) );
Where:
function someFunction( $name ) {
return( "$name" => $_POST[ $name ] ); // Does not work!
}

What you're trying to do with the Associative Array definition is impossible with the current PHP syntax:
array( someFunction( 'foo' ), someFunction( 'bar' ) );
However, this is the closest thing I can think of:
$_POST = array( 'foo' => 'FOO', 'bar' => 'BAR' );
$var = array();
someFunction( $var, 'foo' );
someFunction( $var, 'bar' );
function someFunction( &$output, $name ) {
$output[$name] = $_POST[$name];
}
Not very sexy and might be confusing also, but will do the job.
Update
Is it what you're looking for -- it is even closer:
$var = ( list($foo, $bar) = $_POST );
// OR
$var = (array) ( list($foo, $bar) = $_POST );
Obviously it's highly depended on the array structure, so it might not be a good idea to use it for $_POST for example.

You're thinking in the right direction, but not nearly big enough!
You want to define an input schema like:
$input = $_POST;
$input_schema = array(
"foo" => "string",
"bar" => "string"
);
validate_input($input, $schema);
From then on you can safely use $input in accordance with your schema.

Related

Searching an array of associate arrays for two matching parameters

I have a loop that builds an array of associative arrays that looks like this:
array(
'foo' => '',
'bar' => '',
'thingys' => array()
)
on each iteration of the loop, I want to search through the array for an associate array that's 'foo' and 'bar' properties match those of the current associate array. If it exists I want to append the thingys property of the current associative array to the match. Otherwise append the entire thing.
I know how to do this with for loops, but I'm wondering if there is a simpler way to do this with an array function. I'm on php 5.3.
Example
<?php
$arr = array(
array(
'foo' => 1,
'bar' => 2,
'thing' => 'apple'
),
array(
'foo' => 1,
'bar' => 2,
'thing' => 'orange'
),
array(
'foo' => 2,
'bar' => 2,
'thing' => 'apple'
),
);
$newArr = array();
for ($i=0; $i < count($arr); $i++) {
$matchFound = false;
for ($j=0; $j < count($newArr); $j++) {
if ($arr[$i]['foo'] === $newArr[$j]['foo'] && $arr[$i]['bar'] === $newArr[$j]['bar']) {
array_push($newArr[$j]['thing'], $arr[$i]['things']);
$matchFound = true;
break;
}
}
if (!$matchFound) {
array_push($newArr,
array(
'foo' => $arr[$i]['foo'],
'bar' => $arr[$i]['bar'],
'things' => array($arr[$i]['thing'])
)
);
}
}
/*Output
$newArr = array(
array(
'foo' => 1,
'bar' => 2,
'things' => array('orange', 'apple')
),
array(
'foo' => 2,
'bar' => 2,
'things' => array('apple')
),
)
*/
?>
I don't know if it is possible through a built-in function, but I think no. Something can be implemented through array_map, but anyway you have to perform a double loop.
I propose you a one-loop solution using a temporary array ($keys) as index of already created $newArr items, based on foo and bar; elements of original array are processed through a foreach loop, and if a $keys element with first key as foo value and second key as bar value exists, then the current thing value is added to the returned key index of $newArr, otherwise a new $newArray element is created.
$newArr = $keys = array();
foreach( $arr as $row )
{
if( isset( $keys[$row['foo']][$row['bar']] ) )
{ $newArr[$keys[$row['foo']][$row['bar']]]['thing'][] = $row['thing']; }
else
{
$keys[$row['foo']][$row['bar']] = array_push( $newArr, $row )-1;
$newArr[$keys[$row['foo']][$row['bar']]]['thing'] = array( $row['thing'] );
}
}
unset( $keys );
3v4l.org demo
Edit: array_map variant
This is the same solution above, using array_map instead of foreach loop. Note that also your original code can be converted in this way.
$newArr = $keys = array();
function filterArr( $row )
{
global $newArr, $keys;
if( isset( $keys[$row['foo']][$row['bar']] ) )
{ $newArr[$keys[$row['foo']][$row['bar']]]['thing'][] = $row['thing']; }
else
{
$keys[$row['foo']][$row['bar']] = array_push( $newArr, $row )-1;
$newArr[$keys[$row['foo']][$row['bar']]]['thing'] = array( $row['thing'] );
}
}
array_map( 'filterArr', $arr );
3v4l.org demo

PHP - Losing the array references out of the function (local to global)

I made a small function to parse and get elements from a multidimensional array by a string written in a Unix-like path syntax.
function array_get($path, &$array) {
$keys = preg_split('/[\/\\\]+/', $path, null, PREG_SPLIT_NO_EMPTY);
$current = trim(array_shift($keys));
if (is_array($array) && array_key_exists($current, $array)) {
$path = implode("/", $keys);
if (empty($path)) {
// (Place the code here, see below)
return $array[$current];
}
return array_get($path, $array[$current]);
}
return false;
}
So if I got a simple array like this
$arr = array(
"A" => array(
"X" => array(),
"Y" => array(),
"Z" => array()
),
"B" => array(
"X" => array(),
"Y" => array(),
"Z" => array()
),
"C" => array(
"X" => array(),
"Y" => array(),
"Z" => array()
)
);
and I wish to fill it within some entries like these
$arr['A']['Z'][] = "foo";
$arr['A']['Z'][] = "bar";
I would do the same job using the following statements:
$var = array_get("A/Z", $arr);
$var[] = "foo";
$var[] = "bar";
But something went wrong.
If you try to run the code you will notice that going out of the local scope the references to the passed array will be lost.
If you wish to run a test, you can replace the placeholder comment line inside the function with these two code lines:
$array[$current][] = "foo";
$array[$current][] = "bar";
then you will see that the function would perform actually its own job.
Is there a way to maintain the references in output?
From the documentation, you can specify you want to return a reference by using the & character before the function name AND the function call.
<?php
function &foo(&$arr) {
return $arr[0];
}
$a = [[]];
$b = &foo($a);
$b[0] = 'bar';
print_r($a); /* outputs [ [ 'bar' ] ] */
You can return references.
But I find your approach really cumbersome, and it will lead to misbehaviours / maintainability / readability issues very soon.

Check for key-value pair in multidimensional array

I have the follow array:
Array
(
[0] => Array
(
[type] => foo
)
[1] => Array
(
[type] => bar
)
[2] => Array
(
[type] => bar
)
)
and need to know if exists one or more type which value is bar, without do this:
foreach ($arrayOfTypes as $type) {
if ($type['type'] == 'bar')
{
// Stuff here
}
}
(Only for learning purposes)
I'd go with array_filter();
$filteredArray = array_filter($stuff, function($item){
return $item['type'] == 'bar';
});
if( count($filteredArray) > 0 ){
echo 'There was a bar item in array.';
}else{
echo 'No luck sorry!';
}
Use in_array: http://se.php.net/manual/en/function.in-array.php
Combine it with array_map to flatten what you have. Like so:
$new_array = array_map( function( $arr ) {
return $arr['type'];
}, $array );
in_array( 'bar', $new_array );
Honestly a foreach loop or moonwave99's array-filter answer is probably going to be your best bet, but if what you're looking is the shortest code possible and creativity that will make most programmers gag, you could try serialize-ing the array and using string-searching functions:
serialize(array(
0=>array(
'type' => 'foo'
),
1=>array(
'type' => 'bar'
),
2=>array(
'type' => 'bar'
)
))
becomes
a:3:{i:0;a:1:{s:4:"type";s:3:"foo";}i:1;a:1:{s:4:"type";s:3:"bar";}i:2;a:1:{s:4:"type";s:3:"bar";}}
So you can now run a strpos() or preg_match() function to find them. So your whole function would look like:
$exists = strpos('{s:4:"type";s:3:"bar";}',serialize($arrayOfTypes)); //returns number or false
It's short, it's snappy, and get's the job done for simple string keypairs.
This is basically the same as moonwave99's solution but slightly more useful as it's in a function that you can feed a key/value pair to as well, so it can be used to search for any key/value combo:
function isKVInArray($k, $v, $array) {
$filtered = array_filter($array, function($item) use($k,$v) {
return $item[$k] == $v;
});
if(count($filtered)>=1) return true;
else return false;
}
$k = 'live';
$v = 'y';
$my_array = array(
0=>array(
'live'=>'n',
'something_else'=>'a'
),
1=>array(
'live'=>'y',
'something_else'=>'b'
),
2=>array(
'live'=>'n',
'something_else'=>'c'
)
);
if(isKVInArray($k, $v, $my_array)) echo "$k=>$v was found in the array.";
else echo 'No luck sorry!';
there's another simple way which is useful when you need to count all the different values
foreach ($arrayOfTypes as $type) $cnt[$type['type']]++;
To get the number of 'bar' (or to get the count of another value):
echo($cnt['bar']);

use strings to access (potentially large) multidimensional arrays

I am having trouble figuring out a way to simply parse a string input and find the correct location within a multidimensional array.
I am hoping for one or two lines to do this, as the solutions I have seen rely on long (10-20 line) loops.
Given the following code (note that the nesting could, in theory, be of any arbitrary depth):
function get($string)
{
$vars = array(
'one' => array(
'one-one' => "hello",
'one-two' => "goodbye"
),
'two' => array(
'two-one' => "foo",
'two-two' => "bar"
)
);
return $vars[$string]; //this syntax isn't required, just here to give an idea
}
get("two['two-two']"); //desired output: "bar". Actual output: null
Is there a simple use of built-in functions or something else easy that would recreate my desired output?
Considering $vars being your variables you would like to get one['one-one'] or two['two-two']['more'] from (Demo):
$vars = function($str) use ($vars)
{
$c = function($v, $w) {return $w ? $v[$w] : $v;};
return array_reduce(preg_split('~\[\'|\'\]~', $str), $c, $vars);
};
echo $vars("one['one-one']"); # hello
echo $vars("two['two-two']['more']"); # tea-time!
This is lexing the string into key tokens and then traverse the $vars array on the keyed values while the $vars array has been turned into a function.
Older Stuff:
Overload the array with a function that just eval's:
$vars = array(
'one' => array(
'one-one' => "hello",
'one-two' => "goodbye"
),
'two' => array(
'two-one' => "foo",
'two-two' => "bar"
)
);
$vars = function($str) use ($vars)
{
return eval('return $vars'.$str.';');
};
echo $vars("['one']['one-two']"); # goodbye
If you're not a fan of eval, change the implementation:
$vars = function($str) use ($vars)
{
$r = preg_match_all('~\[\'([a-z-]+)\']~', $str, $keys);
$var = $vars;
foreach($keys[1] as $key)
$var = $var[$key];
return $var;
};
echo $vars("['one']['one-two']"); # goodbye
How about
$vars = array(
'one' => array(
'one-one' => "hello",
'one-two' => "goodbye"
),
'two' => array(
'two-one' => "foo",
'two-two' => "bar"
)
);
function get( $string, $vars )
{
$keys = explode( '][', substr( $string, 1, -1 ) );
foreach( $keys as $key ) {
$vars = $vars[$key];
}
return $vars;
}
echo get( '[two][two-one]', $vars );
For one, you've not got a $var in your get() function. $var was defined outside the function, and PHP scoping rules do not make "higher" vars visible in lower scopes unless explictly made global in the lower scope:
function get($string) {
global $vars;
eval('$x = $vars' . $string);
return $x;
}
get("['two']['two-two']");
might work, but this isn't tested, and using eval is almost always a very bad idea.
Kohana has a nice Config class which alows something like this:
echo Config::get("two.two-two");
You can check it out here: http://kohanaframework.org/3.1/guide/api/Config

Accessing deeper array keys using function arguments

I am trying to create a function (unless one already exists?) which is supposed to accept an "unlimited" amount of arguments (using func_get_args()) to find the value of an array key. Regardless of how deep within the array it is to be found.
Say if I would use $registry->getSetting( 'template', 'default' ); I am supposed to get the value of $this->properties['settings']['template']['default'] or if I would you $registry->getSetting( 'users', 1, 'name', 'first' ); I would expect it to return the value of $this->properties['users'][1]['name']['first'] (just a second example with a couple of extra arguments).
Now, to do something like this, I could count the amount of arguments passed using func_num_args() and then do a switch with different cases. Although, this would limit it to a certain amount of keys.
So I am asking you if there is a way of doing this, to allow an "unlimited" amount rather than a fixed amount of arguments to access a deeper key of an array.
<?PHP
class Registry
{
// Values are actually fetched from a config file, but for easier understanding
private $properties = array(
'settings' => array(
'template' => array(
'default' => 'default',
'caching' => TRUE
)
)
);
public function getSetting( )
{
// do something like
// return $this->properties['settings'][func_get_args( )];
}
}
?>
Any help whatsoever is highly appreciated.
Thank you!
<?PHP
class Registry
{
// Values are actually fetched from a config file, but for easier understanding
private $properties = array(
'settings' => array(
'template' => array(
'default' => 'default',
'caching' => TRUE
)
)
);
public function getSetting()
{
$result = $this->properties;
foreach (func_get_args() as $arg) {
if (isset($result[$arg])) {
$result = $result[$arg];
} else {
return;
}
}
return $result;
}
}
$r = new Registry();
var_dump($r->getSetting('settings', 'template', 'default'));
?>
Hmm, I'm not sure if this is what you want, but this is straight from the PHP manual:
Variable-length argument lists
PHP 4 and above has support for
variable-length argument lists in
user-defined functions. This is really
quite easy, using the func_num_args(),
func_get_arg(), and func_get_args()
functions.
No special syntax is required, and
argument lists may still be explicitly
provided with function definitions and
will behave as normal.
I would just loop over the arguments and output them into a single array (I might be misinterpreting your question):
$output = array();
$arguments = func_get_args();
foreach ($argument as $arg)
{
$output[$arg] = $this->properties['settings'][$arg];
}
return $output;
Good luck?
I don't know if there is a PHP function which can do this, but my way is working fine too:
class Registry
{
// Values are actually fetched from a config file, but for easier understanding
private $properties = array(
'settings' => array(
'template' => array(
'default' => 'default',
'caching' => TRUE
)
)
);
public function getSetting( $array = array() )
{
$return = $this->properties['settings'];
foreach( $array as $a )
{
$return = $return[$a];
}
return $return;
}
}
then use:
$Registry = new Registry();
$template = $Registry->getSetting(array( 'template' ) );
$template_caching = $Registry->getSetting(array( 'template', 'caching' ) );
Use dot as separator for nested arrays:
Registry::getAttribute('first.second.1.value'); // will return $ar['first']['second'][1]['value']
In getAttribute explode param by . and map it into registry values.
Something like this (untested):
function getSetting ( /* .. */ )
{
$args = func_get_args();
array_unshift( $args, $this->properties );
return getSettingInternal( $args );
}
function getSettingInternal ( $args )
{
$arr = $args[0];
if ( count( $args ) > 1 )
{
$key = array_slice( $args, 1, 1 );
if ( !is_array( $arr ) )
return $arr;
$arr = array_key_exists( $arr, $key ) ? $arr[$key] : $arr['default'];
$args[0] = $arr;
return getSettingInternal( $args );
}
else
return $arr;
}

Categories