How to add an array value to the middle of an associative array?

Lets say I have this array:

$array = array('a'=>1,'z'=>2,'d'=>4);

Later in the script, I want to add the value 'c'=>3 before 'z'. How can I do this?

EDIT: Yes, the order is important. When I run a foreach() through the array, I do NOT want this newly added value added to the end of the array. I am getting this array from a mysql_fetch_assoc()

EDIT 2: The keys I used above are placeholders. Using ksort() will not achieve what I want.

EDIT 3: http://www.php.net/manual/en/function.array-splice.php#88896 accomplishes what I'm looking for but I'm looking for something simpler.

EDIT 4: Thanks for the downvotes. I gave feedback to your answers and you couldn't help, so you downvoted and requested to close the question because you didn't know the answer. Thanks.

EDIT 5: Take a sample db table with about 30 columns. I get this data using mysql_fetch_assoc(). In this new array, after column 'pizza' and 'drink', I want to add a new column 'full_dinner' that combines the values of 'pizza' and 'drink' so that when I run a foreach() on the said array, 'full_dinner' comes directly after 'drink'

Answers


Am I missing something?

$key = 'z';
$offset = array_search($key, array_keys($array));

$result = array_merge
        (
            array_slice($array, 0, $offset),
            array('c' => 3),
            array_slice($array, $offset, null)
        );

Handling of nonexistent keys (appending $data by default):

function insertBeforeKey($array, $key, $data = null)
{
    if (($offset = array_search($key, array_keys($array))) === false) // if the key doesn't exist
    {
        $offset = 0; // should we prepend $array with $data?
        $offset = count($array); // or should we append $array with $data? lets pick this one...
    }

    return array_merge(array_slice($array, 0, $offset), (array) $data, array_slice($array, $offset));
}

Demo:

$array = array('a' => 1, 'z' => 2, 'd' => 4);

// array(4) { ["a"]=> int(1) ["c"]=> int(3) ["z"]=> int(2) ["d"]=> int(4) }
var_dump(insertBeforeKey($array, 'z', array('c' => 3)));

// array(4) { ["a"]=> int(1) ["z"]=> int(2) ["d"]=> int(4) ["c"]=> int(3) }
var_dump(insertBeforeKey($array, 'y', array('c' => 3)));

A simple approach to this is to iterate through the original array, constructing a new one as you go:

function InsertBeforeKey( $originalArray, $originalKey, $insertKey, $insertValue ) {

    $newArray = array();
    $inserted = false;

    foreach( $originalArray as $key => $value ) {

        if( !$inserted && $key === $originalKey ) {
            $newArray[ $insertKey ] = $insertValue;
            $inserted = true;
        }

        $newArray[ $key ] = $value;

    }

    return $newArray;

}

Then simply call

$array = InsertBeforeKey( $array, 'd', 'c', 3 );

According to your original question the best answer I can find is this:

$a = array('a'=>1,'z'=>2,'d'=>4);

$splitIndex = array_search('z', array_keys($a));
$b = array_merge(
        array_slice($a, 0, $splitIndex), 
        array('c' => 3), 
        array_slice($a, $splitIndex)
);

var_dump($b);
array(4) {
  ["a"]=>
  int(1)
  ["c"]=>
  int(3)
  ["z"]=>
  int(2)
  ["d"]=>
  int(4)
}

Depending on how big your arrays are you will duplicate quite some data in internal memory, regardless if you use this solution or another.

Furthermore your fifth edit seems to indicate that alternatively your SQL query could be improved. What you seem to want to do there would be something like this:

SELECT a, b, CONCAT(a, ' ', b) AS ab FROM ... WHERE ...

If changing your SELECT statement could make the PHP solution redundant, you should definitely go with the modified SQL.


function insertValue($oldArray, $newKey, $newValue, $followingKey) {

    $newArray = array ();
    foreach (array_keys($oldArray) as $k) {
        if ($k == $followingKey)
            $newArray[$newKey] = $newValue;
        $newArray[$k] = $oldArray [$k];
    }

    return $newArray;
}

You call it as

insertValue($array, 'c', '3', 'z')

As for Edit 5:

edit your sql, so that it reads

SELECT ..., pizza, drink, pizza+drink as full_meal, ... FROM ....

and you have the column automatically:

Array (
  ...
  'pizza' => 12,
  'drink' => 5,
  'full_meal' => 17,
  ...
)

Associative arrays are not ordered, so you can simply add with $array['c'] = 3.

If order is important, one option is switch to a data structure more like:

$array = array(
   array('a' => 1),
   array('b' => 2)
   array('d' => 4)
);

Then, use array_splice($array, 2, 0, array('c' => 3)) to insert at position 2. See manual on array_splice.


An alternative approach is to supplement the associative array structure with an ordered index that determines the iterative order of keys. For instance:

$index = array('a','b','d');

// Add new value and update index
$array['c'] = 3;
array_splice($index, 2, 0, 'c');

