I want to pass an array to a function and iterate through it in this function. But I would like to be able to change the way the single entries are displayed.
Suppose I have an array of complicated objects:
$items = array($one, $two, $three);
Right now I do:
$entries = array();
foreach($items as $item) {
$entries[] = create_li($item["title"], pretty_print($item["date"]));
}
$list = wrap_in_ul($entries);
I would like to do the above in one line:
$list = create_ul($items, $item["title"], pretty_print($item["date"]));
Any chance of doing that as of PHP4? Be creative!
from my understanding, you're looking for an "inject" type iterator with a functional parameter. In php, inject iterator is array_reduce, unfortunately it's broken, so you have to write your own, for example
function array_inject($ary, $func, $acc) {
foreach($ary as $item)
$acc = $func($acc, $item);
return $acc;
}
define a callback function that processes each item and returns accumulator value:
function boldify($list, $item) {
return $list .= "<strong>$item</strong>";
}
the rest is easy:
$items = array('foo', 'bar', 'baz');
$res = array_inject($items, 'boldify', '');
print_r($res);
You could use callbacks:
function item_title($item) {
return $item['title'];
}
function item_date($item) {
return $item['date'];
}
function prettyprint_item_date($item) {
return pretty_print($item['date']);
}
function create_ul($items, $contentf='item_date', $titlef='item_title') {
$entries = array();
foreach($items as $item) {
$title = call_user_func($titlef, $item);
$content = call_user_func($contentf, $item);
$entries[] = create_li($title, $content);
}
return wrap_in_ul($entries);
}
...
$list = create_ul($items, 'prettyprint_item_date');
PHP 5.3 would be a big win here, with its support for anonymous functions.
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>";
}
I have the following scenario:
$class = 'Main\Entity\Redaction'; #or anything else namespaced class
$nameClass = explode('\\', $class);
$jsonNamespace = [];
if (!empty($nameClass[0])) {
$jsonNamespace[$nameClass[0]] = [];
if (!empty($nameClass[1])) {
$jsonNamespace[$nameClass[0]][$nameClass[1]] = [];
if (!empty($nameClass[2])) {
$jsonNamespace[$nameClass[0]][$nameClass[1]][$nameClass[2]] = ['#wherever'];
}
}
}
I want to declare a namespaced object JSON.
Something like this:
{
Main: {
Entity: {
Redaction: ['#wherever']
}
}
}
But without a lot of "IF", something recursive.
You can do this with recursion, but another way is by using a reference. Add a new level to the array, and then simply shift the reference down to that new element.
<?php
function buildArray(array $keys, $value){
$ret = array();
$ref =& $ret;
foreach($keys as $key){
// Add the next level to the array
$ref[$key] = array();
// Then shift the reference, so that the next
// iteration can add a new level
$ref =& $ref[$key];
}
// $ref is a reference to the lowest level added
$ref = array($value);
// Not totally sure if this is needed
unset($ref);
return $ret;
}
$class = 'Main\Entity\Redaction';
$jsonNamespace = buildArray(explode('\\', $class), array('#wherever'));
var_dump($jsonNamespace);
DEMO: http://codepad.org/f7O0Qy3D
I know that eval() is a horrible thing to use, but I couldn't think of a better way to do this.
I have the following method which I want to use to delete items from a multidimensional array, and it should delete the item if it exists.
public function delete(){
$keys = func_get_args();
$str = "";
foreach($keys as $key){
$str .= "['$key']";
}
eval("if(isset(\$_SESSION$str)){unset(\$_SESSION$str);}");
}
To use it I would make a call like so:
$obj->delete("one", "two", "three");
which would be the equivalent to this:
if(isset($_SESSION["one"]["two"]["three"])){
unset($_SESSION["one"]["two"]["three"]);
}
Is there a better way to do this than using eval()?
There is a function like that in Ouzo Goodies:
Arrays::removeNestedKey($_SESSION, ['one', 'two', 'three']);
If you don't want to include the lib, you can take a look at the source code and grab the function itself:
public static function removeNestedKey(array &$array, array $keys)
{
$key = array_shift($keys);
if (count($keys) == 0) {
unset($array[$key]);
} else {
self::removeNestedKey($array[$key], $keys);
}
}
This will achieve what you want:
function delete(){
$keys = func_get_args();
$ref = &$_SESSION;
for($x = 0; $x < sizeOf($keys)-1; $x++) {
$ref = &$ref[$keys[$x]];
}
unset($ref[$keys[sizeOf($keys)-1]]);
unset($ref);
}
The following code uses foreach on an array and if the value is an array it does a for each on the nested array
foreach ($playfull as $a)
{
if (is_array($a))
{
foreach ($a as $b)
{
print($b);
print("<p>");
}
} else {
print($a);
print("<p>");
}
}
This only works if you know that the arrays may only be nested one level deep
If arrays could be nested an unknown number of levels deep how do you achieve the same result? (The desired result being to print the value of every key in every array no matter how deeply nested they are)
You can use array_walk_recursive. Example:
array_walk_recursive($array, function (&$val)
{
print($val);
}
This function is a PHP built in function and it is short.
Use recursive functions (that are functions calling themselves):
function print_array_recursively($a)
{
foreach ($a as $el)
{
if (is_array($el))
{
print_array_recursively($el);
}
else
{
print($el);
}
}
}
This is the way, print_r could do it (see comments).
You want to use recursion, you want to call your printing function in itself, whenever you find an array, click here to see an example
$myArray = array(
"foo",
"bar",
"children" => array(
"biz",
"baz"),
"grandchildren" => array(
"bang" => array(
"pow",
"wow")));
function print_array($playfull)
{
foreach ($playfull as $a)
{
if (is_array($a))
{
print_array($a);
} else {
echo $a;
echo "<p>";
}
}
}
echo "Print Array\n";
print_array($myArray);
You could use a recursive function, but the max depth will be determined by the maximum nesting limit (see this SO question, Increasing nesting functions calls limit, for details about increasing that if you need it)
Here's an example:
$array = array(1,array(2,3,array(4,5)),6,7,8);
function printArray($item)
{
foreach ($item as $a)
{
if (is_array($a))
{
printArray($a);
} else {
print($a);
print("<p>");
}
}
}
printArray($array);
I hope that helps.
Try this -
function array_iterate($arr, $level=0, $maxLevel=0)
{
if (is_array($arr))
{
// unnecessary for this conditional to enclose
// the foreach loop
if ($maxLevel < ++$level)
{ $maxLevel = $level; }
foreach($arr AS $k => $v)
{
// for this to work, the result must be stored
// back into $maxLevel
// FOR TESTING ONLY:
echo("<br>|k=$k|v=$v|level=$level|maxLevel=$maxLevel|");
$maxLevel= array_iterate($v, $level, $maxLevel);
}
$level--;
}
// the conditional that was here caused all kinds
// of problems. so i got rid of it
return($maxLevel);
}
$array[] = 'hi';
$array[] = 'there';
$array[] = 'how';
$array['blobone'][] = 'how';
$array['blobone'][] = 'are';
$array['blobone'][] = 'you';
$array[] = 'this';
$array['this'][] = 'is';
$array['this']['is'][] = 'five';
$array['this']['is']['five'][] = 'levels';
$array['this']['is']['five']['levels'] = 'deep';
$array[] = 'the';
$array[] = 'here';
$var = array_iterate($array);
echo("<br><br><pre>$var");
I have this code that echoes values from an array. I'd like to be able to echo the variable elsewhere in my code though. I've tried wrapping a function around it and echoing the function but I can't get anything to work. Here's the code that I have now...
function team(){
if ( ($items = field_get_items('entityform', $entityform, 'field_team_members')) ) {
$values = array_map($items, function($x) { return $x['value']; });
$comma_separated = implode(', ', $values);
foreach ($items as $item) {
$members = $prefix . $item['safe_value'];
$prefix = ', ';
echo $members;
}
}
}
If I simply do team(); it outputs nothing, as well as echo team();. What am I doing wrong here?
Your team function doesn't return anything which is why echo team(); doesn't print anything.
See the documentation on functions for more info: http://php.net/manual/en/functions.returning-values.php
function team(){
$output = '';
if ( ($items = field_get_items('entityform', $entityform, 'field_team_members')) ) {
$values = array_map($items, function($x) { return $x['value']; });
$comma_separated = implode(', ', $values);
$prefix = '';
$members = '';
foreach ($items as $item) {
$members .= $prefix . $item['safe_value'];
$prefix = ', ';
}
$output = $members;
}
return $output;
}
To clarify what i wrote as a comment, i just created a small script:
<?php
function team () {
$string = "hi";
return $string;
}
echo team();
Functions cant work unless until they are called, so you have to call team(); in some way, once called it will for sure display what ever you are trying to echo. Also above function will not display anything unless you force fully exit in that function. Or return some value.