Insert new item in array on any position in PHP - php

How can I insert a new item into an array on any position, for example in the middle of array?

You may find this a little more intuitive. It only requires one function call to array_splice:
$original = array( 'a', 'b', 'c', 'd', 'e' );
$inserted = array( 'x' ); // not necessarily an array, see manual quote
array_splice( $original, 3, 0, $inserted ); // splice in at position 3
// $original is now a b c x d e
If replacement is just one element it is not necessary to put array() around it, unless the element is an array itself, an object or NULL.
RETURN VALUE: To be noted that the function does not return the desired substitution. The $original is passed by reference and edited in place. See the expression array &$array with & in the parameters list .

A function that can insert at both integer and string positions:
/**
* #param array $array
* #param int|string $position
* #param mixed $insert
*/
function array_insert(&$array, $position, $insert)
{
if (is_int($position)) {
array_splice($array, $position, 0, $insert);
} else {
$pos = array_search($position, array_keys($array));
$array = array_merge(
array_slice($array, 0, $pos),
$insert,
array_slice($array, $pos)
);
}
}
Integer usage:
$arr = ["one", "two", "three"];
array_insert(
$arr,
1,
"one-half"
);
// ->
array (
0 => 'one',
1 => 'one-half',
2 => 'two',
3 => 'three',
)
String Usage:
$arr = [
"name" => [
"type" => "string",
"maxlength" => "30",
],
"email" => [
"type" => "email",
"maxlength" => "150",
],
];
array_insert(
$arr,
"email",
[
"phone" => [
"type" => "string",
"format" => "phone",
],
]
);
// ->
array (
'name' =>
array (
'type' => 'string',
'maxlength' => '30',
),
'phone' =>
array (
'type' => 'string',
'format' => 'phone',
),
'email' =>
array (
'type' => 'email',
'maxlength' => '150',
),
)

$a = array(1, 2, 3, 4);
$b = array_merge(array_slice($a, 0, 2), array(5), array_slice($a, 2));
// $b = array(1, 2, 5, 3, 4)

If you want to keep the keys of the initial array and also add an array that has keys, then use the function below:
function insertArrayAtPosition( $array, $insert, $position ) {
/*
$array : The initial array i want to modify
$insert : the new array i want to add, eg array('key' => 'value') or array('value')
$position : the position where the new array will be inserted into. Please mind that arrays start at 0
*/
return array_slice($array, 0, $position, TRUE) + $insert + array_slice($array, $position, NULL, TRUE);
}
Call example:
$array = insertArrayAtPosition($array, array('key' => 'Value'), 3);

This way you can insert arrays:
function array_insert(&$array, $value, $index)
{
return $array = array_merge(array_splice($array, max(0, $index - 1)), array($value), $array);
}

There is no native PHP function (that I am aware of) that can do exactly what you requested.
I've written 2 methods that I believe are fit for purpose:
function insertBefore($input, $index, $element) {
if (!array_key_exists($index, $input)) {
throw new Exception("Index not found");
}
$tmpArray = array();
$originalIndex = 0;
foreach ($input as $key => $value) {
if ($key === $index) {
$tmpArray[] = $element;
break;
}
$tmpArray[$key] = $value;
$originalIndex++;
}
array_splice($input, 0, $originalIndex, $tmpArray);
return $input;
}
function insertAfter($input, $index, $element) {
if (!array_key_exists($index, $input)) {
throw new Exception("Index not found");
}
$tmpArray = array();
$originalIndex = 0;
foreach ($input as $key => $value) {
$tmpArray[$key] = $value;
$originalIndex++;
if ($key === $index) {
$tmpArray[] = $element;
break;
}
}
array_splice($input, 0, $originalIndex, $tmpArray);
return $input;
}
While faster and probably more memory efficient, this is only really suitable where it is not necessary to maintain the keys of the array.
If you do need to maintain keys, the following would be more suitable;
function insertBefore($input, $index, $newKey, $element) {
if (!array_key_exists($index, $input)) {
throw new Exception("Index not found");
}
$tmpArray = array();
foreach ($input as $key => $value) {
if ($key === $index) {
$tmpArray[$newKey] = $element;
}
$tmpArray[$key] = $value;
}
return $input;
}
function insertAfter($input, $index, $newKey, $element) {
if (!array_key_exists($index, $input)) {
throw new Exception("Index not found");
}
$tmpArray = array();
foreach ($input as $key => $value) {
$tmpArray[$key] = $value;
if ($key === $index) {
$tmpArray[$newKey] = $element;
}
}
return $tmpArray;
}

This function by Brad Erickson worked for me for the associative array:
/*
* Inserts a new key/value after the key in the array.
*
* #param $key
* The key to insert after.
* #param $array
* An array to insert in to.
* #param $new_key
* The key to insert.
* #param $new_value
* An value to insert.
*
* #return
* The new array if the key exists, FALSE otherwise.
*
* #see array_insert_before()
*/
function array_insert_after($key, array &$array, $new_key, $new_value) {
if (array_key_exists($key, $array)) {
$new = array();
foreach ($array as $k => $value) {
$new[$k] = $value;
if ($k === $key) {
$new[$new_key] = $new_value;
}
}
return $new;
}
return FALSE;
}
The function source - this blog post. There's also handy function to insert BEFORE specific key.

Based on #Halil great answer, here is simple function how to insert new element after a specific key,
while preserving integer keys:
private function arrayInsertAfterKey($array, $afterKey, $key, $value){
$pos = array_search($afterKey, array_keys($array));
return array_merge(
array_slice($array, 0, $pos, $preserve_keys = true),
array($key=>$value),
array_slice($array, $pos, $preserve_keys = true)
);
}

function insert(&$arr, $value, $index){
$lengh = count($arr);
if($index<0||$index>$lengh)
return;
for($i=$lengh; $i>$index; $i--){
$arr[$i] = $arr[$i-1];
}
$arr[$index] = $value;
}

This is also a working solution:
function array_insert(&$array,$element,$position=null) {
if (count($array) == 0) {
$array[] = $element;
}
elseif (is_numeric($position) && $position < 0) {
if((count($array)+position) < 0) {
$array = array_insert($array,$element,0);
}
else {
$array[count($array)+$position] = $element;
}
}
elseif (is_numeric($position) && isset($array[$position])) {
$part1 = array_slice($array,0,$position,true);
$part2 = array_slice($array,$position,null,true);
$array = array_merge($part1,array($position=>$element),$part2);
foreach($array as $key=>$item) {
if (is_null($item)) {
unset($array[$key]);
}
}
}
elseif (is_null($position)) {
$array[] = $element;
}
elseif (!isset($array[$position])) {
$array[$position] = $element;
}
$array = array_merge($array);
return $array;
}
credits go to:
http://binarykitten.com/php/52-php-insert-element-and-shift.html

Solution by jay.lee is perfect. In case you want to add item(s) to a multidimensional array, first add a single dimensional array and then replace it afterwards.
$original = (
[0] => Array
(
[title] => Speed
[width] => 14
)
[1] => Array
(
[title] => Date
[width] => 18
)
[2] => Array
(
[title] => Pineapple
[width] => 30
)
)
Adding an item in same format to this array will add all new array indexes as items instead of just item.
$new = array(
'title' => 'Time',
'width' => 10
);
array_splice($original,1,0,array('random_string')); // can be more items
$original[1] = $new; // replaced with actual item
Note: Adding items directly to a multidimensional array with array_splice will add all its indexes as items instead of just that item.

You can use this
foreach ($array as $key => $value)
{
if($key==1)
{
$new_array[]=$other_array;
}
$new_array[]=$value;
}

Hint for adding an element at the beginning of an array:
$a = array('first', 'second');
$a[-1] = 'i am the new first element';
then:
foreach($a as $aelem)
echo $a . ' ';
//returns first, second, i am...
but:
for ($i = -1; $i < count($a)-1; $i++)
echo $a . ' ';
//returns i am as 1st element

Try this one:
$colors = array('red', 'blue', 'yellow');
$colors = insertElementToArray($colors, 'green', 2);
function insertElementToArray($arr = array(), $element = null, $index = 0)
{
if ($element == null) {
return $arr;
}
$arrLength = count($arr);
$j = $arrLength - 1;
while ($j >= $index) {
$arr[$j+1] = $arr[$j];
$j--;
}
$arr[$index] = $element;
return $arr;
}

function array_insert($array, $position, $insert) {
if ($position > 0) {
if ($position == 1) {
array_unshift($array, array());
} else {
$position = $position - 1;
array_splice($array, $position, 0, array(
''
));
}
$array[$position] = $insert;
}
return $array;
}
Call example:
$array = array_insert($array, 1, ['123', 'abc']);

$result_array = array();
$array = array("Tim","John","Mark");
$new_element = "Bill";
$position = 1;
for ($i=0; $i<count($array); $i++)
{
if ($i==$position)
{
$result_array[] = $new_element;
}
$result_array[] = $array[$i];
}
print_r($result_array);
// Result will Array([0] => "Tim",[1] => "Bill", [2] => "John",[1] => "Mark")

How to preserve array keys using array_splice()
The answer of #jay.lee is correct, unfortunately it doesn't preserve the keys of an array, as pointed out in the comments:
$original = array(
'a' => 'A',
'b' => 'B',
'c' => 'C',
// insert here
'd' => 'D',
'e' => 'E');
$inserted = array( 'x' => 'X' );
array_splice( $original, 3, 0, $inserted );
print_r($original);
/* Output
Array
(
[a] => A
[b] => B
[c] => C
[0] => X <= the lost key
[d] => D
[e] => E
) */
The simplest way I found to preserve the array keys is to use the array_splice() function and adding the arrays together using + / union operators (also mentioned in the comments of another answer):
$original = array(
'a' => 'A',
'b' => 'B',
'c' => 'C',
// insert here
'd' => 'D',
'e' => 'E');
$inserted = array( 'x' => 'X' );
// Insert before postion 'd'
$before = array_splice( $original, 0, 3 ); // $original contains the left over
// Merge together
$result = $before + $inserted + $original;
print_r($result);
/* Output
Array
(
[a] => A
[b] => B
[c] => C
[x] => X
[d] => D
[e] => E
) */
Note: using array union operators is only safe when dealing with non-numeric keys
Thanks for the correction #mickmackusa

Normally, with scalar values:
$elements = array('foo', ...);
array_splice($array, $position, $length, $elements);
To insert a single array element into your array don't forget to wrap the array in an array (as it was a scalar value!):
$element = array('key1'=>'value1');
$elements = array($element);
array_splice($array, $position, $length, $elements);
otherwise all the keys of the array will be added piece by piece.

