This question already has answers here:
Return single column from a multi-dimensional array [duplicate]
(7 answers)
Closed 9 years ago.
Are there any functions for recursively exploding and imploding multi-dimensional arrays in PHP?
You can do this by writing a recursive function:
function multi_implode($array, $glue) {
$ret = '';
foreach ($array as $item) {
if (is_array($item)) {
$ret .= multi_implode($item, $glue) . $glue;
} else {
$ret .= $item . $glue;
}
}
$ret = substr($ret, 0, 0-strlen($glue));
return $ret;
}
As for exploding, this is impossible unless you give some kind of formal structure to the string, in which case you are into the realm of serialisation, for which functions already exist: serialize, json_encode, http_build_query among others.
I've found that var_export is good if you need a readable string representation (exploding) of the multi-dimensional array without automatically printing the value like var_dump.
http://www.php.net/manual/en/function.var-export.php
You can use array_walk_recursive to call a given function on every value in the array recursively. How that function looks like depends on the actual data and what you’re trying to do.
I made two recursive functions to implode and explode.
The result of multi_explode may not work as expected (the values are all stored at the same dimension level).
function multi_implode(array $glues, array $array){
$out = "";
$g = array_shift($glues);
$c = count($array);
$i = 0;
foreach ($array as $val){
if (is_array($val)){
$out .= multi_implode($glues,$val);
} else {
$out .= (string)$val;
}
$i++;
if ($i<$c){
$out .= $g;
}
}
return $out;
}
function multi_explode(array $delimiter,$string){
$d = array_shift($delimiter);
if ($d!=NULL){
$tmp = explode($d,$string);
foreach ($tmp as $key => $o){
$out[$key] = multi_explode($delimiter,$o);
}
} else {
return $string;
}
return $out;
}
To use them:
echo $s = multi_implode(
array(';',',','-'),
array(
'a',
array(10),
array(10,20),
array(
10,
array('s','t'),
array('z','g')
)
)
);
$a= multi_explode(array(';',',','-'),$s);
var_export($a);
Related
I need to turn each end-point in a multi-dimensional array (of any dimension) into a row containing the all the descendant nodes using PHP. In other words, I want to resolve each complete branch in the array. I am not sure how to state this more clearly, so maybe the best way is to give an example.
If I start with an array like:
$arr = array(
'A'=>array(
'a'=>array(
'i'=>1,
'j'=>2),
'b'=>3
),
'B'=>array(
'a'=>array(
'm'=>4,
'n'=>5),
'b'=>6
)
);
There are 6 end points, namely the numbers 1 to 6, in the array and I would like to generate the 6 rows as:
A,a,i,1
A,a,j,2
A,b,2
B,a,m,3
B,a,n,4
B,b,2
Each row contains full path of descendants to the end-point. As the array can have any number of dimensions, this suggested a recursive PHP function and I tried:
function array2Rows($arr, $str='', $out='') {
if (is_array($arr)) {
foreach ($arr as $att => $arr1) {
$str .= ((strlen($str)? ',': '')) . $att;
$out = array2Rows($arr1, $str, $out);
}
echo '<hr />';
} else {
$str .= ((strlen($str)? ',': '')) . $arr;
$out .= ((strlen($out)? '<br />': '')) . $str;
}
return $out;
}
The function was called as follows:
echo '<p>'.array2Rows($arr, '', '').'</p>';
The output from this function is:
A,a,i,1
A,a,i,j,2
A,a,b,3
A,B,a,m,4
A,B,a,m,n,5
A,B,a,b,6
Which apart from the first value is incorrect because values on some of the nodes are repeated. I have tried a number of variations of the recursive function and this is the closest I can get.
I will welcome any suggestions for how I can get a solution to this problem and apologize if the statement of the problem is not very clear.
You were so close with your function... I took your function and modified is slightly as follows:
function array2Rows($arr, $str='', $csv='') {
$tmp = $str;
if (is_array($arr)) {
foreach ($arr as $att => $arr1) {
$tmp = $str . ((strlen($str)? ', ': '')) . $att;
$csv = array2Rows($arr1, $tmp, $csv);
}
} else {
$tmp .= ((strlen($str)? ', ': '')) . $arr;
$csv .= ((strlen($csv)? '<br />': '')) . $tmp;
}
return $csv;
}
The only difference is the introduction of a temporary variable $tmp to ensure that you don't change the $str value before the recursion function is run each time.
The output from your function becomes:
This is a nice function, I can think of a few applications for it.
The reason that you are repeating the second to last value is that in your loop you you are appending the key before running the function on the next array. Something like this would work better:
function array2Rows($arr, &$out=[], $row = []) {
if (is_array($arr)) {
foreach ($arr as $key => $newArray) {
if (is_array($newArray)) {
$row[] = $key; //If the current value is an array, add its key to the current row
array2Rows($newArray, $out, $row); //process the new value
} else { //The current value is not an array
$out[] = implode(',',array_merge($row,[$key,$newArray])); //Add the current key and value to the row and write to the output
}
}
}
return $out;
}
This is lightly optimized and utilizes a reference to hold the full output. I've also changed this to use and return an array rather than strings. I find both of those changes to make the function more readable.
If you wanted this to return a string formatted similarly to the one that you have in your function, replace the last line with
return implode('<br>', $out);
Alternatively, you could do that when calling, which would be what I would call "best practice" for something like this; e.g.
$result = array2Rows($arr);
echo implode('<br>', $result);
Note, since this uses a reference for the output, this also works:
array2Rows($arr, $result);
echo implode('<br>', $result);
I am trying to manually sort a PHP array without making use of ksort.
This is how my code looks at the moment:
function my_ksort(&$arg){
foreach($arg as $key1 => $value1){
foreach($arg as $key2 => $value2){
if($key1 > $key2){
$aux = $value2;
$arg[$key2] = $value1;
$arg[$key1] = $aux;
}
}
}
}
It doesn't sort, I can't figure out how to make it sort.
You could try this:
function my_ksort(&$arg)
{
$keys=array_keys($arg);
sort($keys);
foreach($keys as $key)
{
$val=$arg[$key];
unset($arg[$key]);
$arg[$key]=$val;
}
}
I'm sorting the keys separately and then deleting the elements one-by-one and appending them to the end, in ascending order.
I'm using another sorting function (sort()), but if you want to eliminate all available sorting functions from your emulation, sort() is much easier to emulate. In fact, #crypticous's algorithm does just that!
This function return array in ASC. Take in consideration that I'm using goto which is supported in (PHP 5 >= 5.3.0)
function ascending_array($array){
if (!is_array($array)){
$array = explode(",", $array);
}
$new = array();
$flag = true;
iter:
$array = array_values($array); // recount array values with new offsets
(isset($min["max"])) ? $min["value"] = $min["max"] : $min["value"] = $array[0];
$min["offset"] = 0;
for ($i=0;$i<count($array);$i++){
if ($array[$i] < $min["value"]){ // redefine min values each time if statement executed
$min["value"] = $array[$i];
$min["offset"] = $i;
}
if ($flag){ // execute only first time
if ($array[$i] > $min["value"]){ // define max value from array
$min["max"] = $array[$i];
}
$flag = false;
}
if ($i === (count($array)-1)){ // last array element
array_push($new,$min["value"]);
unset($array[$min["offset"]]);
}
}
if (count($array)!=0){
goto iter;
}
print_r($new);
}
$arr = array(50,25,98,45);
ascending_array($arr); // 25 45 50 98
PS. When I was studying php, I wrote this function and now remembered that I had it (that's why I really don't remember what I am doing in it, though fact is it's working properly and hopefully there are comments too), hope you'll enjoy :)
DEMO
I was checking some issue related to this post and i wanted to give my insight about it ! here's what i would have done to implement php's sort :
$array_res = array();
$array = array(50,25,98,45);
$i=0;
$temp = $array[0];
$key = array_search($temp, $array);
while ($i<count($array)-1){
$temp = $array[0];
for($n=0;$n<count($array) ;$n++)
{
if($array[$n]< $temp && $array[$n] != -1 )
{
$temp = $array[$n];
}
else{continue;}
}
//get the index for later deletion
$key = array_search($temp, $array);
array_push($array_res, $temp);
/// flag on those which were ordered
$array[$key] =-1;
$i++;
}
// lastly append the highest number
for($n=0;$n<count($array) ;$n++)
{
if ($array[$n] != -1)
array_push($array_res, $array[$n]);
}
// display the results
print_r($array_res);
This code will display : Array
(
[0] => 25
[1] => 45
[2] => 50
[3] => 98
)
Short and sweet
function custom_ksort($arg)
{
$keys = array_keys($arg);
sort($keys);
foreach($keys as $newV)
{
$newArr[$newV] = $arg[$newV];
}
return $newArr;
}
It looks like your issue is that you're changing "temporary" characters $key1 and $key2 but not the actual arrays. You have to change $arg, not just $key1 and $key2.
Try something like:
$arr = Array(3=>"a",7=>"b");
print_r( $arr );
foreach( $arr as $k=>$v ){
unset($arr[$k]);
$arr[$k+1] = $v;
}
print_r($arr);
Is there a way to perform operations on each items of an array we are imploding without traversing the array twice?
I've run into lambda-based solutions but it traverses the array twice (unless I'm wrong):
$array = array('some','boring','items');
$func = function($arr){
$return = array();
foreach ($arr as $item) {
$return[] = ucfirst($item);
}
return $return;
};
echo ' ' . implode('#', $func($array));
A pretty old report exists on PHP bugtracker but no practical solution were given.
I would like to avoid recoding implode like such:
$iter = new ArrayIterator($array);
while ($iter->valid()) {
echo ucfirst($iter->current());
$iter->next();
if ($iter->valid()) {
echo '#';
}
}
Sure, why not. Just use function call in-place, like:
$array = array('some','boring','items');
$result = substr(array_reduce($array, function(&$cur, $x)
{
return $cur.='#'.ucfirst($x);
}, ''), 1);
Alternatively (if you want to avoid even string overhead when doing substr()) - use
$result = ucfirst(array_shift($array)).array_reduce($array, function(&$cur, $x)
{
return $cur.='#'.ucfirst($x);
}, '');
-less "beautiful" - but certainly will use each element only once.
This question already has answers here:
Create a comma-separated string from a single column of an array of objects
(15 answers)
Closed 2 years ago.
Is there a way to implode the values of similar objects contained in an array? I have an array of objects:
$this->inObjs
and I'd like a comma separated string of each of their messageID properties:
$this->inObjs[$i]->messageID
Is there an elegant way to do this or am I going to have to MacGyver a solution with get_object_vars or foreachs or something similar? Thanks for the help.
$allMessageID = '';
foreach ($this->inObjs as $objectDetail) :
$allMessageID[] = $objectDetail->messageID;
endforeach;
$allMessageID_implode = implode(",", $allMessageID);
echo $allMessageID_implode;
If you can modify the class, you can implement __toString:
class MyObject {
private $messageID = 'Hello';
public function __toString() {
return $this->messageID;
}
}
// ...
$objectList = array(new MyObject, new MyObject);
echo implode(',', $objectList);
// Output: Hello,Hello
The easiest way that I found is using array_map
$messageIDs = array_map( function($yourObject) { return $yourObject->messageID; }, $this->inObjs );
$string = implode(", ", $messageIDs );
$messageIDArray;
foreach($this->inObjs as $obj){
$messageIDArray[] = $obj->messageID;
}
$string = implode(',',$messageIDArray);
I usually make a Helper for this situation, and use it like this
function GetProperties(array $arrOfObjects, $objectName) {
$arrProperties = array();
foreach ($arrOfObjects as $obj) {
if ($obj->$objectName) {
$arrProperties[] = $obj->$objectName;
}
}
return $arrProperties;
}
Here is a two liner:
array_walk($result, create_function('&$v', '$v = $v->property;'));
$result = implode(',', $result);
Or:
array_walk($result, function(&$v, &$k) use (&$result) { $v = $v->name; } );
$result = implode(',', $result);
Where $v->property is your object property name to implode.
Also see array_map().
This question already has answers here:
PHP: How to remove specific element from an array?
(22 answers)
PHP array delete by value (not key)
(20 answers)
Closed 1 year ago.
I need to remove array item with given value:
if (in_array($id, $items)) {
$items = array_flip($items);
unset($items[ $id ]);
$items = array_flip($items);
}
Could it be done in shorter (more efficient) way?
It can be accomplished with a simple one-liner.
Having this array:
$arr = array('nice_item', 'remove_me', 'another_liked_item', 'remove_me_also');
You can do:
$arr = array_diff($arr, array('remove_me', 'remove_me_also'));
And the value of $arr will be:
array('nice_item', 'another_liked_item')
I am adding a second answer. I wrote a quick benchmarking script to try various methods here.
$arr = array(0 => 123456);
for($i = 1; $i < 500000; $i++) {
$arr[$i] = rand(0,PHP_INT_MAX);
}
shuffle($arr);
$arr2 = $arr;
$arr3 = $arr;
/**
* Method 1 - array_search()
*/
$start = microtime(true);
while(($key = array_search(123456,$arr)) !== false) {
unset($arr[$key]);
}
echo count($arr). ' left, in '.(microtime(true) - $start).' seconds<BR>';
/**
* Method 2 - basic loop
*/
$start = microtime(true);
foreach($arr2 as $k => $v) {
if ($v == 123456) {
unset($arr2[$k]);
}
}
echo count($arr2). 'left, in '.(microtime(true) - $start).' seconds<BR>';
/**
* Method 3 - array_keys() with search parameter
*/
$start = microtime(true);
$keys = array_keys($arr3,123456);
foreach($keys as $k) {
unset($arr3[$k]);
}
echo count($arr3). 'left, in '.(microtime(true) - $start).' seconds<BR>';
The third method, array_keys() with the optional search parameter specified, seems to be by far the best method. Output example:
499999 left, in 0.090957164764404 seconds
499999left, in 0.43156313896179 seconds
499999left, in 0.028877019882202 seconds
Judging by this, the solution I would use then would be:
$keysToRemove = array_keys($items,$id);
foreach($keysToRemove as $k) {
unset($items[$k]);
}
How about:
if (($key = array_search($id, $items)) !== false) unset($items[$key]);
or for multiple values:
while(($key = array_search($id, $items)) !== false) {
unset($items[$key]);
}
This would prevent key loss as well, which is a side effect of array_flip().
to remove $rm_val from $arr
unset($arr[array_search($rm_val, $arr)]);
The most powerful solution would be using array_filter, which allows you to define your own filtering function.
But some might say it's a bit overkill, in your situation...
A simple foreach loop to go trough the array and remove the item you don't want should be enough.
Something like this, in your case, should probably do the trick :
foreach ($items as $key => $value) {
if ($value == $id) {
unset($items[$key]);
// If you know you only have one line to remove, you can decomment the next line, to stop looping
//break;
}
}
Try array_search()
Your solutions only work if you have unique values in your array
See:
<?php
$trans = array("a" => 1, "b" => 1, "c" => 2);
$trans = array_flip($trans);
print_r($trans);
?>
A better way would be unset with array_search, in a loop if neccessary.
w/o flip:
<?php
foreach ($items as $key => $value) {
if ($id === $value) {
unset($items[$key]);
}
}
function deleteValyeFromArray($array,$value)
{
foreach($array as $key=>$val)
{
if($val == $value)
{
unset($array[$key]);
}
}
return $array;
}
You can use array_splice function for this operation Ref : array_splice
array_splice($array, array_search(58, $array ), 1);