【问题标题】:How to add an array value to the middle of an associative array?如何将数组值添加到关联数组的中间?
【发布时间】:2011-01-10 02:28:57
【问题描述】:

假设我有这个数组:

$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 列的示例 db 表。我使用 mysql_fetch_assoc() 获取这些数据。在这个新数组中,在“pizza”和“drink”列之后,我想添加一个新列“full_dinner”,它结合了“pizza”和“drink”的值,这样当我在所述数组上运行 foreach() 时, 'full_dinner' 直接出现在 'drink' 之后

【问题讨论】:

  • 我没有对你投反对票。我相信我的 uasort 答案应该对您的目的有所帮助。
  • 有什么比array_splice 更容易插入值?我认为从一开始就明确定义您打算做什么很重要。
  • 我想说,如果您对这个数组的顺序很挑剔,那么您的设计就会有些异味。你能告诉我们为什么你需要它们,更详细地了解数组实际包含的内容吗?也许那时我们可以提出替代设计来完全避免这个问题。
  • @spoulson 打败了我 - @Citizen,如果可以的话,请给我们一个真实的例子,说明您如何设想之后使用数据,以及为什么保留订单很重要。如果我们只了解了一半,那就很难回答了:-)
  • 我也不喜欢有人因为不知道答案而对问题投反对票。关于我发现php.net/manual/en/class.arrayiterator.php 很有用的问题,但似乎没有办法使用迭代器来完成这个特定任务。

标签: php arrays associative-array


【解决方案1】:

我错过了什么吗?

$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:哦,确实!我现在才注意到,对不起。要我删除吗?
  • @AlixAxel,感谢您的解决方案。但是如果元素不存在,即 $offset 为 NULL 怎么办?
  • @Tomas:这也很简单(检查我的编辑),您唯一需要决定的是您是要添加 ($offset = 0) 还是添加 ($offset = count($array)) 值。
  • @Tomas:类型转换是为了确保您仍然可以在$data 中使用标量值,例如3 而不是array('c' => 3)
【解决方案2】:

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

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

【讨论】:

  • 这个可以,但我很难相信没有更简单的方法。
  • 在下面的回答中,我添加了一个基于 ArrayObject 的选项,它使接口变得更简单,但它可能执行得更慢。您可以随时修改它以使用这种迭代方法而不是排序方法。
  • 很好,在我放置赏金并获得切片解决方案之前,我从您的解决方案创建了我的库函数。我想我无论如何都不会重写我的库函数 :)
  • 但是你的函数有一个小问题——你应该使用===而不是==。试试这个:InsertBeforeKey(array(0=>"a", ""=>"b"), "", "c", "new") - 它应该插入 c=>new 作为第二个元素,但 == 它作为第一个插入。我纠正了这个问题。
【解决方案3】:

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

$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,即搜索的元素不存在怎么办?
  • 从最初的问题中我推断出指示插入位置的索引是确切已知的。我认为这里提出的几乎所有解决方案都是建立在这个假设之上的......
  • 为了安全起见:if ($splitIndex===false) { $a['c'] = 3}?如果没有找到key,那么前面就没有什么可以插入的了,所以直接push到数组的末尾就行了……
【解决方案4】:
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')

至于编辑 5:

编辑您的 sql,使其读取

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

并且您自动拥有该列:

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

【讨论】:

    【解决方案5】:

    关联数组没有顺序,因此您可以简单地添加$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 时它才会起作用。
    • 来自问题:“在脚本的后面,我想在 'd' 之前添加值 'c'=>3。我该怎么做?”
    • @ILMV 至少 spoulson 的解决方案与我需要的类似。我真的很想保留当前的关联,所以这不会起作用,但它比任何 php 新手都知道的 ILMV 的“答案”要近得多。
    • 第一段不正确:PHP array IS ordered map
    【解决方案6】:

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

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

    【讨论】:

    • 这不是一个简单的解决方案。我不妨在我的位置之后取消设置所有变量,添加变量,然后读取未设置的变量。
    【解决方案7】:

    在进行按键冒泡排序时,您可以定义自己的排序图。它可能效率不高,但确实有效。

    <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 正在寻找相对于特定键的插入,而不是对所有可能的键进行隐式排序。
    • 我认为有争议的是,在某些情况下,它们是完全相同的东西。实际上,他的问题中没有足够的信息来确定他 100% 暗示的是哪一个。
    • 虽然不是一个完美的解决方案,但我认为它提供了丰富的信息,至少不值得投反对票 :) 感谢彼得提供的额外信息。
    • 非常混乱,一点也不简单。一组类满足一个简单的需求。
    【解决方案8】:

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

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

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

    【讨论】:

      【解决方案9】:
      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);
      

      【讨论】:

        【解决方案10】:

        数组函数的使用非常好,但作为一种更简单的方法如何:

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

        SQL:

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

        数组:

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

        【讨论】:

          【解决方案11】:

          一个简化的 Alix Axel 函数,如果您只需要在第 n 个位置插入数据:

          function array_middle_push( array $array, int $position, array $data ): array {
             return array_merge( array_slice( $array, 0, $position ), $data, array_slice( $array, $position ) );
          }
          

          【讨论】:

            【解决方案12】:

            试试这个

            $array['c']=3;
            

            默认情况下关联数组不排序,但如果您想按字母顺序对它们进行排序,您可以使用ksort() 按其键对数组进行排序。

            如果您查看PHP article 中的ksort(),您会发现按其键对数组进行排序很容易,例如:

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

            【讨论】:

            • 就像我说的,使用ksort(),这将通过ke重新排序您的数组,因此将a, b, d, c代替a, b, c, d
            • 是的,每个数组项都有一个键,因此ksort() 会对它进行排序。
            • 占位符是什么意思?形成您提供此答案的信息应该有效。
            • 我从 mysql_fetch_assoc() 获取信息。键未按字母顺序排列。
            • 好的,很高兴你能在你的问题中解释这一点。当问题不能说明整个故事时,任何人应该如何给出准确的答案。
            【解决方案13】:

            你可以通过做添加它

            $array['c']=3;
            

            如果你绝对希望它为打印目的排序,你可以使用 php 的 ksort($array) 函数

            如果键不能通过 ksort 排序,那么您将不得不使用 php 的 uasort 函数创建自己的排序。在此处查看示例

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

            【讨论】:

            • 是否可以对单个记录使用 uasort?我在这个数组中有数百条记录。
            • 在 uasort 中,您正在做的是为您的键提供一个比较器。你的数组可以是你想要的大小。
            猜你喜欢
            • 2011-01-10
            • 1970-01-01
            • 1970-01-01
            • 2015-10-30
            • 2012-01-23
            • 2021-11-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多