You can try it, use this method to make it easy
/**
* array insert element on position
*
* #link https://vector.cool
*
* #since 1.01.38
*
* #param array $original
* #param mixed $inserted
* #param int $position
* #return array
*/
function array_insert(&$original, $inserted, int $position): array
{
array_splice($original, $position, 0, array($inserted));
return $original;
}
$columns = [
['name' => '預約項目', 'column' => 'item_name'],
['name' => '預約時間', 'column' => 'start_time'],
['name' => '預約姓名', 'column' => 'full_name'],
['name' => '連絡電話', 'column' => 'phone'],
['name' => '建立時間', 'column' => 'create_time']
];
$col = ['name' => '預約帳戶', 'column' => 'user_id'];
$columns = array_insert($columns, $col, 3);
print_r($columns);
Print out:
Array
(
[0] => Array
(
[name] => 預約項目
[column] => item_name
)
[1] => Array
(
[name] => 預約時間
[column] => start_time
)
[2] => Array
(
[name] => 預約姓名
[column] => full_name
)
[3] => Array
(
[name] => 報名人數1
[column] => num_of_people
)
[4] => Array
(
[name] => 連絡電話
[column] => phone
)
[5] => Array
(
[name] => 預約帳戶
[column] => user_id
)
[6] => Array
(
[name] => 建立時間
[column] => create_time
)
)

For inserting elements into an array with string keys you can do something like this:
/* insert an element after given array key
* $src = array() array to work with
* $ins = array() to insert in key=>array format
* $pos = key that $ins will be inserted after
*/
function array_insert_string_keys($src,$ins,$pos) {
$counter=1;
foreach($src as $key=>$s){
if($key==$pos){
break;
}
$counter++;
}
$array_head = array_slice($src,0,$counter);
$array_tail = array_slice($src,$counter);
$src = array_merge($array_head, $ins);
$src = array_merge($src, $array_tail);
return($src);
}

This can be done with array_splice however, array_splice fails when inserting an array or using a string key. I wrote a function to handle all cases:
function array_insert(&$arr, $index, $val)
{
if (is_string($index))
$index = array_search($index, array_keys($arr));
if (is_array($val))
array_splice($arr, $index, 0, [$index => $val]);
else
array_splice($arr, $index, 0, $val);
}

If you have regular arrays and nothing fancy, this will do. Remember, using array_splice() for inserting elements really means insert before the start index. Be careful when moving elements, because moving up means $targetIndex -1, where as moving down means $targetIndex + 1.
class someArrayClass
{
private const KEEP_EXISTING_ELEMENTS = 0;
public function insertAfter(array $array, int $startIndex, $newElements)
{
return $this->insertBefore($array, $startIndex + 1, $newElements);
}
public function insertBefore(array $array, int $startIndex, $newElements)
{
return array_splice($array, $startIndex, self::KEEP_EXISTING_ELEMENTS, $newElements);
}
}

