I want to get values using multiple keys.
I have a php code that works for single key but i want to get values for multiple keys. How can i do that?
<?php
$arr=array(
'1'=>'India',
'2'=>'Canada',
'3'=>'United',
'4'=>'China',
'5'=>'London',
'6'=>'New Delhi',
);
$key1='4';
$key2='3';
$key3='4';
echo $arr[$key1, $key2, $key3];
?>
I want output like this in proper order
China
United
China
Thanks in advance.
We have interface ArrayAccess in PHP:
https://www.php.net/manual/en/class.arrayaccess.php
So we can write code as following to support multiple keys( Updated the example from above page ):
You will have to update it to fit your requirements.
<?php
class MultipleKeyArray implements ArrayAccess {
private $container = array();
private $separator = ',';
public function __construct($arr ) {
$this->container = $arr;
}
public function setSeparator($str){
$this->separator = $str;
}
public function offsetSet($offsets, $values) {
$os = explode(',',$offsets);
$vs = explode(',',$values);
$max = max(count($os),count($vs));
for($i=0;$i<$max;$i++){
$offset = $os[$i];
$value = $vs[$i];
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
}
public function offsetExists($offsets) {
$os = explode(',',$offsets);
for($i=0;$i<count($os);$i++){
$offset = $os[$i];
if( !isset($this->container[$offset]) ){
return false;
}
}
return true;
}
public function offsetUnset($offsets) {
$os = explode(',',$offsets);
for($i=0;$i<count($os);$i++){
$offset = $os[$i];
unset($this->container[$offset]);
}
}
public function offsetGet($offsets) {
$os = explode(',',$offsets);
$result = '';
for($i=0;$i<count($os);$i++){
$offset = $os[$i];
$result .= ($i>0 ? $this->separator:'') . (isset($this->container[$offset]) ? $this->container[$offset] : '');
}
return $result;
}
}
$arr=array(
'1'=>'India',
'2'=>'Canada',
'3'=>'United',
'4'=>'China',
'5'=>'London',
'6'=>'New Delhi',
);
$o = new MultipleKeyArray($arr);
$o[] = 'new0';
$o['f,g']='new1,new2';
var_dump(isset($o['f,g']));
var_dump(isset($o['1,2,f']));
var_dump(isset($o['f,not,there']));
echo $o['4,3,4']."\n";
echo $o['2,f,g']."\n";
$o->setSeparator("|");
echo $o['4,3,4']."\n";
Output:
bool(true)
bool(true)
bool(false)
China,United,China
Canada,new1,new2
China|United|China
PHP index cannot take array - you should do that with loop or PHP array function.
First define array of the key you need as:
$keys = [$key1, $key2, $key3];
Now use a foreach loop to echo them as:
foreach($keys as $k)
echo $arr[$k] . PHP_EOL;
And the one-liner:
array_walk($keys, function($k) use ($arr) {echo $arr[$k] . PHP_EOL;});
Related
I'm using array_filter with multiple parameters but it's not doing the filter correctly. Here where it's supposed to return an array with only "arts,crafts,designs" as an element, it's returning an empty array. The only $askedcat parameter it works for is "arts". I can't find out what the issue is.
I've tried not using an array_filter and instead just looping over the array, and I get the same problem.
<?php
class CategoryFilter {
public $categoryAskedFor;
function __construct($askedCat) {
$this->categoryAskedFor = $askedCat;
}
function categoryCallback($projectCategoryString) {
$project_category_array = explode(",", $projectCategoryString);
if(in_array($this->categoryAskedFor, $project_category_array)) return true;
return false;
}
}
$verifiedProjects = ["arts", "arts,crafts,designs", "film", "film,theater"];
$askedCat = "crafts";
$newArr = array_filter($verifiedProjects, array(new CategoryFilter($askedCat), "categoryCallback"));
for ($i = 0; $i < count($newArr); $i++) {
echo $newArr[$i] . "<br>";
}
I expect the output here to be arts,crafts,design<br> but it's only <br> meaning the array is empty.
There are many way to achieve this but let me show you two way here
WAY #1
If you using the for loop to retrieve the array value then need to have numeric key and as per your code you need array_values function to manage that
<?php
class CategoryFilter {
public $categoryAskedFor;
function __construct($askedCat) {
$this->categoryAskedFor = $askedCat;
}
function categoryCallback($projectCategoryString) {
$project_category_array = explode(",", $projectCategoryString);
if(in_array($this->categoryAskedFor, $project_category_array)) return true;
return false;
}
}
$verifiedProjects = ["arts", "arts,crafts,designs", "film", "film,theater"];
$askedCat = "crafts";
$newArr = array_filter($verifiedProjects, array(new CategoryFilter($askedCat), "categoryCallback"));
$newArr = array_values($newArr);
for ($i = 0; $i < count($newArr); $i++) {
echo $newArr[$i] . "<br>";
}
WAY # 2
If you don't want to use the array_values here then you need to manage the foreach loop instead of for loop
<?php
class CategoryFilter {
public $categoryAskedFor;
function __construct($askedCat) {
$this->categoryAskedFor = $askedCat;
}
function categoryCallback($projectCategoryString) {
$project_category_array = explode(",", $projectCategoryString);
if(in_array($this->categoryAskedFor, $project_category_array)) return true;
return false;
}
}
$verifiedProjects = ["arts", "arts,crafts,designs", "film", "film,theater"];
$askedCat = "crafts";
$newArr = array_filter($verifiedProjects, array(new CategoryFilter($askedCat), "categoryCallback"));
foreach ($newArr as $value) {
echo $value . "<br>";
}
The way you're looping over your resulting array is wrong, because array_filter will preserve the array keys. The 0 index may not be there in the filtered version (and in your case, it actually isn't).
Use foreach instead:
foreach ($newArr as $value) {
echo $value, '<br>';
}
array_filter will remove elements but will not reset the keys. use array_values to reset the keys first.
$newArr = array_values($newArr);
for ($i = 0; $i < count($newArr); $i++) {
echo $newArr[$i] . "<br>";
}
You will get as per your output
$askedCat = 'crafts';
$verifiedProjects = ["arts", "arts,crafts,designs", "film", "film,theater"];
$newArr = array_filter($verifiedProjects, function ($item) use ($askedCat) {
if (stripos($item, $askedCat) !== false) {
return true;
}
return false;
});
foreach ($newArr as $value) {
echo $value . "<br>";
}
Given a string in the format of "one/two" or "one/two/three", with / as delimiters, I need to get the value (as a reference) in a 2d array. In this specific case, I'm accessing the $_SESSION variable.
In the SessionAccessor::getData($str) function, I don't know what to put in there to make it parse the delimited string and return the array item. The idea is I won't know which array key I'm accessing. The function will be generic.
class SessionAccessor {
static &function getData($str) {
// $str = 'one/two/three'
return $_SESSION['one']['two']['three'];
}
}
/** Below is an example of how it will be expected to work **/
/**********************************************************/
// Get a reference to the array
$value =& SessionAccessor::getData('one/two/three');
// Set the value of $_SESSION['one']['two']['three'] to NULL
$value = NULL;
Here's the solution provided by #RocketHazmat here at StackOverflow:
class SessionAccessor {
static function &getVar($str) {
$arr =& $_SESSION;
foreach(explode('/',$str) as $path){
$arr =& $arr[$path];
}
return $arr;
}
}
From what you've described, it sounds like this is what you're after;
&function getData($str) {
$path = explode('/', $str);
$value = $_SESSION;
foreach ($path as $key) {
if (!isset($value[$key])) {
return null;
}
$value = $value[$key];
}
return $value;
}
EDIT
To also allow for the values to be set, it would be best to use a setData method. This should hopefully do what you hoped for;
class SessionAccessor {
static function getData($str) {
$path = explode('/', $str);
$value = $_SESSION;
foreach ($path as $key) {
if (!isset($value[$key])) {
return null;
}
$value = $value[$key];
}
return $value;
}
static function setData($str, $newValue) {
$path = explode('/', $str);
$last = array_pop($path);
$value = &$_SESSION;
foreach ($path as $key) {
if (!isset($value[$key])) {
$value[$key] = array();
}
$value = &$value[$key];
}
$value[$last] = $newValue;
}
}
I have a function called get_config() and an array called $config.
I call the function by using get_config('site.name') and am looking for a way to return the value of $config so for this example, the function should return $config['site']['name'].
I'm nearly pulling my hair out - trying not to use eval()! Any ideas?
EDIT: So far I have:
function get_config($item)
{
global $config;
$item_config = '';
$item_path = '';
foreach(explode('.', $item) as $item_part)
{
$item_path .= $item."][";
$item = $config.{rtrim($item_path, "][")};
}
return $item;
}
This should work:
function get_config($config, $string) {
$keys = explode('.', $string);
$current = $config;
foreach($keys as $key) {
if(!is_array($current) || !array_key_exists($key, $current)) {
throw new Exception('index ' . $string . ' was not found');
}
$current = $current[$key];
}
return $current;
}
you could try something like...
function get_config($item)
{
global $config;
return $config[{str_replace('.','][',$item)}];
}
Inside your get_config function, you can parse the string using explode function in php
function get_config($data){
$pieces = explode(".", $data);
return $config[$pieces[0]][$pieces[1]];
}
I'm trying to write a recursive array iterator function in which the function will return a result set of all sets that are specified by '$needle'. Where $needle = key
Here is my function:
function recursive($needle, $array, $holder = array()) {
foreach ($array as $key => $value) {
if (gettype($value) == 'array') {
if ($key != $needle) {
recursive($needle, $value);
} elseif ($key == $needle) {
if (!empty($value)) {
array_push($holder, $value);
}
}
}
}
return $holder;
}
But I'm not getting all the results back and instead get a few empty results, if I don't specify the !empty($value), although the input array does not have any empty sets. What am I doing wrong?
You don't need to reinvent the wheel since PHP has standard Recursive Iterator API:
//$array is your multi-dimensional array
$result = [];
$search = 'foo';
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator(
$array,
RecursiveArrayIterator::CHILD_ARRAYS_ONLY
)
);
foreach($iterator as $key=>$value)
{
if($search==$key && $value!=='')
{
$result[] = $value;
}
}
-note, that, since you're searching for value by key - in common case $value will hold entire subsection.
If you want to do this in your own recursive function, here's one:
function recursive($needle, $array, $holder = [])
{
$holder = [];
foreach($array as $key=>$value)
{
if($key===$needle && $value!=='')
{
$holder = array_merge($holder, [$value]);
}
if(is_array($value))
{
$holder = array_merge($holder, recursive($needle, $value, $holder));
}
}
return $holder;
}
More fine-grained control is perhaps possible with true (tm) recursive array traversal via RecursiveIterator interface and some key filters and array conversion functions:
$needle = '0';
$array = [[1]];
$it = new KeyFilter(
new RecursiveIteratorIterator(
new MyRecursiveArrayIterator($array)
, RecursiveIteratorIterator::SELF_FIRST
)
, $needle
);
$result = iterator_to_array($it, FALSE);
var_dump($result);
Providing an exemplary result as:
array(2) {
[0] =>
array(1) {
[0] =>
int(1)
}
[1] =>
int(1)
}
Full code example (Demo):
<?php
/**
* #link http://stackoverflow.com/q/19709410/367456
*/
Class MyRecursiveArrayIterator extends ArrayIterator implements RecursiveIterator
{
public function hasChildren()
{
$current = $this->current();
return is_array($current) && count($current);
}
public function getChildren()
{
return new self($this->current());
}
}
class KeyFilter extends RegexIterator
{
public function __construct(Iterator $iterator, $key)
{
parent::__construct(
$iterator, '/' . preg_quote($key) . '/', NULL, RegexIterator::USE_KEY
);
}
}
$needle = '0';
$array = [[1]];
$it = new KeyFilter(
new RecursiveIteratorIterator(
new MyRecursiveArrayIterator($array)
, RecursiveIteratorIterator::SELF_FIRST
)
, $needle
);
$result = iterator_to_array($it, FALSE);
var_dump($result);
A tiny modification of your construction:
$holder = recursive($needle, $value, $holder);
Ay?
I've been trying to think of a way to dynamically return property values for a class using __call instead of creating a slew of functions whose only purpose would be to return those values. The idea I have is to be able to request ( for example ) $this->fetch_PersonFirstName() and have the class check if $this->person["first_name"] is set and return the value. Another example would be calling $this->fetch_BookGenreTitle() and have the class return the value of $this->book["genre"]["title"]. I know something like this would have to do a bit of checking in order for it to automatically determine that, for example, since $this->book["genre_title"] doesn't exist, then it should check for $this->book["genre"]["title"].
So far I've come up with code that ( for some reason ) works for returning the values of an array ( such as my person example ) but my problem quickly develops when I try to return the values of a multidimensional array ( such as with my above book example ). Bare in mind, I'm still trying to think of a way for the __call method to check for the existence of one, and if it doesn't exist, then the other.
Please, throw me a line here. I've been banging my head against the wall trying to figure this out and it's killing me.
<?php
class Test
{
protected $person;
protected $book;
public function __construct()
{
$this->person["first_name"] = "John Smith";
$this->book["genre"]["title"] = "Suspense";
}
public function __call($method, $args)
{
$args = implode(", ", $args);
if (preg_match("/^fetch_([A-Z]{1,}[a-z]{1,})(.*)?/", $method, $match))
{
print_r($match);
echo "<br><br>";
$property = strtolower($match[1]);
$indexes = $match[2];
if (property_exists("Test", $property))
{
if ($indexes)
{
$indexes = preg_split("/(?<=[a-z])(?=[A-Z])/", $indexes);
$num_indexes = count($indexes);
$count_indexes = 1;
for ($count=0; $count<$num_indexes; $count++)
{
$record = strtolower($indexes[$count]);
$index .= $record;
$array .= "{$record}";
$var_index = $index;
$var_array = $array;
echo $var_index." {$count}<br>";
echo $var_array." {$count}<br>";
//print_r($this->{$property}{$var_array});
if ($count_indexes == $num_indexes)
{
if (isset($this->{$property}{$var_index}))
{
return $this->{$property}{$var_index};
}
else
{
return $this->{$property}{$var_array};
}
}
else
{
$index .= "_";
}
$count_indexes++;
}
echo "<br><br>";
}
else
{
return $this->{$property};
}
}
}
}
}
?>
<?php
$test = new Test();
echo $test->fetch_PersonFirstName();
echo "<br><br>";
echo $test->fetch_BookGenreTitle();
?>
Thanks again folks. I think I finally have my solution, which works for multidimensional arrays or any depth and accounts for determining whether a property is, for example $this->book["genre_title"] or $this->book["genre"]["title"] :)
I'm posting the code below as someone else may randomly find it useful in the future
<?php
class Test
{
protected $person;
protected $book;
public function __construct()
{
$this->person["first_name"] = "John Smith";
$this->book["genre"]["title"] = "Suspense";
}
public function __get($var)
{
if (preg_match_all("/([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)/", $var, $matches))
{
$matches = $matches[1];
$property = strtolower($matches[0]);
if (property_exists($this, $property))
{
unset($matches[0]);
$matches = array_values($matches);
$num_matches = count($matches);
$var = &$this->{$property};
if (!$num_matches)
{
return $var;
}
else
{
foreach($matches as &$match)
{
$match = strtolower($match);
$index .= $match;
if ($probe = $this->iterateArray($var, $index))
{
$var = $probe;
unset($index);
}
elseif ($probe = $this->iterateArray($var, $index))
{
$var = $probe;
}
else
{
$index .= "_";
}
}
return $var;
}
}
}
}
public function iterateArray($var, $index)
{
if (array_key_exists($index, $var))
{
return $var[$index];
}
else
{
return false;
}
}
}
?>
<?php
$test = new Test();
echo $test->PersonFirstName;
echo "<br><br>";
echo $test->BookGenreTitle;
?>
There's more than likely some ways to improve/streamline the code, in which case anyone wanting to do so is more than welcome to post an improved version.
Given "BookGenreTitle" :
Use some sort of regex to separate "Book", "Genre", and "Title"
property_exists($this, "Book")
array_key_exists("genre", $this->book)
If key exists, return $this->book["genre"]
If key doesn't exist, array_key_exists("genre_title", $this->book)
If key exists, return $this->book["genre_title"]
If key doesn't exist, array_key_exists("genre", $this->book) && array_key_exists("title", $this->book["genre"])
Keep going
There's probably a way to use a loop or some sort of recursion instead of hard-coding the maximum depth, but I won't get into that now...
Oh, and as the other poster said, what you want is property overloading (__get and __set).
You need to take a look at property overloading, not method overloading as you've already figured out in the question's title yourself.