// Iterate the array in order
foreach $index as $key {
   $value = $array[$key];
}

You can define your own sortmap when doing a bubble-sort by key. It's probably not terribly efficient but it works.

<pre>
<?php

$array = array('a'=>1,'z'=>2,'d'=>4);

$array['c'] = 3;

print_r( $array );

uksort( $array, 'sorter' );

print_r( $array );

function sorter( $a, $b )
{
    static $ordinality = array(
        'a' => 1
      , 'c' => 2
      , 'z' => 3
      , 'd' => 4
    );
    return $ordinality[$a] - $ordinality[$b];
}

?>
</pre>

Here's an approach based on ArrayObject using this same concept

$array = new CitizenArray( array('a'=>1,'z'=>2,'d'=>4) );
$array['c'] = 3;

foreach ( $array as $key => $value )
{
    echo "$key: $value <br>";
}

class CitizenArray extends ArrayObject
{
    static protected $ordinality = array(
        'a' => 1
      , 'c' => 2
      , 'z' => 3
      , 'd' => 4
    );

    function offsetSet( $key, $value )
    {
        parent::offsetSet( $key, $value );
        $this->uksort( array( $this, 'sorter' ) );
    }

    function sorter( $a, $b )
    {
        return self::$ordinality[$a] - self::$ordinality[$b];
    }
}

For the moment the best i can found to try to minimize the creation of new arrays are these two functions :

the first one try to replace value into the original array and the second one return a new array.

// replace value into the original array
function insert_key_before_inplace(&$base, $beforeKey, $newKey, $value) {
 $index = 0;
 foreach($base as $key => $val) {
    if ($key==$beforeKey) break;
    $index++;
 }
 $end = array_splice($base, $index, count($base)-$index);
 $base[$newKey] = $value;
 foreach($end as $key => $val) $base[$key] = $val;
}


$array = array('a'=>1,'z'=>2,'d'=>4);

insert_key_before_inplace($array, 'z', 'c', 3);

var_export($array); // array ( 'a' => 1, 'c' => 3, 'z' => 2, 'd' => 4, )

// create new array
function insert_key_before($base, $beforeKey, $newKey, $value) {
 $index = 0;
 foreach($base as $key => $val) {
    if ($key==$beforeKey) break;
    $index++;
 }
 $end = array_splice($base, $index, count($base)-$index);
 $base[$newKey] = $value;
 return $base+$end;
}


$array = array('a'=>1,'z'=>2,'d'=>4);

$newArray=insert_key_before($array, 'z', 'c', 3);

var_export($array); // ( 'a' => 1, 'z' => 2, 'd' => 4, )

var_export($newArray); // array ( 'a' => 1, 'c' => 3, 'z' => 2, 'd' => 4, )

function putarrayelement(&$array, $arrayobject, $elementposition, $value = null) {

        $count = 0;
        $return = array();
        foreach ($array as $k => $v) {
        if ($count == $elementposition) {
                if (!$value) {
                    $value = $count;
                }
            $return[$value] = $arrayobject;
            $inserted = true;
        }
        $return[$k] = $v;
        $count++;
        }
        if (!$value) {
           $value = $count;
        }
        if (!$inserted){
            $return[$value];
        }
        $array = $return;
       return $array;
     }

        $array = array('a' => 1, 'z' => 2, 'd' => 4);
        putarrayelement($array, '3', 1, 'c');
        print_r($array);

Great usage of array functions but how about this as a simpler way:

Add a static column to the SQL and then replace it in the resultant array. Order stays the same:

SQL :

Select pizza , drink , 'pizza-drink' as 'pizza-drink' , 28 columns..... From Table

Array :

$result['pizza-drink'] = $result['pizza'] . $result['drink'];

Try this

$array['c']=3;

An associative array is not ordered by default, but if you wanted to sort them alphabetically you could use ksort() to sort the array by it's key.

If you check out the PHP article for ksort() you will se it's easy to sort an array by its key, for example:

<?php
$fruits = array("d"=>"lemon", "a"=>"orange", "b"=>"banana", "c"=>"apple");
ksort($fruits);
foreach ($fruits as $key => $val) {
    echo "$key = $val\n";
}
?>

// The above example will output:
a = orange
b = banana
c = apple
d = lemon

you can add it by doing

$array['c']=3;

and if you absolutely want it sorted for printing purposes, you can use php's ksort($array) function

if the keys are not sortable by ksort, then you will have to create your own sort by using php's uasort function. see examples here

http://php.net/manual/en/function.uasort.php


Need Your Help

Howto call back async function from rx subscribe?

c# asynchronous system.reactive reactive-programming

I would like to call back an async function within an Rx subscription.

Concurrency in a GIT repo on a network shared folder

windows linux git concurrency network-share

I want to have a bare git repository stored on a (windows) network share. I use linux, and have the said network share mounted with CIFS. My coleague uses windows xp, and has the network share