32

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?

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()

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

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

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'

E_net4
  • 27,810
  • 13
  • 101
  • 139
Citizen
  • 12,430
  • 26
  • 76
  • 117
  • i didnt downvote you. i believe my uasort answer should be helpful for your purposes. – badideas Jan 27 '10 at 18:57
  • 2
    What could be easier than `array_splice` to insert a value? I think it's important to clearly define what you're intending on doing from the start. – spoulson Jan 27 '10 at 18:57
  • @spouulson array_splice does not work for associative arrays. – Citizen Jan 27 '10 at 19:00
  • 4
    I would say that if you're being picky about the order of this array, something smells with your design. Could you tell us why you need them in order, more detail about what the array actually contains? Maybe then we can suggest alternative designs to avoid the question altogether. – Tesserex Jan 27 '10 at 19:02
  • 1
    @spoulson beat me to it - @Citizen, if you can, give us a real-world example of how you envisage using the data afterwards, and why it's important to preserve the order. It's tricky to answer if we're only getting half the story :-) – richsage Jan 27 '10 at 19:02
  • 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' – Citizen Jan 27 '10 at 19:06
  • Ok, so that 5th edit and example doesn't actually show why you need the order. You say you want 'full_dinner' after 'drink'. But why? What will break if it's not? – Tesserex Jan 27 '10 at 19:32
  • when you say "I want to add a new column... that combines the values of 'pizza' and 'drink'".... if by combine you mean some mathematical operation, how about doing that mathemetical operation in the SQL query? Just a thought... – Ben Jan 27 '10 at 19:38
  • 1
    I as well don't like that there are people that downvote questions just because they don't know the answer. Regarding the question I found http://php.net/manual/en/class.arrayiterator.php to be useful, but seems that there is no way to accomplish this particular task using iterators. – TomTom Jan 09 '13 at 09:25

13 Answers13

47

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)));
Alix Axel
  • 151,645
  • 95
  • 393
  • 500
  • Yes. That I answered exactly the same one day before you. :) – nem75 Mar 28 '12 at 08:28
  • @nem75: Oh, indeed! I just noticed it now, sorry. Do you want me to delete it? – Alix Axel Mar 28 '12 at 12:56
  • @AlixAxel, thanks for your solution. But what if the element is not present, i.e. $offset is NULL? – Tomas Mar 28 '12 at 19:39
  • @Tomas: That's pretty easy too (check my edit), the only thing you have to decide is if you want to prepend (`$offset = 0`) or append (`$offset = count($array)`) the value. – Alix Axel Mar 28 '12 at 23:18
  • @AlixAxel: why would I want you to delete it? The more the merrier. – nem75 Mar 29 '12 at 15:38
  • 2
    @Tomas: The typecast is to make sure you can still use scalar values in `$data` like `3` instead of `array('c' => 3)`. – Alix Axel Mar 29 '12 at 16:19
14

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 );
Tomas
  • 57,621
  • 49
  • 238
  • 373
Amish Programmer
  • 2,051
  • 3
  • 19
  • 22
  • 3
    This does it, but I find it hard to believe that a simpler method does not exist. – Citizen Jan 27 '10 at 19:09
  • In my answer below, I added an option based on ArrayObject that makes the interface to this simpler, but it probably executes slower. You could always modify it to use this iteration approach instead of the sort approach. – Peter Bailey Jan 27 '10 at 19:57
  • nice, I created my librarly functions from your solution before I placed the bounty and got the slice solution. I guess I won't rewrite my library func anyway :) – Tomas Mar 26 '12 at 12:50
  • But there's a small problem with your function - you should use `===` instead of `==`. Try this: `InsertBeforeKey(array(0=>"a", ""=>"b"), "", "c", "new")` - it should insert the c=>new as second element but with `==` it inserts as first. I corrected the problem. – Tomas Mar 26 '12 at 13:00
11

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.

nem75
  • 358
  • 3
  • 9
  • Looks good. But what if the splitIndex results as FALSE, i.e. the searched element is not present? – Tomas Mar 26 '12 at 13:12
  • From the original question I deduced that the index inidicating where to insert is known exactly. I think pretty much every solution proposed here is built on this assumption ... – nem75 Mar 28 '12 at 08:31
  • 2
    to be on the safe side: `if ($splitIndex===false) { $a['c'] = 3}`? if the key is not found, there is nothing to insert before, so just push to the end of the array... – Elias Van Ootegem Mar 28 '12 at 10:52
5
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,
  ...
)
Tomas
  • 57,621
  • 49
  • 238
  • 373
