如何将数组值添加到关联数组的中间?

32
假设我有这个数组:
``` $array = array('a' => 1, 'z' => 2, 'd' => 4); ```
在脚本后面,我想在`'z'`之前添加值`'c' => 3`。 我该怎么做?
是的,顺序很重要。 当我通过数组运行foreach()时,我不希望将此新添加的值添加到数组的末尾。 我从`mysql_fetch_assoc()`获取此数组。
上面使用的键是占位符。 使用`ksort()`无法实现我想要的效果。 http://www.php.net/manual/en/function.array-splice.php#88896可以实现我要求的效果,但我正在寻找更简单的方法。
以大约30列的示例数据库表为例。 我使用`mysql_fetch_assoc()`获取此数据。 在这个新数组中,在“pizza”和“drink”列之后,我想添加一个名为“full_dinner”的新列,它组合了“pizza”和“drink”的值,以便当我对所述数组运行foreach()时,“full_dinner”直接出现在“drink”之后。

2
有什么比使用array_splice更容易的方式来插入一个值呢?我认为从一开始就清楚地定义你打算做什么非常重要。 - spoulson
4
如果你在意这个数组的顺序,那么你的设计可能存在问题。你能告诉我们为什么需要按顺序排列,以及这个数组实际包含了什么吗?也许我们可以建议一些替代设计来避免这个问题。 - Tesserex
1
@spoulson 先我一步了 - @Citizen,如果可以的话,请给我们一个现实世界的例子,说明您预计如何在之后使用这些数据,以及为什么保留顺序很重要。如果我们只得到了故事的一半,回答起来就很棘手 :-) - richsage
当你说“我想添加一个新列...将'pizza'和'drink'的值组合起来”时...如果你所说的组合是指某种数学运算,那么在SQL查询中进行这种数学运算怎么样?只是一个想法... - Ben
1
我也不喜欢有些人因为不知道答案就会对问题进行投反对票的行为。 关于这个问题,我发现http://php.net/manual/en/class.arrayiterator.php是有用的,但似乎无法使用迭代器完成这个特定的任务。 - TomTom
显示剩余4条评论
13个回答

47

我有所疏漏吗?

$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)
        );

处理不存在的键(默认情况下追加 $data):
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));
}

示例:

$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)));

是的,我在你之前一天就回答了完全相同的问题。 :) - nem75
@nem75:哦,确实!我刚刚才注意到,抱歉。你想让我删除它吗? - Alix Axel
@AlixAxel,感谢您的解决方案。但是如果元素不存在,即$offset为NULL怎么办? - Tomas
@Tomas:这也很简单(请查看我的编辑),你需要决定的唯一事情就是你想要在值之前添加($offset = 0)还是之后添加($offset = count($array))。 - Alix Axel
@AlixAxel:为什么我要让你删除它?越多越好。 - nem75
2
@Tomas:类型转换是为了确保您仍然可以在$data中使用标量值,例如3而不是array('c' => 3) - Alix Axel

14

一个简单的方法是遍历原始数组,并在遍历过程中构建一个新数组:

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;

}

然后只需调用

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

3
可以用这个方法,但我很难相信不存在更简单的方法。 - Citizen
在我的下面的答案中,我添加了一个基于ArrayObject的选项,使得对其进行接口更简单,但它可能执行速度较慢。您可以随时修改它,以使用这种迭代方法而不是排序方法。 - Peter Bailey
很好,我在放置悬赏之前就从您的解决方案中创建了我的库函数,并获得了切片解决方案。我想我不会重写我的库函数了 :) - Tomas
但是你的函数有一个小问题 - 你应该使用 === 而不是 ==。尝试这样做:InsertBeforeKey(array(0=>"a", ""=>"b"), "", "c", "new") - 它应该将 c=>new 插入为第二个元素,但是使用 == 会将其插入为第一个元素。我已经纠正了这个问题。 - Tomas

11

根据您最初的问题,我能找到的最好答案是这个:

$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)
}

根据你的数组大小,无论你使用这种解决方案还是其他解决方案,都会在内部存储器中复制相当多的数据。

此外,你第五次编辑似乎表明可以改进你的SQL查询。你似乎想要做的是这样的:

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

如果更改你的SELECT语句可以使PHP解决方案变得多余,那么你肯定应该使用修改后的SQL。


看起来不错。但是如果splitIndex的结果为FALSE,即搜索的元素不存在怎么办? - Tomas
从原始问题中,我推断出插入位置的索引是确切已知的。我认为这里提出的几乎所有解决方案都是基于这个假设构建的... - nem75
2
为了保险起见:如果未找到键,则无需在其前面插入任何内容,只需将其推送到数组的末尾... if ($splitIndex===false) { $a['c'] = 3} - Elias Van Ootegem

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;
}

你称其为:
insertValue($array, 'c', '3', 'z')

关于“Edit 5”:
编辑您的SQL,使其读取如下:
SELECT ..., pizza, drink, pizza+drink as full_meal, ... FROM ....

同时您可以自动添加列:

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

0

目前我能找到的最好的尝试减少新数组创建的方法是这两个函数:

第一个函数尝试将值替换为原始数组中的值,第二个函数返回一个新数组。

// 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, )

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

0

使用数组函数非常好,但是这里有一个更简单的方法:

在 SQL 中添加一个静态列,然后在结果数组中替换它。顺序保持不变:

SQL:

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

数组:

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

0

关联数组是无序的,因此您可以使用$array['c'] = 3简单地添加。

如果顺序很重要,则可以切换到更像以下数据结构的数据结构:

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

然后,使用array_splice($array, 2, 0, array('c' => 3))在位置2插入。请参阅array_splice手册。


这个答案也不是很动态,它只能在你想要将新的数组值插入到位置3时起作用。 - Ben Everard
在脚本的后面,我想在 'd' 之前添加值为 'c'=>3。我该如何实现? - spoulson
@ILMV 至少 spoulson 的解决方案与我所需的类似。我真的想保留当前的关联,所以这不起作用,但比 ILMV 的“答案”更接近,任何 PHP 新手都已经知道了。 - Citizen
1
第一段不准确:PHP数组是有序映射 - Tomas

0

另一种方法是通过有序索引来补充关联数组结构,以确定键的迭代顺序。例如:

$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];
}

这并不是一个简单的解决方案。我可能需要在我的位置之后取消所有变量,添加变量,然后重新添加取消的变量。 - Citizen

0

当使用关键字进行冒泡排序时,您可以定义自己的排序映射。这可能不是非常高效,但它能够正常工作。

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

这是一种基于ArrayObject的方法,使用了相同的概念。
$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];
    }
}

虽然你的代码可能是正确的,但我不认为它适用于手头的问题。@Citizen想要相对于特定键插入,而不是在所有可能的键上具有隐式排序。 - Amish Programmer
我认为在某些情况下,它们是完全相同的可以争论。实际上,在他的问题中没有足够的信息来确定他100%的意思。 - Peter Bailey
虽然不是完美的解决方案,但我认为它很有启发性,至少不应该被负面评价 :) 感谢彼得提供额外的信息。 - Citizen
非常混乱,一点也不简单。为了一个简单的需求而有一堆类。 - Tomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接