After working on this for a few days, here was the easiest solution I could find.
$indexnumbertoaddat // this is a variable that points to the index # where you
want the new array to be inserted
$arrayToAdd = array(array('key' => $value, 'key' => $value)); //this is the new
array and it's values that you want to add. //the key here is to write it like
array(array('key' =>, since you're adding this array inside another array. This
is the point that a lot of answer left out.
array_splice($originalArray, $indexnumbertoaddatt, 0, $arrayToAdd); //the actual
splice function. You're doing it to $originalArray, at the index # you define,
0 means you're just shifting all other index items down 1, and then you add the
new array.

Related

I have an nested array and I want to transform it in single array. I don't know how to do it [duplicate]

Is it possible, in PHP, to flatten a (bi/multi)dimensional array without using recursion or references?
I'm only interested in the values so the keys can be ignored, I'm thinking in the lines of array_map() and array_values().
As of PHP 5.3 the shortest solution seems to be array_walk_recursive() with the new closures syntax:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
You can use the Standard PHP Library (SPL) to "hide" the recursion.
$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));
foreach($it as $v) {
echo $v, " ";
}
prints
1 2 3 4 5 6 7 8 9
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
Solution for 2 dimensional array
Please try this :
$array = your array
$result = call_user_func_array('array_merge', $array);
echo "<pre>";
print_r($result);
EDIT : 21-Aug-13
Here is the solution which works for multi-dimensional array :
function array_flatten($array) {
$return = array();
foreach ($array as $key => $value) {
if (is_array($value)){
$return = array_merge($return, array_flatten($value));
} else {
$return[$key] = $value;
}
}
return $return;
}
$array = Your array
$result = array_flatten($array);
echo "<pre>";
print_r($result);
Ref: http://php.net/manual/en/function.call-user-func-array.php
To flatten w/o recursion (as you have asked for), you can use a stack. Naturally you can put this into a function of it's own like array_flatten. The following is a version that works w/o keys:.
function array_flatten(array $array)
{
$flat = array(); // initialize return array
$stack = array_values($array); // initialize stack
while($stack) // process stack until done
{
$value = array_shift($stack);
if (is_array($value)) // a value to further process
{
array_unshift($stack, ...$value);
}
else // a value to take
{
$flat[] = $value;
}
}
return $flat;
}
Elements are processed in their order. Because subelements will be moved on top of the stack, they will be processed next.
It's possible to take keys into account as well, however, you'll need a different strategy to handle the stack. That's needed because you need to deal with possible duplicate keys in the sub-arrays. A similar answer in a related question: PHP Walk through multidimensional array while preserving keys
I'm not specifically sure, but I I had tested this in the past: The RecurisiveIterator does use recursion, so it depends on what you really need. Should be possible to create a recursive iterator based on stacks as well:
foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}
Demo
I didn't make it so far, to implement the stack based on RecursiveIterator which I think is a nice idea.
Just thought I'd point out that this is a fold, so array_reduce can be used:
array_reduce($my_array, 'array_merge', array());
EDIT: Note that this can be composed to flatten any number of levels. We can do this in several ways:
// Reduces one level
$concat = function($x) { return array_reduce($x, 'array_merge', array()); };
// We can compose $concat with itself $n times, then apply it to $x
// This can overflow the stack for large $n
$compose = function($f, $g) {
return function($x) use ($f, $g) { return $f($g($x)); };
};
$identity = function($x) { return $x; };
$flattenA = function($n) use ($compose, $identity, $concat) {
return function($x) use ($compose, $identity, $concat, $n) {
return ($n === 0)? $x
: call_user_func(array_reduce(array_fill(0, $n, $concat),
$compose,
$identity),
$x);
};
};
// We can iteratively apply $concat to $x, $n times
$uncurriedFlip = function($f) {
return function($a, $b) use ($f) {
return $f($b, $a);
};
};
$iterate = function($f) use ($uncurriedFlip) {
return function($n) use ($uncurriedFlip, $f) {
return function($x) use ($uncurriedFlip, $f, $n) {
return ($n === 0)? $x
: array_reduce(array_fill(0, $n, $f),
$uncurriedFlip('call_user_func'),
$x);
}; };
};
$flattenB = $iterate($concat);
// Example usage:
$apply = function($f, $x) {
return $f($x);
};
$curriedFlip = function($f) {
return function($a) use ($f) {
return function($b) use ($f, $a) {
return $f($b, $a);
}; };
};
var_dump(
array_map(
call_user_func($curriedFlip($apply),
array(array(array('A', 'B', 'C'),
array('D')),
array(array(),
array('E')))),
array($flattenA(2), $flattenB(2))));
Of course, we could also use loops but the question asks for a combinator function along the lines of array_map or array_values.
Straightforward and One-liner answer.
function flatten_array(array $array)
{
return iterator_to_array(
new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}
Usage:
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
print_r( flatten_array($array) );
Output (in PsySH):
Array
(
[name] => Allen Linatoc
[age] => 21
[0] => Call of Duty
[1] => Titanfall
[2] => Far Cry
)
Now it's pretty up to you now how you'll handle the keys. Cheers
EDIT (2017-03-01)
Quoting Nigel Alderton's concern/issue:
Just to clarify, this preserves keys (even numeric ones) so values that have the same key are lost. For example $array = ['a',['b','c']] becomes Array ([0] => b, [1] => c ). The 'a' is lost because 'b' also has a key of 0
Quoting Svish's answer:
Just add false as second parameter ($use_keys) to the iterator_to_array call
Uses recursion. Hopefully upon seeing how not-complex it is, your fear of recursion will dissipate once you see how not-complex it is.
function flatten($array) {
if (!is_array($array)) {
// nothing to do if it's not an array
return array($array);
}
$result = array();
foreach ($array as $value) {
// explode the sub-array, and add the parts
$result = array_merge($result, flatten($value));
}
return $result;
}
$arr = array('foo', array('nobody', 'expects', array('another', 'level'), 'the', 'Spanish', 'Inquisition'), 'bar');
echo '<ul>';
foreach (flatten($arr) as $value) {
echo '<li>', $value, '</li>';
}
echo '<ul>';
Output:
<ul><li>foo</li><li>nobody</li><li>expects</li><li>another</li><li>level</li><li>the</li><li>Spanish</li><li>Inquisition</li><li>bar</li><ul>
Flattens two dimensional arrays only:
$arr = [1, 2, [3, 4]];
$arr = array_reduce($arr, function ($a, $b) {
return array_merge($a, (array) $b);
}, []);
// Result: [1, 2, 3, 4]
This solution is non-recursive. Note that the order of the elements will be somewhat mixed.
function flatten($array) {
$return = array();
while(count($array)) {
$value = array_shift($array);
if(is_array($value))
foreach($value as $sub)
$array[] = $sub;
else
$return[] = $value;
}
return $return;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
Try the following simple function:
function _flatten_array($arr) {
while ($arr) {
list($key, $value) = each($arr);
is_array($value) ? $arr = $value : $out[$key] = $value;
unset($arr[$key]);
}
return (array)$out;
}
So from this:
array (
'und' =>
array (
'profiles' =>
array (
0 =>
array (
'commerce_customer_address' =>
array (
'und' =>
array (
0 =>
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
),
),
),
),
),
),
)
you get:
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
The trick is passing the both the source and destination arrays by reference.
function flatten_array(&$arr, &$dst) {
if(!isset($dst) || !is_array($dst)) {
$dst = array();
}
if(!is_array($arr)) {
$dst[] = $arr;
} else {
foreach($arr as &$subject) {
flatten_array($subject, $dst);
}
}
}
$recursive = array('1', array('2','3',array('4',array('5','6')),'7',array(array(array('8'),'9'),'10')));
echo "Recursive: \r\n";
print_r($recursive);
$flat = null;
flatten_array($recursive, $flat);
echo "Flat: \r\n";
print_r($flat);
// If you change line 3 to $dst[] = &$arr; , you won't waste memory,
// since all you're doing is copying references, and imploding the array
// into a string will be both memory efficient and fast:)
echo "String:\r\n";
echo implode(',',$flat);
If you really don't like a recursion ... try shifting instead :)
$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
$o = [];
for ($i=0; $i<count($a); $i++) {
if (is_array($a[$i])) {
array_splice($a, $i+1, 0, $a[$i]);
} else {
$o[] = $a[$i];
}
}
Note: In this simple version, this does not support array keys.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* For merging values of a multidimensional array into one
*
* $array = [
* 0 => [
* 0 => 'a1',
* 1 => 'b1',
* 2 => 'c1',
* 3 => 'd1'
* ],
* 1 => [
* 0 => 'a2',
* 1 => 'b2',
* 2 => 'c2',
* ]
* ];
*
* becomes :
*
* $array = [
* 0 => 'a1',
* 1 => 'b1',
* 2 => 'c1',
* 3 => 'd1',
* 4 => 'a2',
* 5 => 'b2',
* 6 => 'c2',
*
* ]
*/
array_reduce
(
$multiArray
, function ($lastItem, $currentItem) {
$lastItem = $lastItem ?: array();
return array_merge($lastItem, array_values($currentItem));
}
);
Gist snippet
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
For php 5.2
function flatten(array $array) {
$result = array();
if (is_array($array)) {
foreach ($array as $k => $v) {
if (is_array($v)) {
$result = array_merge($result, flatten($v));
} else {
$result[] = $v;
}
}
}
return $result;
}
This version can do deep, shallow, or a specific number of levels:
/**
* #param array|object $array array of mixed values to flatten
* #param int|boolean $level 0:deep, 1:shallow, 2:2 levels, 3...
* #return array
*/
function flatten($array, $level = 0) {
$level = (int) $level;
$result = array();
foreach ($array as $i => $v) {
if (0 <= $level && is_array($v)) {
$v = flatten($v, $level > 1 ? $level - 1 : 0 - $level);
$result = array_merge($result, $v);
} elseif (is_int($i)) {
$result[] = $v;
} else {
$result[$i] = $v;
}
}
return $result;
}
Because the code in here looks scary. Here is a function that will also convert a multidimensional array into html form compatible syntax, but which is easier to read.
/**
* Flattens a multi demensional array into a one dimensional
* to be compatible with hidden html fields.
*
* #param array $array
* Array in the form:
* array(
* 'a' => array(
* 'b' => '1'
* )
* )
*
* #return array
* Array in the form:
* array(
* 'a[b]' => 1,
* )
*/
function flatten_array($array) {
// Continue until $array is a one-dimensional array.
$continue = TRUE;
while ($continue) {
$continue = FALSE;
// Walk through top and second level of $array and move
// all values in the second level up one level.
foreach ($array as $key => $value) {
if (is_array($value)) {
// Second level found, therefore continue.
$continue = TRUE;
// Move each value a level up.
foreach ($value as $child_key => $child_value) {
$array[$key . '[' . $child_key . ']'] = $child_value;
}
// Remove second level array from top level.
unset($array[$key]);
}
}
}
return $array;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
I needed to represent PHP multidimensional array in HTML input format.
$test = [
'a' => [
'b' => [
'c' => ['a', 'b']
]
],
'b' => 'c',
'c' => [
'd' => 'e'
]
];
$flatten = function ($input, $parent = []) use (&$flatten) {
$return = [];
foreach ($input as $k => $v) {
if (is_array($v)) {
$return = array_merge($return, $flatten($v, array_merge($parent, [$k])));
} else {
if ($parent) {
$key = implode('][', $parent) . '][' . $k . ']';
if (substr_count($key, ']') != substr_count($key, '[')) {
$key = preg_replace('/\]/', '', $key, 1);
}
} else {
$key = $k;
}
$return[$key] = $v;
}
}
return $return;
};
die(var_dump( $flatten($test) ));
array(4) {
["a[b][c][0]"]=>
string(1) "a"
["a[b][c][1]"]=>
string(1) "b"
["b"]=>
string(1) "c"
["c[d]"]=>
string(1) "e"
}
If you have an array of objects and want to flatten it with a node, just use this function:
function objectArray_flatten($array,$childField) {
$result = array();
foreach ($array as $node)
{
$result[] = $node;
if(isset($node->$childField))
{
$result = array_merge(
$result,
objectArray_flatten($node->$childField,$childField)
);
unset($node->$childField);
}
}
return $result;
}
This is my solution, using a reference:
function arrayFlatten($array_in, &$array_out){
if(is_array($array_in)){
foreach ($array_in as $element){
arrayFlatten($element, $array_out);
}
}
else{
$array_out[] = $array_in;
}
}
$arr1 = array('1', '2', array(array(array('3'), '4', '5')), array(array('6')));
arrayFlatten($arr1, $arr2);
echo "<pre>";
print_r($arr2);
echo "</pre>";
<?php
//recursive solution
//test array
$nested_array = [[1,2,[3]],4,[5],[[[6,[7=>[7,8,9,10]]]]]];
/*-----------------------------------------
function call and return result to an array
------------------------------------------*/
$index_count = 1;
$flatered_array = array();
$flatered_array = flat_array($nested_array, $index_count);
/*-----------------------------------------
Print Result
-----------------------------------------*/
echo "<pre>";
print_r($flatered_array);
/*-----------------------------------------
function to flaten an array
-----------------------------------------*/
function flat_array($nested_array, & $index_count, & $flatered_array) {
foreach($nested_array AS $key=>$val) {
if(is_array($val)) {
flat_array($val, $index_count, $flatered_array);
}
else {
$flatered_array[$index_count] = $val;
++$index_count;
}
}
return $flatered_array;
}
?>

How can i merge array inside multidimensional array [duplicate]

Is it possible, in PHP, to flatten a (bi/multi)dimensional array without using recursion or references?
I'm only interested in the values so the keys can be ignored, I'm thinking in the lines of array_map() and array_values().
As of PHP 5.3 the shortest solution seems to be array_walk_recursive() with the new closures syntax:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
You can use the Standard PHP Library (SPL) to "hide" the recursion.
$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));
foreach($it as $v) {
echo $v, " ";
}
prints
1 2 3 4 5 6 7 8 9
In PHP 5.6 and above you can flatten two dimensional arrays with array_merge after unpacking the outer array with ... operator. The code is simple and clear.
array_merge(...$a);
This works with collection of associative arrays too.
$a = [[10, 20], [30, 40]];
$b = [["x" => "A", "y" => "B"], ["y" => "C", "z" => "D"]];
print_r(array_merge(...$a));
print_r(array_merge(...$b));
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
)
Array
(
[x] => A
[y] => C
[z] => D
)
In PHP 8.0 and below, array unpacking does not work when the outer array has non numeric keys. Support for unpacking array with string keys is available from PHP 8.1. To support 8.0 and below, you should call array_values first.
$c = ["a" => ["x" => "A", "y" => "B"], "b" => ["y" => "C", "z" => "D"]];
print_r(array_merge(...array_values($c)));
Array
(
[x] => A
[y] => C
[z] => D
)
Update: Based on comment by #MohamedGharib (for PHP 7.3.x and older ref)
This will throw an error if the outer array is empty, since array_merge would be called with zero arguments. It can be be avoided by adding an empty array as the first argument.
array_merge([], ...$a);
Solution for 2 dimensional array
Please try this :
$array = your array
$result = call_user_func_array('array_merge', $array);
echo "<pre>";
print_r($result);
EDIT : 21-Aug-13
Here is the solution which works for multi-dimensional array :
function array_flatten($array) {
$return = array();
foreach ($array as $key => $value) {
if (is_array($value)){
$return = array_merge($return, array_flatten($value));
} else {
$return[$key] = $value;
}
}
return $return;
}
$array = Your array
$result = array_flatten($array);
echo "<pre>";
print_r($result);
Ref: http://php.net/manual/en/function.call-user-func-array.php
To flatten w/o recursion (as you have asked for), you can use a stack. Naturally you can put this into a function of it's own like array_flatten. The following is a version that works w/o keys:.
function array_flatten(array $array)
{
$flat = array(); // initialize return array
$stack = array_values($array); // initialize stack
while($stack) // process stack until done
{
$value = array_shift($stack);
if (is_array($value)) // a value to further process
{
array_unshift($stack, ...$value);
}
else // a value to take
{
$flat[] = $value;
}
}
return $flat;
}
Elements are processed in their order. Because subelements will be moved on top of the stack, they will be processed next.
It's possible to take keys into account as well, however, you'll need a different strategy to handle the stack. That's needed because you need to deal with possible duplicate keys in the sub-arrays. A similar answer in a related question: PHP Walk through multidimensional array while preserving keys
I'm not specifically sure, but I I had tested this in the past: The RecurisiveIterator does use recursion, so it depends on what you really need. Should be possible to create a recursive iterator based on stacks as well:
foreach(new FlatRecursiveArrayIterator($array) as $key => $value)
{
echo "** ($key) $value\n";
}
Demo
I didn't make it so far, to implement the stack based on RecursiveIterator which I think is a nice idea.
Just thought I'd point out that this is a fold, so array_reduce can be used:
array_reduce($my_array, 'array_merge', array());
EDIT: Note that this can be composed to flatten any number of levels. We can do this in several ways:
// Reduces one level
$concat = function($x) { return array_reduce($x, 'array_merge', array()); };
// We can compose $concat with itself $n times, then apply it to $x
// This can overflow the stack for large $n
$compose = function($f, $g) {
return function($x) use ($f, $g) { return $f($g($x)); };
};
$identity = function($x) { return $x; };
$flattenA = function($n) use ($compose, $identity, $concat) {
return function($x) use ($compose, $identity, $concat, $n) {
return ($n === 0)? $x
: call_user_func(array_reduce(array_fill(0, $n, $concat),
$compose,
$identity),
$x);
};
};
// We can iteratively apply $concat to $x, $n times
$uncurriedFlip = function($f) {
return function($a, $b) use ($f) {
return $f($b, $a);
};
};
$iterate = function($f) use ($uncurriedFlip) {
return function($n) use ($uncurriedFlip, $f) {
return function($x) use ($uncurriedFlip, $f, $n) {
return ($n === 0)? $x
: array_reduce(array_fill(0, $n, $f),
$uncurriedFlip('call_user_func'),
$x);
}; };
};
$flattenB = $iterate($concat);
// Example usage:
$apply = function($f, $x) {
return $f($x);
};
$curriedFlip = function($f) {
return function($a) use ($f) {
return function($b) use ($f, $a) {
return $f($b, $a);
}; };
};
var_dump(
array_map(
call_user_func($curriedFlip($apply),
array(array(array('A', 'B', 'C'),
array('D')),
array(array(),
array('E')))),
array($flattenA(2), $flattenB(2))));
Of course, we could also use loops but the question asks for a combinator function along the lines of array_map or array_values.
Straightforward and One-liner answer.
function flatten_array(array $array)
{
return iterator_to_array(
new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array)));
}
Usage:
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
print_r( flatten_array($array) );
Output (in PsySH):
Array
(
[name] => Allen Linatoc
[age] => 21
[0] => Call of Duty
[1] => Titanfall
[2] => Far Cry
)
Now it's pretty up to you now how you'll handle the keys. Cheers
EDIT (2017-03-01)
Quoting Nigel Alderton's concern/issue:
Just to clarify, this preserves keys (even numeric ones) so values that have the same key are lost. For example $array = ['a',['b','c']] becomes Array ([0] => b, [1] => c ). The 'a' is lost because 'b' also has a key of 0
Quoting Svish's answer:
Just add false as second parameter ($use_keys) to the iterator_to_array call
Uses recursion. Hopefully upon seeing how not-complex it is, your fear of recursion will dissipate once you see how not-complex it is.
function flatten($array) {
if (!is_array($array)) {
// nothing to do if it's not an array
return array($array);
}
$result = array();
foreach ($array as $value) {
// explode the sub-array, and add the parts
$result = array_merge($result, flatten($value));
}
return $result;
}
$arr = array('foo', array('nobody', 'expects', array('another', 'level'), 'the', 'Spanish', 'Inquisition'), 'bar');
echo '<ul>';
foreach (flatten($arr) as $value) {
echo '<li>', $value, '</li>';
}
echo '<ul>';
Output:
<ul><li>foo</li><li>nobody</li><li>expects</li><li>another</li><li>level</li><li>the</li><li>Spanish</li><li>Inquisition</li><li>bar</li><ul>
Flattens two dimensional arrays only:
$arr = [1, 2, [3, 4]];
$arr = array_reduce($arr, function ($a, $b) {
return array_merge($a, (array) $b);
}, []);
// Result: [1, 2, 3, 4]
This solution is non-recursive. Note that the order of the elements will be somewhat mixed.
function flatten($array) {
$return = array();
while(count($array)) {
$value = array_shift($array);
if(is_array($value))
foreach($value as $sub)
$array[] = $sub;
else
$return[] = $value;
}
return $return;
}
I believe this is the cleanest solution without using any mutations nor unfamiliar classes.
<?php
function flatten($array)
{
return array_reduce($array, function($acc, $item){
return array_merge($acc, is_array($item) ? flatten($item) : [$item]);
}, []);
}
// usage
$array = [1, 2, [3, 4], [5, [6, 7]], 8, 9, 10];
print_r(flatten($array));
The Laravel helper for flattening arrays is Arr::flatten()
From PHP v7.4, you can use the spread operator and merge the arrays. Simple and effective.
$flatArr = array_merge(...$originalArray);
Try the following simple function:
function _flatten_array($arr) {
while ($arr) {
list($key, $value) = each($arr);
is_array($value) ? $arr = $value : $out[$key] = $value;
unset($arr[$key]);
}
return (array)$out;
}
So from this:
array (
'und' =>
array (
'profiles' =>
array (
0 =>
array (
'commerce_customer_address' =>
array (
'und' =>
array (
0 =>
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
),
),
),
),
),
),
)
you get:
array (
'first_name' => 'First name',
'last_name' => 'Last name',
'thoroughfare' => 'Address 1',
'premise' => 'Address 2',
'locality' => 'Town/City',
'administrative_area' => 'County',
'postal_code' => 'Postcode',
)
You can do it with ouzo goodies:
$result = Arrays::flatten($multidimensional);
See: Here
If you want to keep also your keys that is solution.
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
return $return;
}
Unfortunately it outputs only final nested arrays, without middle keys. So for the following example:
$array = array(
'sweet' => array(
'a' => 'apple',
'b' => 'banana'),
'sour' => 'lemon');
print_r(flatten($fruits));
Output is:
Array
(
[a] => apple
[b] => banana
[sour] => lemon
)
The trick is passing the both the source and destination arrays by reference.
function flatten_array(&$arr, &$dst) {
if(!isset($dst) || !is_array($dst)) {
$dst = array();
}
if(!is_array($arr)) {
$dst[] = $arr;
} else {
foreach($arr as &$subject) {
flatten_array($subject, $dst);
}
}
}
$recursive = array('1', array('2','3',array('4',array('5','6')),'7',array(array(array('8'),'9'),'10')));
echo "Recursive: \r\n";
print_r($recursive);
$flat = null;
flatten_array($recursive, $flat);
echo "Flat: \r\n";
print_r($flat);
// If you change line 3 to $dst[] = &$arr; , you won't waste memory,
// since all you're doing is copying references, and imploding the array
// into a string will be both memory efficient and fast:)
echo "String:\r\n";
echo implode(',',$flat);
If you really don't like a recursion ... try shifting instead :)
$a = array(1,2,array(3,4, array(5,6,7), 8), 9);
$o = [];
for ($i=0; $i<count($a); $i++) {
if (is_array($a[$i])) {
array_splice($a, $i+1, 0, $a[$i]);
} else {
$o[] = $a[$i];
}
}
Note: In this simple version, this does not support array keys.
How about using a recursive generator? https://ideone.com/d0TXCg
<?php
$array = [
'name' => 'Allen Linatoc',
'profile' => [
'age' => 21,
'favourite_games' => [ 'Call of Duty', 'Titanfall', 'Far Cry' ]
]
];
foreach (iterate($array) as $item) {
var_dump($item);
};
function iterate($array)
{
foreach ($array as $item) {
if (is_array($item)) {
yield from iterate($item);
} else {
yield $item;
}
}
}
/**
* For merging values of a multidimensional array into one
*
* $array = [
* 0 => [
* 0 => 'a1',
* 1 => 'b1',
* 2 => 'c1',
* 3 => 'd1'
* ],
* 1 => [
* 0 => 'a2',
* 1 => 'b2',
* 2 => 'c2',
* ]
* ];
*
* becomes :
*
* $array = [
* 0 => 'a1',
* 1 => 'b1',
* 2 => 'c1',
* 3 => 'd1',
* 4 => 'a2',
* 5 => 'b2',
* 6 => 'c2',
*
* ]
*/
array_reduce
(
$multiArray
, function ($lastItem, $currentItem) {
$lastItem = $lastItem ?: array();
return array_merge($lastItem, array_values($currentItem));
}
);
Gist snippet
Anyone looking for a really clean solution to this; here's an option:
Taking an array of arrays with various key value configurations:
$test_array = array(
array('test' => 0, 0, 0, 0),
array(0, 0, 'merp' => array('herp' => 'derp'), 0),
array(0, 0, 0, 0),
array(0, 0, 0, 0)
);
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($test_array));
var_dump( iterator_to_array($it, false) ) ;
This will take only the values from each array and return a single flat array.
Output of values in result:
0 0 0 0 0 0 derp 0 0 0 0 0 0 0 0 0
For php 5.2
function flatten(array $array) {
$result = array();
if (is_array($array)) {
foreach ($array as $k => $v) {
if (is_array($v)) {
$result = array_merge($result, flatten($v));
} else {
$result[] = $v;
}
}
}
return $result;
}
This version can do deep, shallow, or a specific number of levels:
/**
* #param array|object $array array of mixed values to flatten
* #param int|boolean $level 0:deep, 1:shallow, 2:2 levels, 3...
* #return array
*/
function flatten($array, $level = 0) {
$level = (int) $level;
$result = array();
foreach ($array as $i => $v) {
if (0 <= $level && is_array($v)) {
$v = flatten($v, $level > 1 ? $level - 1 : 0 - $level);
$result = array_merge($result, $v);
} elseif (is_int($i)) {
$result[] = $v;
} else {
$result[$i] = $v;
}
}
return $result;
}
Because the code in here looks scary. Here is a function that will also convert a multidimensional array into html form compatible syntax, but which is easier to read.
/**
* Flattens a multi demensional array into a one dimensional
* to be compatible with hidden html fields.
*
* #param array $array
* Array in the form:
* array(
* 'a' => array(
* 'b' => '1'
* )
* )
*
* #return array
* Array in the form:
* array(
* 'a[b]' => 1,
* )
*/
function flatten_array($array) {
// Continue until $array is a one-dimensional array.
$continue = TRUE;
while ($continue) {
$continue = FALSE;
// Walk through top and second level of $array and move
// all values in the second level up one level.
foreach ($array as $key => $value) {
if (is_array($value)) {
// Second level found, therefore continue.
$continue = TRUE;
// Move each value a level up.
foreach ($value as $child_key => $child_value) {
$array[$key . '[' . $child_key . ']'] = $child_value;
}
// Remove second level array from top level.
unset($array[$key]);
}
}
}
return $array;
}
If you want to keep intermediate keys:
function flattenArray(array &$result, $value, string $key = "")
{
if (!is_array($value)) {
$result[$key] = $value;
return $result;
}
foreach ($value as $subKey => $subArray) {
$newKey = $key !== "" ? $key . "_" . $subKey : $subKey;
flattenArray($result, $subArray, $newKey);
}
return $result;
}
$nestedArray = [
"name" => "John",
"pets" => [
["id" => 1, "name" => "snooop"],
["id" => 2, "name" => "medor"],
],
"job" => ["title" => "developper"],
];
$intermediateResult = [];
$flattened = flattenArray($intermediateResult, $nestedArray);
var_dump($flattened);
This will output:
array(6) {
["name"]=>
string(4) "John"
["pets_0_id"]=>
int(1)
["pets_0_name"]=>
string(6) "snooop"
["pets_1_id"]=>
int(2)
["pets_1_name"]=>
string(5) "medor"
["job_title"]=>
string(10) "developper"
}
See https://ideone.com/KXLtzZ#stdout
Non recursive, non references based implementation, as asked, which may be easier to understand than a recursion based implemetation.
Can manage arbitrary deep multidimensional arrays, can't flatten associative arrays.
It works by flattening the array one level per cycle, until it is completly valid.
function array_flatten(): array{
$result = func_get_args();
// check all elements of $list are not arrays
$_is_flat = function (array $list): bool {
foreach ($list as $val) {
if (is_array($val)) {
return false;
}
}
return true;
};
do {
$tmp = [];
foreach ($result as $val) {
if (is_array($val)) {
if (!array_is_list($val)) {
throw new \Exception(sprintf("array_flatten can't handle associative arrays: %s", json_encode($val)));
}
$tmp = array_merge($tmp, $val);
} else {
$tmp[] = $val;
}
}
$result = $tmp;
} while (!$_is_flat($result));
return $result;
}
This are the cases it handles:
assertEquals(array_flatten(1, 2), $expected = [1, 2], 'array_flatten 1a');
assertEquals(array_flatten([1], [2]), $expected = [1, 2], 'array_flatten 1b');
assertEquals(array_flatten([1], [[2], 3]), $expected = [1, 2, 3], 'array_flatten 1c');
assertEquals(array_flatten(1, [2, 3], [4, 5]), $expected = [1, 2, 3, 4, 5], 'array_flatten 2');
assertEquals(array_flatten(2, 3, [4, 5], [6, 7], 8), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 3');
assertEquals(array_flatten([2, 3, [4, 5], [6, 7], 8]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten 4');
assertEquals(array_flatten([2, [3, [4, [5]], [6, [7]], 8]]), $expected = [2, 3, 4, 5, 6, 7, 8], 'array_flatten complex');
I needed to represent PHP multidimensional array in HTML input format.
$test = [
'a' => [
'b' => [
'c' => ['a', 'b']
]
],
'b' => 'c',
'c' => [
'd' => 'e'
]
];
$flatten = function ($input, $parent = []) use (&$flatten) {
$return = [];
foreach ($input as $k => $v) {
if (is_array($v)) {
$return = array_merge($return, $flatten($v, array_merge($parent, [$k])));
} else {
if ($parent) {
$key = implode('][', $parent) . '][' . $k . ']';
if (substr_count($key, ']') != substr_count($key, '[')) {
$key = preg_replace('/\]/', '', $key, 1);
}
} else {
$key = $k;
}
$return[$key] = $v;
}
}
return $return;
};
die(var_dump( $flatten($test) ));
array(4) {
["a[b][c][0]"]=>
string(1) "a"
["a[b][c][1]"]=>
string(1) "b"
["b"]=>
string(1) "c"
["c[d]"]=>
string(1) "e"
}
If you have an array of objects and want to flatten it with a node, just use this function:
function objectArray_flatten($array,$childField) {
$result = array();
foreach ($array as $node)
{
$result[] = $node;
if(isset($node->$childField))
{
$result = array_merge(
$result,
objectArray_flatten($node->$childField,$childField)
);
unset($node->$childField);
}
}
return $result;
}
This is my solution, using a reference:
function arrayFlatten($array_in, &$array_out){
if(is_array($array_in)){
foreach ($array_in as $element){
arrayFlatten($element, $array_out);
}
}
else{
$array_out[] = $array_in;
}
}
$arr1 = array('1', '2', array(array(array('3'), '4', '5')), array(array('6')));
arrayFlatten($arr1, $arr2);
echo "<pre>";
print_r($arr2);
echo "</pre>";
<?php
//recursive solution
//test array
$nested_array = [[1,2,[3]],4,[5],[[[6,[7=>[7,8,9,10]]]]]];
/*-----------------------------------------
function call and return result to an array
------------------------------------------*/
$index_count = 1;
$flatered_array = array();
$flatered_array = flat_array($nested_array, $index_count);
/*-----------------------------------------
Print Result
-----------------------------------------*/
echo "<pre>";
print_r($flatered_array);
/*-----------------------------------------
function to flaten an array
-----------------------------------------*/
function flat_array($nested_array, & $index_count, & $flatered_array) {
foreach($nested_array AS $key=>$val) {
if(is_array($val)) {
flat_array($val, $index_count, $flatered_array);
}
else {
$flatered_array[$index_count] = $val;
++$index_count;
}
}
return $flatered_array;
}
?>

How to insert element into arrays at specific position?

Let's imagine that we have two arrays:
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
$array_2 = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
Now, I'd like to insert array('sample_key' => 'sample_value') after third element of each array. How can I do it?
array_slice() can be used to extract parts of the array, and the union array operator (+) can recombine the parts.
$res = array_slice($array, 0, 3, true) +
array("my_key" => "my_value") +
array_slice($array, 3, count($array)-3, true);
This example:
$array = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
$res = array_slice($array, 0, 3, true) +
array("my_key" => "my_value") +
array_slice($array, 3, count($array) - 1, true) ;
print_r($res);
gives:
Array
(
[zero] => 0
[one] => 1
[two] => 2
[my_key] => my_value
[three] => 3
)
For your first array, use array_splice():
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
array_splice($array_1, 3, 0, 'more');
print_r($array_1);
output:
Array(
[0] => zero
[1] => one
[2] => two
[3] => more
[4] => three
)
for the second one there is no order so you just have to do :
$array_2['more'] = '2.5';
print_r($array_2);
And sort the keys by whatever you want.
code:
function insertValueAtPosition($arr, $insertedArray, $position) {
$i = 0;
$new_array=[];
foreach ($arr as $key => $value) {
if ($i == $position) {
foreach ($insertedArray as $ikey => $ivalue) {
$new_array[$ikey] = $ivalue;
}
}
$new_array[$key] = $value;
$i++;
}
return $new_array;
}
example:
$array = ["A"=8, "K"=>3];
$insert_array = ["D"= 9];
insertValueAtPosition($array, $insert_array, $position=2);
// result ====> ["A"=>8, "D"=>9, "K"=>3];
May not really look perfect, but it works.
Here's a simple function that you could use. Just plug n play.
This is Insert By Index, Not By Value.
you can choose to pass the array, or use one that you already have declared.
Newer, shorter version:
function insert($array, $index, $val)
{
$size = count($array); //because I am going to use this more than one time
if (!is_int($index) || $index < 0 || $index > $size)
{
return -1;
}
else
{
$temp = array_slice($array, 0, $index);
$temp[] = $val;
return array_merge($temp, array_slice($array, $index, $size));
}
}
Older, longer version:
function insert($array, $index, $val) { //function decleration
$temp = array(); // this temp array will hold the value
$size = count($array); //because I am going to use this more than one time
// Validation -- validate if index value is proper (you can omit this part)
if (!is_int($index) || $index < 0 || $index > $size) {
echo "Error: Wrong index at Insert. Index: " . $index . " Current Size: " . $size;
echo "<br/>";
return false;
}
//here is the actual insertion code
//slice part of the array from 0 to insertion index
$temp = array_slice($array, 0, $index);//e.g index=5, then slice will result elements [0-4]
//add the value at the end of the temp array// at the insertion index e.g 5
array_push($temp, $val);
//reconnect the remaining part of the array to the current temp
$temp = array_merge($temp, array_slice($array, $index, $size));
$array = $temp;//swap// no need for this if you pass the array cuz you can simply return $temp, but, if u r using a class array for example, this is useful.
return $array; // you can return $temp instead if you don't use class array
}
Usage example:
//1
$result = insert(array(1,2,3,4,5),0, 0);
echo "<pre>";
echo "<br/>";
print_r($result);
echo "</pre>";
//2
$result = insert(array(1,2,3,4,5),2, "a");
echo "<pre>";
print_r($result);
echo "</pre>";
//3
$result = insert(array(1,2,3,4,5) ,4, "b");
echo "<pre>";
print_r($result);
echo "</pre>";
//4
$result = insert(array(1,2,3,4,5),5, 6);
echo "<pre>";
echo "<br/>";
print_r($result);
echo "</pre>";
Expected result:
//1
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)
//2
Array
(
[0] => 1
[1] => 2
[2] => a
[3] => 3
[4] => 4
[5] => 5
)
//3
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => b
[5] => 5
)
//4
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
)
$list = array(
'Tunisia' => 'Tunis',
'Germany' => 'Berlin',
'Italy' => 'Rom',
'Egypt' => 'Cairo'
);
$afterIndex = 2;
$newVal= array('Palestine' => 'Jerusalem');
$newList = array_merge(array_slice($list,0,$afterIndex+1), $newVal,array_slice($list,$afterIndex+1));
This function supports:
both numeric and assoc keys
insert before or after the founded key
append to the end of array if key isn't founded
function insert_into_array( $array, $search_key, $insert_key, $insert_value, $insert_after_founded_key = true, $append_if_not_found = false ) {
$new_array = array();
foreach( $array as $key => $value ){
// INSERT BEFORE THE CURRENT KEY?
// ONLY IF CURRENT KEY IS THE KEY WE ARE SEARCHING FOR, AND WE WANT TO INSERT BEFORE THAT FOUNDED KEY
if( $key === $search_key && ! $insert_after_founded_key )
$new_array[ $insert_key ] = $insert_value;
// COPY THE CURRENT KEY/VALUE FROM OLD ARRAY TO A NEW ARRAY
$new_array[ $key ] = $value;
// INSERT AFTER THE CURRENT KEY?
// ONLY IF CURRENT KEY IS THE KEY WE ARE SEARCHING FOR, AND WE WANT TO INSERT AFTER THAT FOUNDED KEY
if( $key === $search_key && $insert_after_founded_key )
$new_array[ $insert_key ] = $insert_value;
}
// APPEND IF KEY ISNT FOUNDED
if( $append_if_not_found && count( $array ) == count( $new_array ) )
$new_array[ $insert_key ] = $insert_value;
return $new_array;
}
USAGE:
$array1 = array(
0 => 'zero',
1 => 'one',
2 => 'two',
3 => 'three',
4 => 'four'
);
$array2 = array(
'zero' => '# 0',
'one' => '# 1',
'two' => '# 2',
'three' => '# 3',
'four' => '# 4'
);
$array3 = array(
0 => 'zero',
1 => 'one',
64 => '64',
3 => 'three',
4 => 'four'
);
// INSERT AFTER WITH NUMERIC KEYS
print_r( insert_into_array( $array1, 3, 'three+', 'three+ value') );
// INSERT AFTER WITH ASSOC KEYS
print_r( insert_into_array( $array2, 'three', 'three+', 'three+ value') );
// INSERT BEFORE
print_r( insert_into_array( $array3, 64, 'before-64', 'before-64 value', false) );
// APPEND IF SEARCH KEY ISNT FOUNDED
print_r( insert_into_array( $array3, 'undefined assoc key', 'new key', 'new value', true, true) );
RESULTS:
Array
(
[0] => zero
[1] => one
[2] => two
[3] => three
[three+] => three+ value
[4] => four
)
Array
(
[zero] => # 0
[one] => # 1
[two] => # 2
[three] => # 3
[three+] => three+ value
[four] => # 4
)
Array
(
[0] => zero
[1] => one
[before-64] => before-64 value
[64] => 64
[3] => three
[4] => four
)
Array
(
[0] => zero
[1] => one
[64] => 64
[3] => three
[4] => four
[new key] => new value
)
Simplest solution, if you want to insert (an element or array) after a certain key:
function array_splice_after_key($array, $key, $array_to_insert)
{
$key_pos = array_search($key, array_keys($array));
if($key_pos !== false){
$key_pos++;
$second_array = array_splice($array, $key_pos);
$array = array_merge($array, $array_to_insert, $second_array);
}
return $array;
}
So, if you have:
$array = [
'one' => 1,
'three' => 3
];
$array_to_insert = ['two' => 2];
And execute:
$result_array = array_splice_after_key($array, 'one', $array_to_insert);
You'll have:
Array (
['one'] => 1
['two'] => 2
['three'] => 3
)
I recently wrote a function to do something similar to what it sounds like you're attempting, it's a similar approach to clasvdb's answer.
function magic_insert($index,$value,$input_array ) {
if (isset($input_array[$index])) {
$output_array = array($index=>$value);
foreach($input_array as $k=>$v) {
if ($k<$index) {
$output_array[$k] = $v;
} else {
if (isset($output_array[$k]) ) {
$output_array[$k+1] = $v;
} else {
$output_array[$k] = $v;
}
}
}
} else {
$output_array = $input_array;
$output_array[$index] = $value;
}
ksort($output_array);
return $output_array;
}
Basically it inserts at a specific point, but avoids overwriting by shifting all items down.
Using array_splice instead of array_slice gives one less function call.
$toto = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3'
);
$ret = array_splice($toto, 3 );
$toto = $toto + array("my_key" => "my_value") + $ret;
print_r($toto);
If you don't know that you want to insert it at position #3, but you know the key that you want to insert it after, I cooked up this little function after seeing this question.
/**
* Inserts any number of scalars or arrays at the point
* in the haystack immediately after the search key ($needle) was found,
* or at the end if the needle is not found or not supplied.
* Modifies $haystack in place.
* #param array &$haystack the associative array to search. This will be modified by the function
* #param string $needle the key to search for
* #param mixed $stuff one or more arrays or scalars to be inserted into $haystack
* #return int the index at which $needle was found
*/
function array_insert_after(&$haystack, $needle = '', $stuff){
if (! is_array($haystack) ) return $haystack;
$new_array = array();
for ($i = 2; $i < func_num_args(); ++$i){
$arg = func_get_arg($i);
if (is_array($arg)) $new_array = array_merge($new_array, $arg);
else $new_array[] = $arg;
}
$i = 0;
foreach($haystack as $key => $value){
++$i;
if ($key == $needle) break;
}
$haystack = array_merge(array_slice($haystack, 0, $i, true), $new_array, array_slice($haystack, $i, null, true));
return $i;
}
Here's a codepad fiddle to see it in action: http://codepad.org/5WlKFKfz
Note: array_splice() would have been a lot more efficient than array_merge(array_slice()) but then the keys of your inserted arrays would have been lost. Sigh.
Cleaner approach (based on fluidity of use and less code).
/**
* Insert data at position given the target key.
*
* #param array $array
* #param mixed $target_key
* #param mixed $insert_key
* #param mixed $insert_val
* #param bool $insert_after
* #param bool $append_on_fail
* #param array $out
* #return array
*/
function array_insert(
array $array,
$target_key,
$insert_key,
$insert_val = null,
$insert_after = true,
$append_on_fail = false,
$out = [])
{
foreach ($array as $key => $value) {
if ($insert_after) $out[$key] = $value;
if ($key == $target_key) $out[$insert_key] = $insert_val;
if (!$insert_after) $out[$key] = $value;
}
if (!isset($array[$target_key]) && $append_on_fail) {
$out[$insert_key] = $insert_val;
}
return $out;
}
Usage:
$colors = [
'blue' => 'Blue',
'green' => 'Green',
'orange' => 'Orange',
];
$colors = array_insert($colors, 'blue', 'pink', 'Pink');
die(var_dump($colors));
This is an old question, but I posted a comment in 2014 and frequently come back to this. I thought I would leave a full answer. This isn't the shortest solution but it is quite easy to understand.
Insert a new value into an associative array, at a numbered position, preserving keys, and preserving order.
$columns = array(
'id' => 'ID',
'name' => 'Name',
'email' => 'Email',
'count' => 'Number of posts'
);
$columns = array_merge(
array_slice( $columns, 0, 3, true ), // The first 3 items from the old array
array( 'subscribed' => 'Subscribed' ), // New value to add after the 3rd item
array_slice( $columns, 3, null, true ) // Other items after the 3rd
);
print_r( $columns );
/*
Array (
[id] => ID
[name] => Name
[email] => Email
[subscribed] => Subscribed
[count] => Number of posts
)
*/
I do that as
$slightly_damaged = array_merge(
array_slice($slightly_damaged, 0, 4, true) + ["4" => "0.0"],
array_slice($slightly_damaged, 4, count($slightly_damaged) - 4, true)
);
This is another solution in PHP 7.1
/**
* #param array $input Input array to add items to
* #param array $items Items to insert (as an array)
* #param int $position Position to inject items from (starts from 0)
*
* #return array
*/
function arrayInject( array $input, array $items, int $position ): array
{
if (0 >= $position) {
return array_merge($items, $input);
}
if ($position >= count($input)) {
return array_merge($input, $items);
}
return array_merge(
array_slice($input, 0, $position, true),
$items,
array_slice($input, $position, null, true)
);
}
I just created an ArrayHelper class that would make this very easy for numeric indexes.
class ArrayHelper
{
/*
Inserts a value at the given position or throws an exception if
the position is out of range.
This function will push the current values up in index. ex. if
you insert at index 1 then the previous value at index 1 will
be pushed to index 2 and so on.
$pos: The position where the inserted value should be placed.
Starts at 0.
*/
public static function insertValueAtPos(array &$array, $pos, $value) {
$maxIndex = count($array)-1;
if ($pos === 0) {
array_unshift($array, $value);
} elseif (($pos > 0) && ($pos <= $maxIndex)) {
$firstHalf = array_slice($array, 0, $pos);
$secondHalf = array_slice($array, $pos);
$array = array_merge($firstHalf, array($value), $secondHalf);
} else {
throw new IndexOutOfBoundsException();
}
}
}
Example:
$array = array('a', 'b', 'c', 'd', 'e');
$insertValue = 'insert';
\ArrayHelper::insertValueAtPos($array, 3, $insertValue);
Beginning $array:
Array (
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
)
Result:
Array (
[0] => a
[1] => b
[2] => c
[3] => insert
[4] => d
[5] => e
)
This is better method how insert item to array on some position.
function arrayInsert($array, $item, $position)
{
$begin = array_slice($array, 0, $position);
array_push($begin, $item);
$end = array_slice($array, $position);
$resultArray = array_merge($begin, $end);
return $resultArray;
}
I needed something that could do an insert before, replace, after the key; and add at the start or end of the array if target key is not found. Default is to insert after the key.
New Function
/**
* Insert element into an array at a specific key.
*
* #param array $input_array
* The original array.
* #param array $insert
* The element that is getting inserted; array(key => value).
* #param string $target_key
* The key name.
* #param int $location
* 1 is after, 0 is replace, -1 is before.
*
* #return array
* The new array with the element merged in.
*/
function insert_into_array_at_key(array $input_array, array $insert, $target_key, $location = 1) {
$output = array();
$new_value = reset($insert);
$new_key = key($insert);
foreach ($input_array as $key => $value) {
if ($key === $target_key) {
// Insert before.
if ($location == -1) {
$output[$new_key] = $new_value;
$output[$key] = $value;
}
// Replace.
if ($location == 0) {
$output[$new_key] = $new_value;
}
// After.
if ($location == 1) {
$output[$key] = $value;
$output[$new_key] = $new_value;
}
}
else {
// Pick next key if there is an number collision.
if (is_numeric($key)) {
while (isset($output[$key])) {
$key++;
}
}
$output[$key] = $value;
}
}
// Add to array if not found.
if (!isset($output[$new_key])) {
// Before everything.
if ($location == -1) {
$output = $insert + $output;
}
// After everything.
if ($location == 1) {
$output[$new_key] = $new_value;
}
}
return $output;
}
Input code
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
$array_2 = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
$array_1 = insert_into_array_at_key($array_1, array('sample_key' => 'sample_value'), 2, 1);
print_r($array_1);
$array_2 = insert_into_array_at_key($array_2, array('sample_key' => 'sample_value'), 'two', 1);
print_r($array_2);
Output
Array
(
[0] => zero
[1] => one
[2] => two
[sample_key] => sample_value
[3] => three
)
Array
(
[zero] => 0
[one] => 1
[two] => 2
[sample_key] => sample_value
[three] => 3
)
Very simple 2 string answer to your question:
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
At first you insert anything to your third element with array_splice and then assign a value to this element:
array_splice($array_1, 3, 0 , true);
$array_1[3] = array('sample_key' => 'sample_value');
Not as concrete as the answer of Artefacto, but based in his suggestion of using array_slice(), I wrote the next function:
function arrayInsert($target, $byKey, $byOffset, $valuesToInsert, $afterKey) {
if (isset($byKey)) {
if (is_numeric($byKey)) $byKey = (int)floor($byKey);
$offset = 0;
foreach ($target as $key => $value) {
if ($key === $byKey) break;
$offset++;
}
if ($afterKey) $offset++;
} else {
$offset = $byOffset;
}
$targetLength = count($target);
$targetA = array_slice($target, 0, $offset, true);
$targetB = array_slice($target, $offset, $targetLength, true);
return array_merge($targetA, $valuesToInsert, $targetB);
}
Features:
Inserting one or múltiple values
Inserting key value pair(s)
Inserting before/after the key, or by offset
Usage examples:
$target = [
'banana' => 12,
'potatoe' => 6,
'watermelon' => 8,
'apple' => 7,
2 => 21,
'pear' => 6
];
// Values must be nested in an array
$insertValues = [
'orange' => 0,
'lemon' => 3,
3
];
// By key
// Third parameter is not applicable
// Insert after 2 (before 'pear')
var_dump(arrayInsert($target, 2, null, $valuesToInsert, true));
// Insert before 'watermelon'
var_dump(arrayInsert($target, 'watermelon', null, $valuesToInsert, false));
// By offset
// Second and last parameter are not applicable
// Insert in position 2 (zero based i.e. before 'watermelon')
var_dump(arrayInsert($target, null, 2, $valuesToInsert, null));
In case you are just looking to insert an item into an array at a certain position (based on #clausvdb answer):
function array_insert($arr, $insert, $position) {
$i = 0;
$ret = array();
foreach ($arr as $key => $value) {
if ($i == $position) {
$ret[] = $insert;
}
$ret[] = $value;
$i++;
}
return $ret;
}
Here is my version:
/**
*
* Insert an element after an index in an array
* #param array $array
* #param string|int $key
* #param mixed $value
* #param string|int $offset
* #return mixed
*/
function array_splice_associative($array, $key, $value, $offset) {
if (!is_array($array)) {
return $array;
}
if (array_key_exists($key, $array)) {
unset($array[$key]);
}
$return = array();
$inserted = false;
foreach ($array as $k => $v) {
$return[$k] = $v;
if ($k == $offset && !$inserted) {
$return[$key] = $value;
$inserted = true;
}
}
if (!$inserted) {
$return[$key] = $value;
}
return $return;
}
try this one ===
$key_pos=0;
$a1=array("a"=>"red", "b"=>"green", "c"=>"blue", "d"=>"yellow");
$arrkey=array_keys($a1);
array_walk($arrkey,function($val,$key) use(&$key_pos) {
if($val=='b')
{
$key_pos=$key;
}
});
$a2=array("e"=>"purple");
$newArray = array_slice($a1, 0, $key_pos, true) + $a2 +
array_slice($a1, $key_pos, NULL, true);
print_r($newArray);
Output
Array (
[a] => red
[e] => purple
[b] => green
[c] => blue
[d] => yellow )
This can be done by array.splice(). Please note array_splice or array_merge doesn't preserve keys for associative arrays. So array_slice is used and '+' operator is used for concatenating the two arrays.
More details here
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
$array_2 = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
$index = 2;
$finalArray = array_slice($array_1, 0, $index, true) +
$array2 +
array_slice($array_2, $index, NULL, true);
print_r($finalArray);
/*
Array
(
[0] => zero
[1] => one
[10] => grapes
[z] => mangoes
[two] => 2
[three] => 3
)
*/
I've created a function (PHP 8.1), which allows you to insert items to associative or numeric arrays:
function insertItemsToPosition(array $array, string|int $insertAfterPosition, array $itemsToAdd): array
{
$insertAfterIndex = array_search($insertAfterPosition, array_keys($array), true);
if ($insertAfterIndex === false) {
throw new \UnexpectedValueException(sprintf('You try to insert items to an array after the key "%s", but this key is not existing in given array. Available keys are: %s', $insertAfterPosition, implode(', ', array_keys($array))));
}
$itemsBefore = array_slice($array, 0, $insertAfterIndex + 1);
$itemsAfter = array_slice($array, $insertAfterIndex + 1);
return $itemsBefore + $itemsToAdd + $itemsAfter;
}
You can insert elements during a foreach loop, since this loop works on a copy of the original array, but you have to keep track of the number of inserted lines (I call this "bloat" in this code):
$bloat=0;
foreach ($Lines as $n=>$Line)
{
if (MustInsertLineHere($Line))
{
array_splice($Lines,$n+$bloat,0,"string to insert");
++$bloat;
}
}
Obviously, you can generalize this "bloat" idea to handle arbitrary insertions and deletions during the foreach loop.

Switch two items in associative array

Example:
$arr = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
I want to switch the positions of grapefruit and pear, so the array will become
$arr = array(
'apple' => 'sweet',
'pear' => 'tasty',
'grapefruit' => 'bitter',
'banana' => 'yellow'
)
I know the keys and values of the elements I want to switch, is there an easy way to do this? Or will it require a loop + creating a new array?
Thanks
Just a little shorter and less complicated than the solution of arcaneerudite:
<?php
if(!function_exists('array_swap_assoc')) {
function array_swap_assoc($key1, $key2, $array) {
$newArray = array ();
foreach ($array as $key => $value) {
if ($key == $key1) {
$newArray[$key2] = $array[$key2];
} elseif ($key == $key2) {
$newArray[$key1] = $array[$key1];
} else {
$newArray[$key] = $value;
}
}
return $newArray;
}
}
$array = $arrOrig = array(
'fruit' => 'pear',
'veg' => 'cucumber',
'tuber' => 'potato',
'meat' => 'ham'
);
$newArray = array_swap_assoc('veg', 'tuber', $array);
var_dump($array, $newArray);
?>
Tested and works fine
Here's my version of the swap function:
function array_swap_assoc(&$array,$k1,$k2) {
if($k1 === $k2) return; // Nothing to do
$keys = array_keys($array);
$p1 = array_search($k1, $keys);
if($p1 === FALSE) return; // Sanity check...keys must exist
$p2 = array_search($k2, $keys);
if($p2 === FALSE) return;
$keys[$p1] = $k2; // Swap the keys
$keys[$p2] = $k1;
$values = array_values($array);
// Swap the values
list($values[$p1],$values[$p2]) = array($values[$p2],$values[$p1]);
$array = array_combine($keys, $values);
}
if the array comes from the db, add a sort_order field so you can always be sure in what order the elements are in the array.
This may or may not be an option depending on your particular use-case, but if you initialize your array with null values with the appropriate keys before populating it with data, you can set the values in any order and the original key-order will be maintained. So instead of swapping elements, you can prevent the need to swap them entirely:
$arr = array('apple' => null,
'pear' => null,
'grapefruit' => null,
'banana' => null);
...
$arr['apple'] = 'sweet';
$arr['grapefruit'] = 'bitter'; // set grapefruit before setting pear
$arr['pear'] = 'tasty';
$arr['banana'] = 'yellow';
print_r($arr);
>>> Array
(
[apple] => sweet
[pear] => tasty
[grapefruit] => bitter
[banana] => yellow
)
Not entirely sure if this was mentioned, but, the reason this is tricky is because it's non-indexed.
Let's take:
$arrOrig = array(
'fruit'=>'pear',
'veg'=>'cucumber',
'tuber'=>'potato'
);
Get the keys:
$arrKeys = array_keys($arrOrig);
print_r($arrKeys);
Array(
[0]=>fruit
[1]=>veg
[2]=>tuber
)
Get the values:
$arrVals = array_values($arrOrig);
print_r($arrVals);
Array(
[0]=>pear
[1]=>cucumber
[2]=>potato
)
Now you've got 2 arrays that are numerical. Swap the indices of the ones you want to swap, then read the other array back in in the order of the modified numerical array. Let's say we want to swap 'fruit' and 'veg':
$arrKeysFlipped = array_flip($arrKeys);
print_r($arrKeysFlipped);
Array (
[fruit]=>0
[veg]=>1
[tuber]=>2
)
$indexFruit = $arrKeysFlipped['fruit'];
$indexVeg = $arrKeysFlipped['veg'];
$arrKeysFlipped['veg'] = $indexFruit;
$arrKeysFlipped['fruit'] = $indexVeg;
print_r($arrKeysFlipped);
Array (
[fruit]=>1
[veg]=>0
[tuber]=>2
)
Now, you can swap back the array:
$arrKeys = array_flip($arrKeysFlipped);
print_r($arrKeys);
Array (
[0]=>veg
[1]=>fruit
[2]=>tuber
)
Now, you can build an array by going through the oringal array in the 'order' of the rearranged keys.
$arrNew = array ();
foreach($arrKeys as $index=>$key) {
$arrNew[$key] = $arrOrig[$key];
}
print_r($arrNew);
Array (
[veg]=>cucumber
[fruit]=>pear
[tuber]=>potato
)
I haven't tested this - but this is what I'd expect. Does this at least provide any kind of help? Good luck :)
You could put this into a function $arrNew = array_swap_assoc($key1,$key2,$arrOld);
<?php
if(!function_exists('array_swap_assoc')) {
function array_swap_assoc($key1='',$key2='',$arrOld=array()) {
$arrNew = array ();
if(is_array($arrOld) && count($arrOld) > 0) {
$arrKeys = array_keys($arrOld);
$arrFlip = array_flip($arrKeys);
$indexA = $arrFlip[$key1];
$indexB = $arrFlip[$key2];
$arrFlip[$key1]=$indexB;
$arrFlip[$key2]=$indexA;
$arrKeys = array_flip($arrFlip);
foreach($arrKeys as $index=>$key) {
$arrNew[$key] = $arrOld[$key];
}
} else {
$arrNew = $arrOld;
}
return $arrNew;
}
}
?>
WARNING: Please test and debug this before just using it - no testing has been done at all.
There is no easy way, just a loop or a new array definition.
Classical associative array doesn't define or guarantee sequence of elements in any way. There is plain array/vector for that. If you use associative array you are assumed to need random access but not sequential. For me you are using assoc array for task it is not made for.
yeah I agree with Lex, if you are using an associative array to hold data, why not using your logic handle how they are accessed instead of depending on how they are arranged in the array.
If you really wanted to make sure they were in a correct order, trying creating fruit objects and then put them in a normal array.
There is no easy way to do this. This sounds like a slight design-logic error on your part which has lead you to try to do this when there is a better way to do whatever it is you are wanting to do. Can you tell us why you want to do this?
You say that I know the keys and values of the elements I want to switch which makes me think that what you really want is a sorting function since you can easily access the proper elements anytime you want as they are.
$value = $array[$key];
If that is the case then I would use sort(), ksort() or one of the many other sorting functions to get the array how you want. You can even use usort() to Sort an array by values using a user-defined comparison function.
Other than that you can use array_replace() if you ever need to swap values or keys.
Here are two solutions. The first is longer, but doesn't create a temporary array, so it saves memory. The second probably runs faster, but uses more memory:
function swap1(array &$a, $key1, $key2)
{
if (!array_key_exists($key1, $a) || !array_key_exists($key2, $a) || $key1 == $key2) return false;
$after = array();
while (list($key, $val) = each($a))
{
if ($key1 == $key)
{
break;
}
else if ($key2 == $key)
{
$tmp = $key1;
$key1 = $key2;
$key2 = $tmp;
break;
}
}
$val1 = $a[$key1];
$val2 = $a[$key2];
while (list($key, $val) = each($a))
{
if ($key == $key2)
$after[$key1] = $val1;
else
$after[$key] = $val;
unset($a[$key]);
}
unset($a[$key1]);
$a[$key2] = $val2;
while (list($key, $val) = each($after))
{
$a[$key] = $val;
unset($after[$key]);
}
return true;
}
function swap2(array &$a, $key1, $key2)
{
if (!array_key_exists($key1, $a) || !array_key_exists($key2, $a) || $key1 == $key2) return false;
$swapped = array();
foreach ($a as $key => $val)
{
if ($key == $key1)
$swapped[$key2] = $a[$key2];
else if ($key == $key2)
$swapped[$key1] = $a[$key1];
else
$swapped[$key] = $val;
}
$a = $swapped;
return true;
}
fwiw here is a function to swap two adjacent items to implement moveUp() or moveDown() in an associative array without foreach()
/**
* #param array $array to modify
* #param string $key key to move
* #param int $direction +1 for down | -1 for up
* #return $array
*/
protected function moveInArray($array, $key, $direction = 1)
{
if (empty($array)) {
return $array;
}
$keys = array_keys($array);
$index = array_search($key, $keys);
if ($index === false) {
return $array; // not found
}
if ($direction < 0) {
$index--;
}
if ($index < 0 || $index >= count($array) - 1) {
return $array; // at the edge: cannot move
}
$a = $keys[$index];
$b = $keys[$index + 1];
$result = array_slice($array, 0, $index, true);
$result[$b] = $array[$b];
$result[$a] = $array[$a];
return array_merge($result, array_slice($array, $index + 2, null, true));
}
There is an easy way:
$sourceArray = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
// set new order
$orderArray = array(
'apple' => '', //this values would be replaced
'pear' => '',
'grapefruit' => '',
//it is not necessary to touch all elemets that will remains the same
);
$result = array_replace($orderArray, $sourceArray);
print_r($result);
and you get:
$result = array(
'apple' => 'sweet',
'pear' => 'tasty',
'grapefruit' => 'bitter',
'banana' => 'yellow'
)
function arr_swap_keys(array &$arr, $key1, $key2, $f_swap_vals=false) {
// if f_swap_vals is false, then
// swap only the keys, keeping the original values in their original place
// ( i.e. do not preserve the key value correspondence )
// i.e. if arr is (originally)
// [ 'dog' => 'alpha', 'cat' => 'beta', 'horse' => 'gamma' ]
// then calling this on arr with, e.g. key1 = 'cat', and key2 = 'horse'
// will result in arr becoming:
// [ 'dog' => 'alpha', 'horse' => 'beta', 'cat' => 'gamma' ]
//
// if f_swap_vals is true, then preserve the key value correspondence
// i.e. in the above example, arr will become:
// [ 'dog' => 'alpha', 'horse' => 'gamma', 'cat' => 'beta' ]
//
//
$arr_vals = array_values($arr); // is a (numerical) index to value mapping
$arr_keys = array_keys($arr); // is a (numerical) index to key mapping
$arr_key2idx = array_flip($arr_keys);
$idx1 = $arr_key2idx[$key1];
$idx2 = $arr_key2idx[$key2];
swap($arr_keys[$idx1], $arr_keys[$idx2]);
if ( $f_swap_vals ) {
swap($arr_vals[$idx1], $arr_vals[$idx2]);
}
$arr = array_combine($arr_keys, $arr_vals);
}
function swap(&$a, &$b) {
$t = $a;
$a = $b;
$b = $t;
}
Well it's just a key sorting problem. We can use uksort for this purpose. It needs a key comparison function and we only need to know that it should return 0 to leave keys position untouched and something other than 0 to move key up or down.
Notice that it will only work if your keys you want to swap are next to each other.
<?php
$arr = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
uksort(
$arr,
function ($k1, $k2) {
if ($k1 == 'grapefruit' && $k2 == 'pear') return 1;
else return 0;
}
);
var_dump($arr);
I'll share my short version too, it works with both numeric and associative arrays.
array array_swap ( array $array , mixed $key1 , mixed $key2 [, bool $preserve_keys = FALSE [, bool $strict = FALSE ]] )
Returns a new array with the two elements swapped. It preserve original keys if specified. Return FALSE if keys are not found.
function array_swap(array $array, $key1, $key2, $preserve_keys = false, $strict = false) {
$keys = array_keys($array);
if(!array_key_exists($key1, $array) || !array_key_exists($key2, $array)) return false;
if(($index1 = array_search($key1, $keys, $strict)) === false) return false;
if(($index2 = array_search($key2, $keys, $strict)) === false) return false;
if(!$preserve_keys) list($keys[$index1], $keys[$index2]) = array($key2, $key1);
list($array[$key1], $array[$key2]) = array($array[$key2], $array[$key1]);
return array_combine($keys, array_values($array));
}
For example:
$arr = array_swap($arr, 'grapefruit', 'pear');
I wrote a function with more general purpose, with this problem in mind.
array with known keys
specify order of keys in a second array ($order array keys indicate key position)
function order_array($array, $order) {
foreach (array_keys($array) as $k => $v) {
$keys[++$k] = $v;
}
for ($i = 1; $i <= count($array); $i++) {
if (isset($order[$i])) {
unset($keys[array_search($order[$i], $keys)]);
}
if ($i === count($array)) {
array_push($keys, $order[$i]);
} else {
array_splice($keys, $i-1, 0, $order[$i]);
}
}
}
foreach ($keys as $key) {
$result[$key] = $array[$key];
}
return $result;
} else {
return false;
}
}
$order = array(1 => 'item3', 2 => 'item5');
$array = array("item1" => 'val1', "item2" => 'val2', "item3" => 'val3', "item4" => 'val4', "item5" => 'val5');
print_r($array); -> Array ( [item1] => val1 [item2] => val2 [item3] => val3 [item4] => val4 [item5] => val5 )
print_r(order_array($array, $order)); -> Array ( [item3] => val3 [item5] => val5 [item1] => val1 [item2] => val2 [item4] => val4 )
I hope this is relevant / helpful for someone
Arrays in php are ordered maps.
$arr = array('apple'=>'sweet','grapefruit'=>'bitter','
pear'=>'tasty','banana'=>'yellow');
doesn't mean that that the first element is 'apple'=>'sweet' and the last - 'banana'=>'yellow' just because you put 'apple' first and 'banana' last. Actually, 'apple'=>'sweet' will be the first and
'banana'=>'yellow' will be the second because of alphabetical ascending sort order.

Transposing multidimensional arrays in PHP

How would you flip 90 degrees (transpose) a multidimensional array in PHP? For example:
// Start with this array
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$bar = flipDiagonally($foo); // Mystery function
var_dump($bar[2]);
// Desired output:
array(3) {
["a"]=>
string(2) "a2"
["b"]=>
string(2) "b2"
["c"]=>
string(2) "c2"
}
How would you implement flipDiagonally()?
Edit: this is not homework. I just want to see if any SOers have a more creative solution than the most obvious route. But since a few people have complained about this problem being too easy, what about a more general solution that works with an nth dimension array?
i.e. How would you write a function so that:
$foo[j][k][...][x][y][z] = $bar[z][k][...][x][y][j]
?(ps. I don't think 12 nested for loops is the best solution in this case.)
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
Or if you're using PHP 5.6 or later:
function transpose($array) {
return array_map(null, ...$array);
}
With 2 loops.
function flipDiagonally($arr) {
$out = array();
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
I think you're referring to the array transpose (columns become rows, rows become columns).
Here is a function that does it for you (source):
function array_transpose($array, $selectKey = false) {
if (!is_array($array)) return false;
$return = array();
foreach($array as $key => $value) {
if (!is_array($value)) return $array;
if ($selectKey) {
if (isset($value[$selectKey])) $return[] = $value[$selectKey];
} else {
foreach ($value as $key2 => $value2) {
$return[$key2][$key] = $value2;
}
}
}
return $return;
}
Transposing an N-dimensional array:
function transpose($array, &$out, $indices = array())
{
if (is_array($array))
{
foreach ($array as $key => $val)
{
//push onto the stack of indices
$temp = $indices;
$temp[] = $key;
transpose($val, $out, $temp);
}
}
else
{
//go through the stack in reverse - make the new array
$ref = &$out;
foreach (array_reverse($indices) as $idx)
$ref = &$ref[$idx];
$ref = $array;
}
}
$foo[1][2][3][3][3] = 'a';
$foo[4][5][6][5][5] = 'b';
$out = array();
transpose($foo, $out);
echo $out[3][3][3][2][1] . ' ' . $out[5][5][6][5][4];
Really hackish, and probably not the best solution, but hey it works.
Basically it traverses the array recursively, accumulating the current indicies in an array.
Once it gets to the referenced value, it takes the "stack" of indices and reverses it, putting it into the $out array. (Is there a way of avoiding use of the $temp array?)
Codler's answer fails for a single-row matrix (e.g. [[1,2]]) and also for the empty matrix ([]), which must be special-cased:
function transpose(array $matrix): array {
if (!$matrix) return [];
return array_map(count($matrix) == 1 ? fn ($x) => [$x] : null, ...$matrix);
}
(note: PHP 7.4+ syntax, easy enough to adapt for older versions)
I got confronted with the same problem. Here is what i came up with:
function array_transpose(array $arr)
{
$keys = array_keys($arr);
$sum = array_values(array_map('count', $arr));
$transposed = array();
for ($i = 0; $i < max($sum); $i ++)
{
$item = array();
foreach ($keys as $key)
{
$item[$key] = array_key_exists($i, $arr[$key]) ? $arr[$key][$i] : NULL;
}
$transposed[] = $item;
}
return $transposed;
}
I needed a transpose function with support for associative array:
$matrix = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
$result = \array_transpose($matrix);
$trans = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
And the way back:
$matrix = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
$result = \array_transpose($matrix);
$trans = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
The array_unshift trick did not work NOR the array_map...
So I've coded a array_map_join_array function to deal with record keys association:
/**
* Similar to array_map() but tries to join values on intern keys.
* #param callable $callback takes 2 args, the intern key and the list of associated values keyed by array (extern) keys.
* #param array $arrays the list of arrays to map keyed by extern keys NB like call_user_func_array()
* #return array
*/
function array_map_join_array(callable $callback, array $arrays)
{
$keys = [];
// try to list all intern keys
array_walk($arrays, function ($array) use (&$keys) {
$keys = array_merge($keys, array_keys($array));
});
$keys = array_unique($keys);
$res = [];
// for each intern key
foreach ($keys as $key) {
$items = [];
// walk through each array
array_walk($arrays, function ($array, $arrKey) use ($key, &$items) {
if (isset($array[$key])) {
// stack/transpose existing value for intern key with the array (extern) key
$items[$arrKey] = $array[$key];
} else {
// or stack a null value with the array (extern) key
$items[$arrKey] = null;
}
});
// call the callback with intern key and all the associated values keyed with array (extern) keys
$res[$key] = call_user_func($callback, $key, $items);
}
return $res;
}
and array_transpose became obvious:
function array_transpose(array $matrix)
{
return \array_map_join_array(function ($key, $items) {
return $items;
}, $matrix);
}
We can do this by using Two foreach. Traveling one array and another array to create new arrayLike This:
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$newFoo = [];
foreach($foo as $a => $k){
foreach($k as $i => $j){
$newFoo[$i][]= $j;
}
}
Check The Output
echo "<pre>";
print_r($newFoo);
echo "</pre>";
Before I start, I'd like to say thanks again to #quazardus for posting his generalised solution for tranposing any two dimenional associative (or non-associative) array!
As I am in the habit of writing my code as tersely as possible I went on to "minimizing" his code a little further. This will very likely not be to everybody's taste. But just in case anyone should be interested, here is my take on his solution:
function arrayMap($cb, array $arrays) // $cb: optional callback function
{ $keys = [];
array_walk($arrays, function ($array) use (&$keys)
{ $keys = array_merge($keys, array_keys($array)); });
$keys = array_unique($keys); $res = [];
foreach ($keys as $key) {
$items = array_map(function ($arr) use ($key)
{return isset($arr[$key]) ? $arr[$key] : null; },$arrays);
$res[$key] = call_user_func(
is_callable($cb) ? $cb
: function($k, $itms){return $itms;},
$key, $items);
}
return $res;
}
Now, analogous to the PHP standard function array_map(), when you call
arrayMap(null,$b);
you will get the desired transposed matrix.
This is another way to do the exact same thing which #codler s answer does. I had to dump some arrays in csv so I used the following function:
function transposeCsvData($data)
{
$ct=0;
foreach($data as $key => $val)
{
//echo count($val);
if($ct< count($val))
$ct=count($val);
}
//echo $ct;
$blank=array_fill(0,$ct,array_fill(0,count($data),null));
//print_r($blank);
$retData = array();
foreach ($data as $row => $columns)
{
foreach ($columns as $row2 => $column2)
{
$retData[$row2][$row] = $column2;
}
}
$final=array();
foreach($retData as $k=>$aval)
{
$final[]=array_replace($blank[$k], $aval);
}
return $final;
}
Test and output reference: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/
Here is array_walk way to achieve this,
function flipDiagonally($foo){
$temp = [];
array_walk($foo, function($item,$key) use(&$temp){
foreach($item as $k => $v){
$temp[$k][$key] = $v;
}
});
return $temp;
}
$bar = flipDiagonally($foo); // Mystery function
Demo.
Here's a variation of Codler/Andreas's solution that works with associative arrays. Somewhat longer but loop-less purely functional:
<?php
function transpose($array) {
$keys = array_keys($array);
return array_map(function($array) use ($keys) {
return array_combine($keys, $array);
}, array_map(null, ...array_values($array)));
}
Example:
<?php
$foo = array(
"fooA" => [ "a1", "a2", "a3"],
"fooB" => [ "b1", "b2", "b3"],
"fooC" => [ "c1", "c2", "c3"]
);
print_r( transpose( $foo ));
// Output like this:
Array (
[0] => Array (
[fooA] => a1
[fooB] => b1
[fooC] => c1
)
[1] => Array (
[fooA] => a2
[fooB] => b2
[fooC] => c2
)
[2] => Array (
[fooA] => a3
[fooB] => b3
[fooC] => c3
)
);

Categories