Dan Soap
  • 10,114
  • 1
  • 40
  • 49
0

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.

spoulson
  • 21,335
  • 15
  • 77
  • 102
  • This answer also isn't very dynamic, it will only work if you want to insert the new array value into position 3. – Ben Everard Jan 27 '10 at 18:49
  • From the question: "Later in the script, I want to add the value 'c'=>3 before 'd'. How can I do this?" – spoulson Jan 27 '10 at 18:56
  • @ILMV At least spoulson's solution works similarly to what I need. I really want to keep the current associations so this wont work but its a lot closer than ILMV's "answer" that any php newbie would have already known. – Citizen Jan 27 '10 at 18:59
  • 1
    1st paragraph is not true: [PHP array __IS__ **ordered map**](http://ca.php.net/manual/en/language.types.array.php) – Tomas Mar 22 '12 at 13:03
0

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];
}
spoulson
  • 21,335
  • 15
  • 77
  • 102
  • This is not exactly a simple solution. I might as well unset all the variables after my location, add the variable, and then readd the unset'd variables. – Citizen Jan 27 '10 at 18:51
0

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];
    }
}
Peter Bailey
  • 105,256
  • 31
  • 182
  • 206
  • While your code may be correct, I don't believe it applies to question at hand. @Citizen is looking to insert relative to a specific key, not have an implicit ordering over all possible keys. – Amish Programmer Jan 27 '10 at 20:06
  • I think it's arguable that, in some circumstances, they are the exact same thing. There's actually not enough info in his question to determine which he's implying 100%. – Peter Bailey Jan 27 '10 at 20:13
  • Although not a perfect solution, I thought it was informative and at the very least deserves not to have a negative vote :) Thanks peter for the extra info. – Citizen Jan 27 '10 at 21:35
  • very messy and not at all simple. Bunch of classes for one simple requirement. – Tomas Mar 26 '12 at 12:47
0

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'];
Jonah Graham
  • 7,890
  • 23
  • 55
yak
  • 340
  • 3
  • 7
0

A simplified Alix Axel function if you need to just insert data in nth position:

function array_middle_push( array $array, int $position, array $data ): array {
   return array_merge( array_slice( $array, 0, $position ), $data, array_slice( $array, $position ) );
}
Sergiy Zaharchenko
  • 1,490
  • 1
  • 12
  • 11
0

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, )
Patrick
  • 15,702
  • 1
  • 39
  • 39
0
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);
Juan Mellado
  • 14,973
  • 5
  • 47
  • 54
Ravi Jethva
  • 1,931
  • 2
  • 11
  • 12
-1

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
Ben Everard
  • 13,652
  • 14
  • 67
  • 96
  • Like I said, use `ksort()`, that will reorder your array by ke, so instead of `a, b, d, c` it will be `a, b, c, d` – Ben Everard Jan 27 '10 at 18:43
  • Yes it will, each array item has a key, and therefore `ksort()` will sort it. – Ben Everard Jan 27 '10 at 18:47
  • What do you mean by placeholders? Form the information you've given this answer should work. – Ben Everard Jan 27 '10 at 18:47
  • I'm getting the info from a mysql_fetch_assoc(). The keys are not in alphabetical order. – Citizen Jan 27 '10 at 18:49
  • 1
    Ok well it was nice of you to explain that in your question. How is anyone supposed to give an accurate answer when the question isn't telling the entire story. – Ben Everard Jan 27 '10 at 18:51
  • Ok then so if you've got 50 records, where is your new records going to be inserted? What are you rules, will it always go right before the very end? – Ben Everard Jan 27 '10 at 18:53
  • What I have is I know the exact key that I want to place a record after in an associative array. – Citizen Jan 27 '10 at 18:55
  • Again, it was nice of you to specifically say then when you asked the question, of course the example a, b, d, c was going to throw people off. And have you downvoted some of my questions? I don't know why you're calling a 'newbie' when you've had to edit your own question 5 times to get the correct answer! – Ben Everard Jan 27 '10 at 19:27
  • [PHP associative array __IS__ **ordered map**](http://ca.php.net/manual/en/language.types.array.php) – Tomas Mar 22 '12 at 13:05
-1

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

badideas
  • 3,189
  • 3
  • 25
  • 26
  • Is it possible to use uasort on a single record? I have hundreds of records in this array. – Citizen Jan 27 '10 at 19:02
  • in uasort, what you're doing is feeding it a a comparator for your keys. your array can be as big or small as you want it to be. – badideas Jan 27 '10 at 19:24